Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make eip712 independent #43

Merged
merged 4 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ jobs:
- run: yarn build
- run: export PRIVATE_KEY=${{secrets.PRIVATE_KEY}}
- run: yarn test:unit
- run: yarn test:integration
- run: yarn test test/integration/rpc test/integration/mainnet test/integration/zksync test/integration/utils
99 changes: 0 additions & 99 deletions src/Eip712.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,102 +355,3 @@ export class EIP712Signer {
return this.eip712Domain;
}
}
// export class EIP712Transaction extends BaseTransaction<EIP712Transaction> {
// private txData: Eip712TxData;
// private signature?: SignatureObject;
// constructor(txData: Eip712TxData) {
// super(txData, {} as web3Accounts.TxOptions);
// const { v, r, s, ...data } = txData;
//
// if (r && s) {
// this.signature = new SignatureObject(toUint8Array(r), toUint8Array(s), toBigInt(v));
// }
//
// this.txData = data;
// }
// public getSignature(): SignatureObject | undefined {
// return this.signature;
// }
// public getMessageToSign(isHash = false): Uint8Array {
// const typedDataStruct = EIP712.txTypedData(this.txData);
// const message = web3Abi.getEncodedEip712Data(typedDataStruct, isHash);
// return web3Utils.hexToBytes(message);
// }
// _processSignature(
// v: Numbers,
// r: EthereumSignature['r'],
// s: EthereumSignature['s'],
// ): EIP712Transaction {
// const signature = new SignatureObject(toUint8Array(r), toUint8Array(s), toBigInt(v));
// return new EIP712Transaction({
// ...this.txData,
// v: toBigInt(signature.v),
// r: toHex(signature.r),
// s: toHex(signature.s),
// });
// }
// public ecsign(msgHash: Uint8Array, privateKey: Uint8Array, chainId?: bigint) {
// const { s, r, v } = this._ecsign(msgHash, privateKey, chainId);
// this.signature = new SignatureObject(toUint8Array(r), toUint8Array(s), toBigInt(v));
// return this.signature;
// }
//
// protected _errorMsg(msg: string): string {
// return `${msg} (${this.errorStr()})`;
// }
//
// public static fromTxData(txData: Eip712TxData, _ = {}) {
// return new EIP712Transaction(txData);
// }
//
// errorStr(): string {
// return '';
// }
//
// getMessageToVerifySignature(): Uint8Array {
// return this.getMessageToSign();
// }
//
// getSenderPublicKey(): Uint8Array {
// // @TODO: implement recover transaction here
// return new Uint8Array();
// }
//
// getUpfrontCost(): bigint {
// return 0n;
// }
//
// hash(): Uint8Array {
// return toUint8Array(EIP712.txHash(this.txData));
// }
// raw(): web3Accounts.TxValuesArray {
// return EIP712.raw(this.txData) as unknown as web3Accounts.TxValuesArray;
// }
//
// serialize(): Uint8Array {
// return toUint8Array(EIP712.serialize(this.txData));
// }
//
// toJSON(): web3Accounts.JsonTx {
// const data = EIP712.getSignInput(this.txData);
// return {
// to: data.to && toHex(data.to),
// gasLimit: toHex(data.gasLimit),
// // @ts-ignore-next-line
// gasPerPubdataByteLimit: data.gasPerPubdataByteLimit,
// customData: data.customData,
// maxFeePerGas: toHex(data.maxFeePerGas),
// maxPriorityFeePerGas: toHex(data.maxPriorityFeePerGas),
// paymaster: data.paymaster,
// nonce: toHex(data.nonce),
// value: toHex(data.value),
// data: toHex(data.data),
// factoryDeps: data.factoryDeps,
// paymasterInput: data.paymasterInput,
// type: toHex(data.txType),
// v: this.signature?.v ? toHex(this.signature.v) : undefined,
// r: this.signature?.r ? toHex(this.signature?.r) : undefined,
// s: this.signature?.s ? toHex(this.signature?.s) : undefined,
// };
// }
// }
15 changes: 5 additions & 10 deletions src/smart-account-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { privateKeyToAccount, signMessageWithPrivateKey, Web3Account } from 'web
// import { DEFAULT_GAS_PER_PUBDATA_LIMIT, EIP712_TX_TYPE } from './constants';
import { Web3ZKsyncL2 } from './web3zksync-l2';
import type * as web3Types from 'web3-types';
import * as utils from './utils';
import { DEFAULT_GAS_PER_PUBDATA_LIMIT, EIP712_TX_TYPE } from './constants';
import { Transaction } from 'web3-types';
import { Address } from 'web3';
Expand Down Expand Up @@ -157,15 +156,11 @@ export const populateTransactionECDSA: TransactionBuilder = async (
typeof secret === 'object' && secret.privateKey
? (secret as Web3Account)
: privateKeyToAccount(secret as string);
provider._eip712Signer = async () => {
if (!provider.eip712) {
provider.eip712 = new utils.EIP712Signer(
account,
Number(await provider.eth.getChainId()),
);
}
return provider.eip712;
};

if (!provider.eth.accounts.wallet.get(account.address)) {
provider.eth.accounts.wallet.add(account);
}

tx.type = EIP712_TX_TYPE;
tx.chainId = format({ format: 'uint' }, tx.chainId ?? (await provider.eth.getChainId()));
tx.value = format({ format: 'uint' }, tx.value ? tx.value : 0n);
Expand Down
8 changes: 8 additions & 0 deletions src/smart-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ export class SmartAccount extends AdapterL2 {
this._address = signer.address;
this.payloadSigner = signer.payloadSigner || signPayloadWithECDSA;
this.transactionBuilder = signer.transactionBuilder || populateTransactionECDSA;
if (this._provider) {
const accounts = Array.isArray(this._account) ? this._account : [this._account];
for (const account of accounts) {
if (!this._provider.eth.accounts.wallet.get(account.address)) {
this._provider.eth.accounts.wallet.add(account);
}
}
}
}
_contextL2(): Web3ZKsyncL2 {
return this._provider!;
Expand Down
2 changes: 0 additions & 2 deletions src/web3zksync-l2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@ import {
} from './constants';
import { IL2BridgeABI } from './contracts/IL2Bridge';
import { IERC20ABI } from './contracts/IERC20';
import * as utils from './utils';

// Equivalent to both Provider and Signer in zksync-ethers
export class Web3ZKsyncL2 extends Web3ZkSync {
eip712!: utils.EIP712Signer;
async getZKTransactionReceipt<ReturnFormat extends DataFormat>(
transactionHash: Bytes,
returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat,
Expand Down
15 changes: 8 additions & 7 deletions src/web3zksync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ import {
REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT,
ZERO_ADDRESS,
} from './constants';
import { EIP712, type EIP712Signer, isAddressEq, isETH } from './utils';
import { EIP712, isAddressEq, isETH } from './utils';
import { RpcMethods } from './rpc.methods';
import { IL2BridgeABI } from './contracts/IL2Bridge';
import { IERC20ABI } from './contracts/IERC20';
import { EIP712TransactionSchema } from './schemas';
import { toUint8Array } from 'web3-eth-accounts';
import { toUint8Array, Web3Account } from 'web3-eth-accounts';
import * as utils from './utils';

/**
* The base class for interacting with ZKsync Era.
Expand Down Expand Up @@ -96,9 +97,6 @@ export class Web3ZkSync extends Web3.Web3 {
): Promise<bigint> {
return this._rpc.l1ChainId(returnFormat);
}
async _eip712Signer(): Promise<EIP712Signer> {
throw new Error('Must be implemented by the derived class!');
}
/**
* Returns the latest L1 batch number.
*
Expand Down Expand Up @@ -647,8 +645,11 @@ export class Web3ZkSync extends Web3.Web3 {

async signTransaction(tx: Transaction): Promise<string> {
if (tx.type && toHex(tx.type) === toHex(EIP712_TX_TYPE)) {
const signer = await this._eip712Signer();
tx.chainId = signer.getDomain().chainId;
const signer = new utils.EIP712Signer(
this.eth.accounts.wallet.get(tx.from!) as Web3Account,
Number(tx.chainId),
);
// tx.chainId = signer.getDomain().chainId;
// @ts-ignore
tx.customData = {
// @ts-ignore
Expand Down
19 changes: 1 addition & 18 deletions src/zksync-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import type * as web3Types from 'web3-types';
import type { Transaction } from 'web3-types';
import type { Web3ZKsyncL2 } from './web3zksync-l2';
import type { Web3ZKsyncL1 } from './web3zksync-l1';
import * as utils from './utils';
import { AdapterL1, AdapterL2 } from './adapters';
import type { Address, Eip712TxData, PaymasterParams, TransactionOverrides } from './types';
import type { EIP712Signer } from './utils';
import { getPriorityOpResponse, isAddressEq } from './utils';

class Adapters extends AdapterL1 {
Expand All @@ -17,8 +15,8 @@ class Adapters extends AdapterL1 {
this.adapterL2 = new AdapterL2();
this.adapterL2.getAddress = this.getAddress.bind(this);
this.adapterL2._contextL2 = this._contextL2.bind(this);
this.adapterL2._eip712Signer = this._eip712Signer;
}

getBalance(token?: Address, blockTag: web3Types.BlockNumberOrTag = 'latest') {
return this.adapterL2.getBalance(token, blockTag);
}
Expand All @@ -38,9 +36,6 @@ class Adapters extends AdapterL1 {
getL2BridgeContracts() {
return this.adapterL2.getL2BridgeContracts();
}
protected async _eip712Signer(): Promise<EIP712Signer> {
throw new Error('Must be implemented by the derived class!');
}

/**
* Initiates the withdrawal process which withdraws ETH or any ERC20 token
Expand Down Expand Up @@ -84,7 +79,6 @@ class Adapters extends AdapterL1 {
export class ZKsyncWallet extends Adapters {
provider?: Web3ZKsyncL2;
providerL1?: Web3ZKsyncL1;
protected eip712!: utils.EIP712Signer;
public account: Web3Account;
/**
*
Expand Down Expand Up @@ -138,7 +132,6 @@ export class ZKsyncWallet extends Adapters {
// errno: 'ECONNREFUSED',
// code: 'ECONNREFUSED'
// }
this.provider._eip712Signer = this._eip712Signer.bind(this);

return this;
}
Expand All @@ -152,16 +145,6 @@ export class ZKsyncWallet extends Adapters {
return this;
}

protected async _eip712Signer(): Promise<utils.EIP712Signer> {
if (!this.eip712) {
this.eip712 = new utils.EIP712Signer(
this.account,
Number(await this.provider!.eth.getChainId()),
);
}

return this.eip712!;
}
protected _contextL1() {
return this.providerL1!;
}
Expand Down
82 changes: 82 additions & 0 deletions test/local/provider.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Web3ZKsyncL2, ZKsyncWallet, SmartAccount } from '../../src';
import { L2Provider } from './fixtures';
import { ADDRESS2, PRIVATE_KEY1, PRIVATE_KEY2 } from '../utils';
import { EIP712_TX_TYPE } from '../../src/constants';
import { privateKeyToAccount } from 'web3-eth-accounts';
describe('EIP712Signer', () => {
it('should different wallets be able to use different accounts, even when using the same EIP712Signer', async () => {
const l2Provider = new Web3ZKsyncL2(L2Provider);
const w1 = new ZKsyncWallet(PRIVATE_KEY1, l2Provider);

const s1 = await w1.provider?.signTransaction({
type: EIP712_TX_TYPE,
to: ADDRESS2,
value: 7_000_000n,
from: w1.address,
chainId: 1,
});
const w2 = new ZKsyncWallet(PRIVATE_KEY2, l2Provider);
await w2.provider?.signTransaction({
type: EIP712_TX_TYPE,
to: ADDRESS2,
value: 7_000_000n,
from: w2.address,
chainId: 1,
});

const s11 = await w1.provider?.signTransaction({
type: EIP712_TX_TYPE,
to: ADDRESS2,
value: 7_000_000n,
from: w1.address,
chainId: 1,
});
expect(s11).toBe(s1);
expect(w1.getAddress()).not.toBe(w2.getAddress());
});

it('should different smart accounts be able to use different accounts, even when using the same EIP712Signer', async () => {
const l2Provider = new Web3ZKsyncL2(L2Provider);
const acc = privateKeyToAccount(PRIVATE_KEY1);
const acc2 = privateKeyToAccount(PRIVATE_KEY2);
const sa1 = new SmartAccount(
{
secret: acc.privateKey,
address: acc.address,
},
l2Provider,
);

const s1 = await sa1.provider?.signTransaction({
type: EIP712_TX_TYPE,
to: ADDRESS2,
value: 7_000_000n,
from: acc.address,
chainId: 1,
});
const sa2 = new SmartAccount(
{
secret: acc2.privateKey,
address: acc2.address,
},
l2Provider,
);
await sa2.provider?.signTransaction({
type: EIP712_TX_TYPE,
to: ADDRESS2,
value: 7_000_000n,
from: acc2.address,
chainId: 1,
});

const s11 = await sa1.provider?.signTransaction({
type: EIP712_TX_TYPE,
to: ADDRESS2,
value: 7_000_000n,
from: acc.address,
chainId: 1,
});
expect(s11).toBe(s1);
expect(sa1.getAddress()).not.toBe(sa2.getAddress());
});
});
3 changes: 1 addition & 2 deletions test/unit/signer.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// import '../custom-matchers';
import { EIP712Signer } from '../../src/Eip712';
import { EIP712Signer, EIP712 } from '../../src/Eip712';
import { ADDRESS1, ADDRESS2 } from '../utils';
import { ZeroAddress } from '../../src/types';
import { DEFAULT_GAS_PER_PUBDATA_LIMIT, EIP712_TX_TYPE } from '../../src/constants';
import { EIP712 } from '../../src/Eip712';

describe('EIP712Signer', () => {
describe('#getSignInput()', () => {
Expand Down