From 61f6dc8dda65f06e1ccd371aa174b948c579d926 Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Sat, 25 May 2024 20:08:44 +0200
Subject: [PATCH 01/30] add ABI files and some types and do few naming and
formatting
---
src/contracts/ERC721Token.ts | 173 --
src/contracts/IBridgehub.ts | 631 +++++
src/contracts/IContractDeployer.ts | 410 ++++
src/contracts/IERC1271.ts | 43 +
src/contracts/{ERC20Token.ts => IERC20.ts} | 2 +-
src/contracts/IEthToken.ts | 245 ++
src/contracts/IL1Bridge.ts | 512 ++++
src/contracts/IL1ERC20Bridge.ts | 377 +++
src/contracts/IL1Messenger.ts | 146 ++
src/contracts/IL1SharedBridge.ts | 692 ++++++
src/contracts/{L2Bridge.ts => IL2Bridge.ts} | 2 +-
src/contracts/INonceHolder.ts | 219 ++
src/contracts/IPaymasterFlow.ts | 38 +
src/contracts/ITestnetERC20Token.ts | 39 +
src/contracts/IZkSync.ts | 1622 +++++++++++++
src/contracts/IZkSyncStateTransition.ts | 2403 +++++++++++++++++++
src/plugin.ts | 28 +-
src/types.ts | 308 ++-
test/fixtures.ts | 83 +-
19 files changed, 7712 insertions(+), 261 deletions(-)
delete mode 100644 src/contracts/ERC721Token.ts
create mode 100644 src/contracts/IBridgehub.ts
create mode 100644 src/contracts/IContractDeployer.ts
create mode 100644 src/contracts/IERC1271.ts
rename src/contracts/{ERC20Token.ts => IERC20.ts} (99%)
create mode 100644 src/contracts/IEthToken.ts
create mode 100644 src/contracts/IL1Bridge.ts
create mode 100644 src/contracts/IL1ERC20Bridge.ts
create mode 100644 src/contracts/IL1Messenger.ts
create mode 100644 src/contracts/IL1SharedBridge.ts
rename src/contracts/{L2Bridge.ts => IL2Bridge.ts} (98%)
create mode 100644 src/contracts/INonceHolder.ts
create mode 100644 src/contracts/IPaymasterFlow.ts
create mode 100644 src/contracts/ITestnetERC20Token.ts
create mode 100644 src/contracts/IZkSync.ts
create mode 100644 src/contracts/IZkSyncStateTransition.ts
diff --git a/src/contracts/ERC721Token.ts b/src/contracts/ERC721Token.ts
deleted file mode 100644
index 973db8e..0000000
--- a/src/contracts/ERC721Token.ts
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
-This file is part of web3.js.
-
-web3.js is free software: you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-web3.js is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with web3.js. If not, see .
-*/
-
-export const ERC721TokenAbi = [
- { inputs: [], stateMutability: 'nonpayable', type: 'constructor' },
- {
- anonymous: false,
- inputs: [
- { indexed: true, internalType: 'address', name: 'owner', type: 'address' },
- { indexed: true, internalType: 'address', name: 'approved', type: 'address' },
- { indexed: true, internalType: 'uint256', name: 'tokenId', type: 'uint256' },
- ],
- name: 'Approval',
- type: 'event',
- },
- {
- anonymous: false,
- inputs: [
- { indexed: true, internalType: 'address', name: 'owner', type: 'address' },
- { indexed: true, internalType: 'address', name: 'operator', type: 'address' },
- { indexed: false, internalType: 'bool', name: 'approved', type: 'bool' },
- ],
- name: 'ApprovalForAll',
- type: 'event',
- },
- {
- anonymous: false,
- inputs: [
- { indexed: true, internalType: 'address', name: 'from', type: 'address' },
- { indexed: true, internalType: 'address', name: 'to', type: 'address' },
- { indexed: true, internalType: 'uint256', name: 'tokenId', type: 'uint256' },
- ],
- name: 'Transfer',
- type: 'event',
- },
- {
- inputs: [
- { internalType: 'address', name: 'to', type: 'address' },
- { internalType: 'uint256', name: 'tokenId', type: 'uint256' },
- ],
- name: 'approve',
- outputs: [],
- stateMutability: 'nonpayable',
- type: 'function',
- },
- {
- inputs: [
- { internalType: 'address', name: 'player', type: 'address' },
- { internalType: 'string', name: 'tokenURI', type: 'string' },
- ],
- name: 'awardItem',
- outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
- stateMutability: 'nonpayable',
- type: 'function',
- },
- {
- inputs: [{ internalType: 'address', name: 'owner', type: 'address' }],
- name: 'balanceOf',
- outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
- stateMutability: 'view',
- type: 'function',
- },
- {
- inputs: [{ internalType: 'uint256', name: 'tokenId', type: 'uint256' }],
- name: 'getApproved',
- outputs: [{ internalType: 'address', name: '', type: 'address' }],
- stateMutability: 'view',
- type: 'function',
- },
- {
- inputs: [
- { internalType: 'address', name: 'owner', type: 'address' },
- { internalType: 'address', name: 'operator', type: 'address' },
- ],
- name: 'isApprovedForAll',
- outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
- stateMutability: 'view',
- type: 'function',
- },
- {
- inputs: [],
- name: 'name',
- outputs: [{ internalType: 'string', name: '', type: 'string' }],
- stateMutability: 'view',
- type: 'function',
- },
- {
- inputs: [{ internalType: 'uint256', name: 'tokenId', type: 'uint256' }],
- name: 'ownerOf',
- outputs: [{ internalType: 'address', name: '', type: 'address' }],
- stateMutability: 'view',
- type: 'function',
- },
- {
- inputs: [
- { internalType: 'address', name: 'from', type: 'address' },
- { internalType: 'address', name: 'to', type: 'address' },
- { internalType: 'uint256', name: 'tokenId', type: 'uint256' },
- ],
- name: 'safeTransferFrom',
- outputs: [],
- stateMutability: 'nonpayable',
- type: 'function',
- },
- {
- inputs: [
- { internalType: 'address', name: 'from', type: 'address' },
- { internalType: 'address', name: 'to', type: 'address' },
- { internalType: 'uint256', name: 'tokenId', type: 'uint256' },
- { internalType: 'bytes', name: 'data', type: 'bytes' },
- ],
- name: 'safeTransferFrom',
- outputs: [],
- stateMutability: 'nonpayable',
- type: 'function',
- },
- {
- inputs: [
- { internalType: 'address', name: 'operator', type: 'address' },
- { internalType: 'bool', name: 'approved', type: 'bool' },
- ],
- name: 'setApprovalForAll',
- outputs: [],
- stateMutability: 'nonpayable',
- type: 'function',
- },
- {
- inputs: [{ internalType: 'bytes4', name: 'interfaceId', type: 'bytes4' }],
- name: 'supportsInterface',
- outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
- stateMutability: 'view',
- type: 'function',
- },
- {
- inputs: [],
- name: 'symbol',
- outputs: [{ internalType: 'string', name: '', type: 'string' }],
- stateMutability: 'view',
- type: 'function',
- },
- {
- inputs: [{ internalType: 'uint256', name: 'tokenId', type: 'uint256' }],
- name: 'tokenURI',
- outputs: [{ internalType: 'string', name: '', type: 'string' }],
- stateMutability: 'view',
- type: 'function',
- },
- {
- inputs: [
- { internalType: 'address', name: 'from', type: 'address' },
- { internalType: 'address', name: 'to', type: 'address' },
- { internalType: 'uint256', name: 'tokenId', type: 'uint256' },
- ],
- name: 'transferFrom',
- outputs: [],
- stateMutability: 'nonpayable',
- type: 'function',
- },
-] as const;
diff --git a/src/contracts/IBridgehub.ts b/src/contracts/IBridgehub.ts
new file mode 100644
index 0000000..d1d1468
--- /dev/null
+++ b/src/contracts/IBridgehub.ts
@@ -0,0 +1,631 @@
+export const IBridgehubABI = [
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'oldAdmin',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'newAdmin',
+ type: 'address',
+ },
+ ],
+ name: 'NewAdmin',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'chainId',
+ type: 'uint256',
+ },
+ {
+ indexed: false,
+ internalType: 'address',
+ name: 'stateTransitionManager',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'chainGovernance',
+ type: 'address',
+ },
+ ],
+ name: 'NewChain',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'oldPendingAdmin',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'newPendingAdmin',
+ type: 'address',
+ },
+ ],
+ name: 'NewPendingAdmin',
+ type: 'event',
+ },
+ {
+ inputs: [],
+ name: 'acceptAdmin',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_stateTransitionManager',
+ type: 'address',
+ },
+ ],
+ name: 'addStateTransitionManager',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_token',
+ type: 'address',
+ },
+ ],
+ name: 'addToken',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ ],
+ name: 'baseToken',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: '_stateTransitionManager',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_baseToken',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_salt',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: '_admin',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes',
+ name: '_initData',
+ type: 'bytes',
+ },
+ ],
+ name: 'createNewChain',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: 'chainId',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ ],
+ name: 'getStateTransition',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_gasPrice',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2GasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2GasPerPubdataByteLimit',
+ type: 'uint256',
+ },
+ ],
+ name: 'l2TransactionBaseCost',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_l2TxHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint16',
+ name: '_l2TxNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_merkleProof',
+ type: 'bytes32[]',
+ },
+ {
+ internalType: 'enum TxStatus',
+ name: '_status',
+ type: 'uint8',
+ },
+ ],
+ name: 'proveL1ToL2TransactionStatus',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_batchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_index',
+ type: 'uint256',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint8',
+ name: 'l2ShardId',
+ type: 'uint8',
+ },
+ {
+ internalType: 'bool',
+ name: 'isService',
+ type: 'bool',
+ },
+ {
+ internalType: 'uint16',
+ name: 'txNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'address',
+ name: 'sender',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'key',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'value',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct L2Log',
+ name: '_log',
+ type: 'tuple',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_proof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'proveL2LogInclusion',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_batchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_index',
+ type: 'uint256',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint16',
+ name: 'txNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'address',
+ name: 'sender',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes',
+ name: 'data',
+ type: 'bytes',
+ },
+ ],
+ internalType: 'struct L2Message',
+ name: '_message',
+ type: 'tuple',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_proof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'proveL2MessageInclusion',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_stateTransitionManager',
+ type: 'address',
+ },
+ ],
+ name: 'removeStateTransitionManager',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ components: [
+ {
+ internalType: 'uint256',
+ name: 'chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'mintValue',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: 'l2Contract',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: 'l2Value',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes',
+ name: 'l2Calldata',
+ type: 'bytes',
+ },
+ {
+ internalType: 'uint256',
+ name: 'l2GasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'l2GasPerPubdataByteLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes[]',
+ name: 'factoryDeps',
+ type: 'bytes[]',
+ },
+ {
+ internalType: 'address',
+ name: 'refundRecipient',
+ type: 'address',
+ },
+ ],
+ internalType: 'struct L2TransactionRequestDirect',
+ name: '_request',
+ type: 'tuple',
+ },
+ ],
+ name: 'requestL2TransactionDirect',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: 'canonicalTxHash',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ components: [
+ {
+ internalType: 'uint256',
+ name: 'chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'mintValue',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'l2Value',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'l2GasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'l2GasPerPubdataByteLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: 'refundRecipient',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: 'secondBridgeAddress',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: 'secondBridgeValue',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes',
+ name: 'secondBridgeCalldata',
+ type: 'bytes',
+ },
+ ],
+ internalType: 'struct L2TransactionRequestTwoBridgesOuter',
+ name: '_request',
+ type: 'tuple',
+ },
+ ],
+ name: 'requestL2TransactionTwoBridges',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: 'canonicalTxHash',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_newPendingAdmin',
+ type: 'address',
+ },
+ ],
+ name: 'setPendingAdmin',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_sharedBridge',
+ type: 'address',
+ },
+ ],
+ name: 'setSharedBridge',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'sharedBridge',
+ outputs: [
+ {
+ internalType: 'contract IL1SharedBridge',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ ],
+ name: 'stateTransitionManager',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_stateTransitionManager',
+ type: 'address',
+ },
+ ],
+ name: 'stateTransitionManagerIsRegistered',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_baseToken',
+ type: 'address',
+ },
+ ],
+ name: 'tokenIsRegistered',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+] as const;
diff --git a/src/contracts/IContractDeployer.ts b/src/contracts/IContractDeployer.ts
new file mode 100644
index 0000000..37e4041
--- /dev/null
+++ b/src/contracts/IContractDeployer.ts
@@ -0,0 +1,410 @@
+export const IContractDeployerABI = [
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'accountAddress',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'enum IContractDeployer.AccountNonceOrdering',
+ name: 'nonceOrdering',
+ type: 'uint8',
+ },
+ ],
+ name: 'AccountNonceOrderingUpdated',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'accountAddress',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'enum IContractDeployer.AccountAbstractionVersion',
+ name: 'aaVersion',
+ type: 'uint8',
+ },
+ ],
+ name: 'AccountVersionUpdated',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'deployerAddress',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'bytecodeHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'contractAddress',
+ type: 'address',
+ },
+ ],
+ name: 'ContractDeployed',
+ type: 'event',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bytes32',
+ name: '_salt',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_bytecodeHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes',
+ name: '_input',
+ type: 'bytes',
+ },
+ ],
+ name: 'create',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bytes32',
+ name: '_salt',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_bytecodeHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes',
+ name: '_input',
+ type: 'bytes',
+ },
+ ],
+ name: 'create2',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bytes32',
+ name: '_salt',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_bytecodeHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes',
+ name: '_input',
+ type: 'bytes',
+ },
+ {
+ internalType: 'enum IContractDeployer.AccountAbstractionVersion',
+ name: '_aaVersion',
+ type: 'uint8',
+ },
+ ],
+ name: 'create2Account',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bytes32',
+ name: '',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_bytecodeHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes',
+ name: '_input',
+ type: 'bytes',
+ },
+ {
+ internalType: 'enum IContractDeployer.AccountAbstractionVersion',
+ name: '_aaVersion',
+ type: 'uint8',
+ },
+ ],
+ name: 'createAccount',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_address',
+ type: 'address',
+ },
+ ],
+ name: 'extendedAccountVersion',
+ outputs: [
+ {
+ internalType: 'enum IContractDeployer.AccountAbstractionVersion',
+ name: '',
+ type: 'uint8',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ components: [
+ {
+ internalType: 'bytes32',
+ name: 'bytecodeHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'address',
+ name: 'newAddress',
+ type: 'address',
+ },
+ {
+ internalType: 'bool',
+ name: 'callConstructor',
+ type: 'bool',
+ },
+ {
+ internalType: 'uint256',
+ name: 'value',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes',
+ name: 'input',
+ type: 'bytes',
+ },
+ ],
+ internalType: 'struct ContractDeployer.ForceDeployment',
+ name: '_deployment',
+ type: 'tuple',
+ },
+ {
+ internalType: 'address',
+ name: '_sender',
+ type: 'address',
+ },
+ ],
+ name: 'forceDeployOnAddress',
+ outputs: [],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ components: [
+ {
+ internalType: 'bytes32',
+ name: 'bytecodeHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'address',
+ name: 'newAddress',
+ type: 'address',
+ },
+ {
+ internalType: 'bool',
+ name: 'callConstructor',
+ type: 'bool',
+ },
+ {
+ internalType: 'uint256',
+ name: 'value',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes',
+ name: 'input',
+ type: 'bytes',
+ },
+ ],
+ internalType: 'struct ContractDeployer.ForceDeployment[]',
+ name: '_deployments',
+ type: 'tuple[]',
+ },
+ ],
+ name: 'forceDeployOnAddresses',
+ outputs: [],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_address',
+ type: 'address',
+ },
+ ],
+ name: 'getAccountInfo',
+ outputs: [
+ {
+ components: [
+ {
+ internalType: 'enum IContractDeployer.AccountAbstractionVersion',
+ name: 'supportedAAVersion',
+ type: 'uint8',
+ },
+ {
+ internalType: 'enum IContractDeployer.AccountNonceOrdering',
+ name: 'nonceOrdering',
+ type: 'uint8',
+ },
+ ],
+ internalType: 'struct IContractDeployer.AccountInfo',
+ name: 'info',
+ type: 'tuple',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_sender',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_senderNonce',
+ type: 'uint256',
+ },
+ ],
+ name: 'getNewAddressCreate',
+ outputs: [
+ {
+ internalType: 'address',
+ name: 'newAddress',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'pure',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_sender',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_bytecodeHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_salt',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes',
+ name: '_input',
+ type: 'bytes',
+ },
+ ],
+ name: 'getNewAddressCreate2',
+ outputs: [
+ {
+ internalType: 'address',
+ name: 'newAddress',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'enum IContractDeployer.AccountAbstractionVersion',
+ name: '_version',
+ type: 'uint8',
+ },
+ ],
+ name: 'updateAccountVersion',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'enum IContractDeployer.AccountNonceOrdering',
+ name: '_nonceOrdering',
+ type: 'uint8',
+ },
+ ],
+ name: 'updateNonceOrdering',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+] as const;
diff --git a/src/contracts/IERC1271.ts b/src/contracts/IERC1271.ts
new file mode 100644
index 0000000..2028356
--- /dev/null
+++ b/src/contracts/IERC1271.ts
@@ -0,0 +1,43 @@
+/*
+This file is part of web3.js.
+
+web3.js is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+web3.js is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with web3.js. If not, see .
+*/
+
+export const IERC1271ABI = [
+ {
+ inputs: [
+ {
+ internalType: 'bytes32',
+ name: 'hash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes',
+ name: 'signature',
+ type: 'bytes',
+ },
+ ],
+ name: 'isValidSignature',
+ outputs: [
+ {
+ internalType: 'bytes4',
+ name: 'magicValue',
+ type: 'bytes4',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+] as const;
diff --git a/src/contracts/ERC20Token.ts b/src/contracts/IERC20.ts
similarity index 99%
rename from src/contracts/ERC20Token.ts
rename to src/contracts/IERC20.ts
index 98bdf44..7b64f8f 100644
--- a/src/contracts/ERC20Token.ts
+++ b/src/contracts/IERC20.ts
@@ -15,7 +15,7 @@ You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see .
*/
-export const ERC20TokenAbi = [
+export const IERC20ABI = [
{
inputs: [{ internalType: 'uint256', name: 'initialSupply', type: 'uint256' }],
stateMutability: 'nonpayable',
diff --git a/src/contracts/IEthToken.ts b/src/contracts/IEthToken.ts
new file mode 100644
index 0000000..4820993
--- /dev/null
+++ b/src/contracts/IEthToken.ts
@@ -0,0 +1,245 @@
+export const Abi = [
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'account',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'Mint',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'from',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'value',
+ type: 'uint256',
+ },
+ ],
+ name: 'Transfer',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: '_l2Sender',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: '_l1Receiver',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: '_amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'Withdrawal',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: '_l2Sender',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: '_l1Receiver',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: '_amount',
+ type: 'uint256',
+ },
+ {
+ indexed: false,
+ internalType: 'bytes',
+ name: '_additionalData',
+ type: 'bytes',
+ },
+ ],
+ name: 'WithdrawalWithMessage',
+ type: 'event',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ name: 'balanceOf',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'decimals',
+ outputs: [
+ {
+ internalType: 'uint8',
+ name: '',
+ type: 'uint8',
+ },
+ ],
+ stateMutability: 'pure',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_account',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'mint',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'name',
+ outputs: [
+ {
+ internalType: 'string',
+ name: '',
+ type: 'string',
+ },
+ ],
+ stateMutability: 'pure',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'symbol',
+ outputs: [
+ {
+ internalType: 'string',
+ name: '',
+ type: 'string',
+ },
+ ],
+ stateMutability: 'pure',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'totalSupply',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_from',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_to',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'transferFromTo',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_l1Receiver',
+ type: 'address',
+ },
+ ],
+ name: 'withdraw',
+ outputs: [],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_l1Receiver',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes',
+ name: '_additionalData',
+ type: 'bytes',
+ },
+ ],
+ name: 'withdrawWithMessage',
+ outputs: [],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+] as const;
diff --git a/src/contracts/IL1Bridge.ts b/src/contracts/IL1Bridge.ts
new file mode 100644
index 0000000..3e24678
--- /dev/null
+++ b/src/contracts/IL1Bridge.ts
@@ -0,0 +1,512 @@
+export const IL1BridgeABI = [
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'chainId',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'txDataHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'l2DepositTxHash',
+ type: 'bytes32',
+ },
+ ],
+ name: 'BridgehubDepositFinalized',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'chainId',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'txDataHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'from',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'address',
+ name: 'l1Token',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'BridgehubDepositInitiatedSharedBridge',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'chainId',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'l1Token',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'ClaimedFailedDepositSharedBridge',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'chainId',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'l2DepositTxHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'from',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'address',
+ name: 'l1Token',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'DepositInitiatedSharedBridge',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'chainId',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'l1Token',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'WithdrawalFinalizedSharedBridge',
+ type: 'event',
+ },
+ {
+ inputs: [],
+ name: 'bridgehub',
+ outputs: [
+ {
+ internalType: 'contract IBridgehub',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_txDataHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_txHash',
+ type: 'bytes32',
+ },
+ ],
+ name: 'bridgehubConfirmL2Transaction',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: '_prevMsgSender',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes',
+ name: '_data',
+ type: 'bytes',
+ },
+ ],
+ name: 'bridgehubDeposit',
+ outputs: [
+ {
+ components: [
+ {
+ internalType: 'bytes32',
+ name: 'magicValue',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'address',
+ name: 'l2Contract',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes',
+ name: 'l2Calldata',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes[]',
+ name: 'factoryDeps',
+ type: 'bytes[]',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'txDataHash',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct L2TransactionRequestTwoBridgesInner',
+ name: 'request',
+ type: 'tuple',
+ },
+ ],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: '_prevMsgSender',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_l1Token',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'bridgehubDepositBaseToken',
+ outputs: [],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: '_depositSender',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_l1Token',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_amount',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_l2TxHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint16',
+ name: '_l2TxNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_merkleProof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'claimFailedDeposit',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: '_l2Receiver',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_l1Token',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_mintValue',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_amount',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2TxGasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2TxGasPerPubdataByte',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: '_refundRecipient',
+ type: 'address',
+ },
+ ],
+ name: 'deposit',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: 'txHash',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_l2TxHash',
+ type: 'bytes32',
+ },
+ ],
+ name: 'depositHappened',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: '',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint16',
+ name: '_l2TxNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'bytes',
+ name: '_message',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_merkleProof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'finalizeWithdrawal',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ ],
+ name: 'isWithdrawalFinalizedShared',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ ],
+ name: 'l2BridgeAddress',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+] as const;
diff --git a/src/contracts/IL1ERC20Bridge.ts b/src/contracts/IL1ERC20Bridge.ts
new file mode 100644
index 0000000..ee84bc5
--- /dev/null
+++ b/src/contracts/IL1ERC20Bridge.ts
@@ -0,0 +1,377 @@
+export const IL1BridgeABI = [
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'l1Token',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'ClaimedFailedDeposit',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'l2DepositTxHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'from',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'address',
+ name: 'l1Token',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'DepositInitiated',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'l1Token',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'WithdrawalFinalized',
+ type: 'event',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_depositSender',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_l1Token',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_l2TxHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint16',
+ name: '_l2TxNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_merkleProof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'claimFailedDeposit',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_l2Receiver',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_l1Token',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_amount',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2TxGasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2TxGasPerPubdataByte',
+ type: 'uint256',
+ },
+ ],
+ name: 'deposit',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: 'txHash',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_l2Receiver',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_l1Token',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_amount',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2TxGasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2TxGasPerPubdataByte',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: '_refundRecipient',
+ type: 'address',
+ },
+ ],
+ name: 'deposit',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: 'txHash',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_account',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_l1Token',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_depositL2TxHash',
+ type: 'bytes32',
+ },
+ ],
+ name: 'depositAmount',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint16',
+ name: '_l2TxNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'bytes',
+ name: '_message',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_merkleProof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'finalizeWithdrawal',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ ],
+ name: 'isWithdrawalFinalized',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'l2Bridge',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_l1Token',
+ type: 'address',
+ },
+ ],
+ name: 'l2TokenAddress',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'l2TokenBeacon',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'sharedBridge',
+ outputs: [
+ {
+ internalType: 'contract IL1SharedBridge',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_token',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'transferTokenToSharedBridge',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+] as const;
diff --git a/src/contracts/IL1Messenger.ts b/src/contracts/IL1Messenger.ts
new file mode 100644
index 0000000..47627a4
--- /dev/null
+++ b/src/contracts/IL1Messenger.ts
@@ -0,0 +1,146 @@
+export const IL1MessengerABI = [
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'bytes32',
+ name: '_bytecodeHash',
+ type: 'bytes32',
+ },
+ ],
+ name: 'BytecodeL1PublicationRequested',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: '_sender',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: '_hash',
+ type: 'bytes32',
+ },
+ {
+ indexed: false,
+ internalType: 'bytes',
+ name: '_message',
+ type: 'bytes',
+ },
+ ],
+ name: 'L1MessageSent',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ components: [
+ {
+ internalType: 'uint8',
+ name: 'l2ShardId',
+ type: 'uint8',
+ },
+ {
+ internalType: 'bool',
+ name: 'isService',
+ type: 'bool',
+ },
+ {
+ internalType: 'uint16',
+ name: 'txNumberInBlock',
+ type: 'uint16',
+ },
+ {
+ internalType: 'address',
+ name: 'sender',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'key',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'value',
+ type: 'bytes32',
+ },
+ ],
+ indexed: false,
+ internalType: 'struct L2ToL1Log',
+ name: '_l2log',
+ type: 'tuple',
+ },
+ ],
+ name: 'L2ToL1LogSent',
+ type: 'event',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bytes32',
+ name: '_bytecodeHash',
+ type: 'bytes32',
+ },
+ ],
+ name: 'requestBytecodeL1Publication',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bool',
+ name: '_isService',
+ type: 'bool',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_key',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_value',
+ type: 'bytes32',
+ },
+ ],
+ name: 'sendL2ToL1Log',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: 'logIdInMerkleTree',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bytes',
+ name: '_message',
+ type: 'bytes',
+ },
+ ],
+ name: 'sendToL1',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: '',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+] as const;
diff --git a/src/contracts/IL1SharedBridge.ts b/src/contracts/IL1SharedBridge.ts
new file mode 100644
index 0000000..17d7b60
--- /dev/null
+++ b/src/contracts/IL1SharedBridge.ts
@@ -0,0 +1,692 @@
+export const Abi = [
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'chainId',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'from',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'address',
+ name: 'l1Token',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'BridgehubDepositBaseTokenInitiated',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'chainId',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'txDataHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'l2DepositTxHash',
+ type: 'bytes32',
+ },
+ ],
+ name: 'BridgehubDepositFinalized',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'chainId',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'txDataHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'from',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'address',
+ name: 'l1Token',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'BridgehubDepositInitiated',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'chainId',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'l1Token',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'ClaimedFailedDepositSharedBridge',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'chainId',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'l2DepositTxHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'from',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'address',
+ name: 'l1Token',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'LegacyDepositInitiated',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'chainId',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'l1Token',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'WithdrawalFinalizedSharedBridge',
+ type: 'event',
+ },
+ {
+ inputs: [],
+ name: 'bridgehub',
+ outputs: [
+ {
+ internalType: 'contract IBridgehub',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_txDataHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_txHash',
+ type: 'bytes32',
+ },
+ ],
+ name: 'bridgehubConfirmL2Transaction',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: '_prevMsgSender',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2Value',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes',
+ name: '_data',
+ type: 'bytes',
+ },
+ ],
+ name: 'bridgehubDeposit',
+ outputs: [
+ {
+ components: [
+ {
+ internalType: 'bytes32',
+ name: 'magicValue',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'address',
+ name: 'l2Contract',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes',
+ name: 'l2Calldata',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes[]',
+ name: 'factoryDeps',
+ type: 'bytes[]',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'txDataHash',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct L2TransactionRequestTwoBridgesInner',
+ name: 'request',
+ type: 'tuple',
+ },
+ ],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: '_prevMsgSender',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_l1Token',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'bridgehubDepositBaseToken',
+ outputs: [],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: '_depositSender',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_l1Token',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_amount',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_l2TxHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint16',
+ name: '_l2TxNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_merkleProof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'claimFailedDeposit',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_depositSender',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_l1Token',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_amount',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_l2TxHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint16',
+ name: '_l2TxNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_merkleProof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'claimFailedDepositLegacyErc20Bridge',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: '_l2TxHash',
+ type: 'bytes32',
+ },
+ ],
+ name: 'depositHappened',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: '',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_msgSender',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_l2Receiver',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_l1Token',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_amount',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2TxGasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2TxGasPerPubdataByte',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address',
+ name: '_refundRecipient',
+ type: 'address',
+ },
+ ],
+ name: 'depositLegacyErc20Bridge',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: 'txHash',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint16',
+ name: '_l2TxNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'bytes',
+ name: '_message',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_merkleProof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'finalizeWithdrawal',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint16',
+ name: '_l2TxNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'bytes',
+ name: '_message',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_merkleProof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'finalizeWithdrawalLegacyErc20Bridge',
+ outputs: [
+ {
+ internalType: 'address',
+ name: 'l1Receiver',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: 'l1Token',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ ],
+ name: 'isWithdrawalFinalized',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'l1WethAddress',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ ],
+ name: 'l2BridgeAddress',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'legacyBridge',
+ outputs: [
+ {
+ internalType: 'contract IL1ERC20Bridge',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ ],
+ name: 'receiveEth',
+ outputs: [],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_eraFirstPostUpgradeBatch',
+ type: 'uint256',
+ },
+ ],
+ name: 'setEraFirstPostUpgradeBatch',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+] as const;
diff --git a/src/contracts/L2Bridge.ts b/src/contracts/IL2Bridge.ts
similarity index 98%
rename from src/contracts/L2Bridge.ts
rename to src/contracts/IL2Bridge.ts
index 31e9f8f..51e177e 100644
--- a/src/contracts/L2Bridge.ts
+++ b/src/contracts/IL2Bridge.ts
@@ -1,5 +1,5 @@
// https://docs.zksync.io/build/sdks/go/types/types.html#l2bridgecontracts
-export const L2BridgeAbi = [
+export const IL2BridgeABI = [
{
inputs: [
{
diff --git a/src/contracts/INonceHolder.ts b/src/contracts/INonceHolder.ts
new file mode 100644
index 0000000..bd4f685
--- /dev/null
+++ b/src/contracts/INonceHolder.ts
@@ -0,0 +1,219 @@
+export const INonceHolderABI = [
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'accountAddress',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'key',
+ type: 'uint256',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'value',
+ type: 'uint256',
+ },
+ ],
+ name: 'ValueSetUnderNonce',
+ type: 'event',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_address',
+ type: 'address',
+ },
+ ],
+ name: 'getDeploymentNonce',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_address',
+ type: 'address',
+ },
+ ],
+ name: 'getMinNonce',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_address',
+ type: 'address',
+ },
+ ],
+ name: 'getRawNonce',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_key',
+ type: 'uint256',
+ },
+ ],
+ name: 'getValueUnderNonce',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_value',
+ type: 'uint256',
+ },
+ ],
+ name: 'increaseMinNonce',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_address',
+ type: 'address',
+ },
+ ],
+ name: 'incrementDeploymentNonce',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_expectedNonce',
+ type: 'uint256',
+ },
+ ],
+ name: 'incrementMinNonceIfEquals',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_address',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_nonce',
+ type: 'uint256',
+ },
+ ],
+ name: 'isNonceUsed',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_key',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_value',
+ type: 'uint256',
+ },
+ ],
+ name: 'setValueUnderNonce',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_address',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_key',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bool',
+ name: '_shouldBeUsed',
+ type: 'bool',
+ },
+ ],
+ name: 'validateNonceUsage',
+ outputs: [],
+ stateMutability: 'view',
+ type: 'function',
+ },
+] as const;
diff --git a/src/contracts/IPaymasterFlow.ts b/src/contracts/IPaymasterFlow.ts
new file mode 100644
index 0000000..5c1f3a3
--- /dev/null
+++ b/src/contracts/IPaymasterFlow.ts
@@ -0,0 +1,38 @@
+export const Abi = [
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_token',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_minAllowance',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes',
+ name: '_innerInput',
+ type: 'bytes',
+ },
+ ],
+ name: 'approvalBased',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bytes',
+ name: 'input',
+ type: 'bytes',
+ },
+ ],
+ name: 'general',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+] as const;
diff --git a/src/contracts/ITestnetERC20Token.ts b/src/contracts/ITestnetERC20Token.ts
new file mode 100644
index 0000000..c1e590d
--- /dev/null
+++ b/src/contracts/ITestnetERC20Token.ts
@@ -0,0 +1,39 @@
+export const Abi = [
+ {
+ inputs: [],
+ name: 'decimals',
+ outputs: [
+ {
+ internalType: 'uint8',
+ name: '',
+ type: 'uint8',
+ },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_to',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'mint',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+] as const;
diff --git a/src/contracts/IZkSync.ts b/src/contracts/IZkSync.ts
new file mode 100644
index 0000000..bff7c89
--- /dev/null
+++ b/src/contracts/IZkSync.ts
@@ -0,0 +1,1622 @@
+export const Abi = [
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'batchNumber',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ name: 'BlockCommit',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'batchNumber',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ name: 'BlockExecution',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'totalBatchesCommitted',
+ type: 'uint256',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'totalBatchesVerified',
+ type: 'uint256',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'totalBatchesExecuted',
+ type: 'uint256',
+ },
+ ],
+ name: 'BlocksRevert',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'previousLastVerifiedBatch',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'currentLastVerifiedBatch',
+ type: 'uint256',
+ },
+ ],
+ name: 'BlocksVerification',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'EthWithdrawalFinalized',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ components: [
+ {
+ components: [
+ {
+ internalType: 'address',
+ name: 'facet',
+ type: 'address',
+ },
+ {
+ internalType: 'enum Diamond.Action',
+ name: 'action',
+ type: 'uint8',
+ },
+ {
+ internalType: 'bool',
+ name: 'isFreezable',
+ type: 'bool',
+ },
+ {
+ internalType: 'bytes4[]',
+ name: 'selectors',
+ type: 'bytes4[]',
+ },
+ ],
+ internalType: 'struct Diamond.FacetCut[]',
+ name: 'facetCuts',
+ type: 'tuple[]',
+ },
+ {
+ internalType: 'address',
+ name: 'initAddress',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes',
+ name: 'initCalldata',
+ type: 'bytes',
+ },
+ ],
+ indexed: false,
+ internalType: 'struct Diamond.DiamondCutData',
+ name: 'diamondCut',
+ type: 'tuple',
+ },
+ ],
+ name: 'ExecuteUpgrade',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [],
+ name: 'Freeze',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'bool',
+ name: 'isPorterAvailable',
+ type: 'bool',
+ },
+ ],
+ name: 'IsPorterAvailableStatusUpdate',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'oldAdmin',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'newAdmin',
+ type: 'address',
+ },
+ ],
+ name: 'NewAdmin',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'oldGovernor',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'newGovernor',
+ type: 'address',
+ },
+ ],
+ name: 'NewGovernor',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'oldPendingAdmin',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'newPendingAdmin',
+ type: 'address',
+ },
+ ],
+ name: 'NewPendingAdmin',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'oldPendingGovernor',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'newPendingGovernor',
+ type: 'address',
+ },
+ ],
+ name: 'NewPendingGovernor',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'txId',
+ type: 'uint256',
+ },
+ {
+ indexed: false,
+ internalType: 'bytes32',
+ name: 'txHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: false,
+ internalType: 'uint64',
+ name: 'expirationTimestamp',
+ type: 'uint64',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint256',
+ name: 'txType',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'from',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'to',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'gasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'gasPerPubdataByteLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'maxFeePerGas',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'maxPriorityFeePerGas',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'paymaster',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'nonce',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'value',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256[4]',
+ name: 'reserved',
+ type: 'uint256[4]',
+ },
+ {
+ internalType: 'bytes',
+ name: 'data',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes',
+ name: 'signature',
+ type: 'bytes',
+ },
+ {
+ internalType: 'uint256[]',
+ name: 'factoryDeps',
+ type: 'uint256[]',
+ },
+ {
+ internalType: 'bytes',
+ name: 'paymasterInput',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes',
+ name: 'reservedDynamic',
+ type: 'bytes',
+ },
+ ],
+ indexed: false,
+ internalType: 'struct IMailbox.L2CanonicalTransaction',
+ name: 'transaction',
+ type: 'tuple',
+ },
+ {
+ indexed: false,
+ internalType: 'bytes[]',
+ name: 'factoryDeps',
+ type: 'bytes[]',
+ },
+ ],
+ name: 'NewPriorityRequest',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'oldPriorityTxMaxGasLimit',
+ type: 'uint256',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'newPriorityTxMaxGasLimit',
+ type: 'uint256',
+ },
+ ],
+ name: 'NewPriorityTxMaxGasLimit',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [],
+ name: 'Unfreeze',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'validatorAddress',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'bool',
+ name: 'isActive',
+ type: 'bool',
+ },
+ ],
+ name: 'ValidatorStatusUpdate',
+ type: 'event',
+ },
+ {
+ inputs: [],
+ name: 'acceptAdmin',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'acceptGovernor',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ components: [
+ {
+ internalType: 'uint64',
+ name: 'batchNumber',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'indexRepeatedStorageChanges',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint256',
+ name: 'numberOfLayer1Txs',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'priorityOperationsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'l2LogsTreeRoot',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: 'timestamp',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct IExecutor.StoredBatchInfo',
+ name: '_lastCommittedBatchData',
+ type: 'tuple',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint64',
+ name: 'batchNumber',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint64',
+ name: 'timestamp',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint64',
+ name: 'indexRepeatedStorageChanges',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'newStateRoot',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: 'numberOfLayer1Txs',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'priorityOperationsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'bootloaderHeapInitialContentsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'eventsQueueStateHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes',
+ name: 'systemLogs',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes',
+ name: 'totalL2ToL1Pubdata',
+ type: 'bytes',
+ },
+ ],
+ internalType: 'struct IExecutor.CommitBatchInfo[]',
+ name: '_newBatchesData',
+ type: 'tuple[]',
+ },
+ ],
+ name: 'commitBatches',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ components: [
+ {
+ internalType: 'uint64',
+ name: 'batchNumber',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'indexRepeatedStorageChanges',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint256',
+ name: 'numberOfLayer1Txs',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'priorityOperationsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'l2LogsTreeRoot',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: 'timestamp',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct IExecutor.StoredBatchInfo[]',
+ name: '_batchesData',
+ type: 'tuple[]',
+ },
+ ],
+ name: 'executeBatches',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ components: [
+ {
+ components: [
+ {
+ internalType: 'address',
+ name: 'facet',
+ type: 'address',
+ },
+ {
+ internalType: 'enum Diamond.Action',
+ name: 'action',
+ type: 'uint8',
+ },
+ {
+ internalType: 'bool',
+ name: 'isFreezable',
+ type: 'bool',
+ },
+ {
+ internalType: 'bytes4[]',
+ name: 'selectors',
+ type: 'bytes4[]',
+ },
+ ],
+ internalType: 'struct Diamond.FacetCut[]',
+ name: 'facetCuts',
+ type: 'tuple[]',
+ },
+ {
+ internalType: 'address',
+ name: 'initAddress',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes',
+ name: 'initCalldata',
+ type: 'bytes',
+ },
+ ],
+ internalType: 'struct Diamond.DiamondCutData',
+ name: '_diamondCut',
+ type: 'tuple',
+ },
+ ],
+ name: 'executeUpgrade',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bytes4',
+ name: '_selector',
+ type: 'bytes4',
+ },
+ ],
+ name: 'facetAddress',
+ outputs: [
+ {
+ internalType: 'address',
+ name: 'facet',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'facetAddresses',
+ outputs: [
+ {
+ internalType: 'address[]',
+ name: 'facets',
+ type: 'address[]',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_facet',
+ type: 'address',
+ },
+ ],
+ name: 'facetFunctionSelectors',
+ outputs: [
+ {
+ internalType: 'bytes4[]',
+ name: '',
+ type: 'bytes4[]',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'facets',
+ outputs: [
+ {
+ components: [
+ {
+ internalType: 'address',
+ name: 'addr',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes4[]',
+ name: 'selectors',
+ type: 'bytes4[]',
+ },
+ ],
+ internalType: 'struct IGetters.Facet[]',
+ name: '',
+ type: 'tuple[]',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint16',
+ name: '_l2TxNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'bytes',
+ name: '_message',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_merkleProof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'finalizeEthWithdrawal',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'freezeDiamond',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getFirstUnprocessedPriorityTx',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getGovernor',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getL2BootloaderBytecodeHash',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: '',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getL2DefaultAccountBytecodeHash',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: '',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getL2SystemContractsUpgradeBatchNumber',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getL2SystemContractsUpgradeTxHash',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: '',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getName',
+ outputs: [
+ {
+ internalType: 'string',
+ name: '',
+ type: 'string',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getPendingGovernor',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getPriorityQueueSize',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getPriorityTxMaxGasLimit',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getProtocolVersion',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getTotalBatchesCommitted',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getTotalBatchesExecuted',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getTotalBatchesVerified',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getTotalPriorityTxs',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getVerifier',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getVerifierParams',
+ outputs: [
+ {
+ components: [
+ {
+ internalType: 'bytes32',
+ name: 'recursionNodeLevelVkHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'recursionLeafLevelVkHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'recursionCircuitsSetVksHash',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct VerifierParams',
+ name: '',
+ type: 'tuple',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'isDiamondStorageFrozen',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ ],
+ name: 'isEthWithdrawalFinalized',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_facet',
+ type: 'address',
+ },
+ ],
+ name: 'isFacetFreezable',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: 'isFreezable',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bytes4',
+ name: '_selector',
+ type: 'bytes4',
+ },
+ ],
+ name: 'isFunctionFreezable',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_address',
+ type: 'address',
+ },
+ ],
+ name: 'isValidator',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_batchNumber',
+ type: 'uint256',
+ },
+ ],
+ name: 'l2LogsRootHash',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: 'hash',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_gasPrice',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2GasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2GasPerPubdataByteLimit',
+ type: 'uint256',
+ },
+ ],
+ name: 'l2TransactionBaseCost',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'priorityQueueFrontOperation',
+ outputs: [
+ {
+ components: [
+ {
+ internalType: 'bytes32',
+ name: 'canonicalTxHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'expirationTimestamp',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint192',
+ name: 'layer2Tip',
+ type: 'uint192',
+ },
+ ],
+ internalType: 'struct PriorityOperation',
+ name: '',
+ type: 'tuple',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ components: [
+ {
+ internalType: 'uint64',
+ name: 'batchNumber',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'indexRepeatedStorageChanges',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint256',
+ name: 'numberOfLayer1Txs',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'priorityOperationsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'l2LogsTreeRoot',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: 'timestamp',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct IExecutor.StoredBatchInfo',
+ name: '_prevBatch',
+ type: 'tuple',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint64',
+ name: 'batchNumber',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'indexRepeatedStorageChanges',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint256',
+ name: 'numberOfLayer1Txs',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'priorityOperationsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'l2LogsTreeRoot',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: 'timestamp',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct IExecutor.StoredBatchInfo[]',
+ name: '_committedBatches',
+ type: 'tuple[]',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint256[]',
+ name: 'recursiveAggregationInput',
+ type: 'uint256[]',
+ },
+ {
+ internalType: 'uint256[]',
+ name: 'serializedProof',
+ type: 'uint256[]',
+ },
+ ],
+ internalType: 'struct IExecutor.ProofInput',
+ name: '_proof',
+ type: 'tuple',
+ },
+ ],
+ name: 'proveBatches',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bytes32',
+ name: '_l2TxHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint16',
+ name: '_l2TxNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_merkleProof',
+ type: 'bytes32[]',
+ },
+ {
+ internalType: 'enum TxStatus',
+ name: '_status',
+ type: 'uint8',
+ },
+ ],
+ name: 'proveL1ToL2TransactionStatus',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_index',
+ type: 'uint256',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint8',
+ name: 'l2ShardId',
+ type: 'uint8',
+ },
+ {
+ internalType: 'bool',
+ name: 'isService',
+ type: 'bool',
+ },
+ {
+ internalType: 'uint16',
+ name: 'txNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'address',
+ name: 'sender',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'key',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'value',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct L2Log',
+ name: '_log',
+ type: 'tuple',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_proof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'proveL2LogInclusion',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_index',
+ type: 'uint256',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint16',
+ name: 'txNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'address',
+ name: 'sender',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes',
+ name: 'data',
+ type: 'bytes',
+ },
+ ],
+ internalType: 'struct L2Message',
+ name: '_message',
+ type: 'tuple',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_proof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'proveL2MessageInclusion',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_contractL2',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2Value',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes',
+ name: '_calldata',
+ type: 'bytes',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2GasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2GasPerPubdataByteLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes[]',
+ name: '_factoryDeps',
+ type: 'bytes[]',
+ },
+ {
+ internalType: 'address',
+ name: '_refundRecipient',
+ type: 'address',
+ },
+ ],
+ name: 'requestL2Transaction',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: 'canonicalTxHash',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_newLastBatch',
+ type: 'uint256',
+ },
+ ],
+ name: 'revertBatches',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_newPendingAdmin',
+ type: 'address',
+ },
+ ],
+ name: 'setPendingAdmin',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_newPendingGovernor',
+ type: 'address',
+ },
+ ],
+ name: 'setPendingGovernor',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bool',
+ name: '_zkPorterIsAvailable',
+ type: 'bool',
+ },
+ ],
+ name: 'setPorterAvailability',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_newPriorityTxMaxGasLimit',
+ type: 'uint256',
+ },
+ ],
+ name: 'setPriorityTxMaxGasLimit',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_validator',
+ type: 'address',
+ },
+ {
+ internalType: 'bool',
+ name: '_active',
+ type: 'bool',
+ },
+ ],
+ name: 'setValidator',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_batchNumber',
+ type: 'uint256',
+ },
+ ],
+ name: 'storedBatchHash',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: '',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'unfreezeDiamond',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+] as const;
diff --git a/src/contracts/IZkSyncStateTransition.ts b/src/contracts/IZkSyncStateTransition.ts
new file mode 100644
index 0000000..71f4d09
--- /dev/null
+++ b/src/contracts/IZkSyncStateTransition.ts
@@ -0,0 +1,2403 @@
+export const IZkSyncABI = [
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'batchNumber',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ name: 'BlockCommit',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'batchNumber',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: true,
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ name: 'BlockExecution',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'totalBatchesCommitted',
+ type: 'uint256',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'totalBatchesVerified',
+ type: 'uint256',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'totalBatchesExecuted',
+ type: 'uint256',
+ },
+ ],
+ name: 'BlocksRevert',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'previousLastVerifiedBatch',
+ type: 'uint256',
+ },
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'currentLastVerifiedBatch',
+ type: 'uint256',
+ },
+ ],
+ name: 'BlocksVerification',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'EthWithdrawalFinalized',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ components: [
+ {
+ components: [
+ {
+ internalType: 'address',
+ name: 'facet',
+ type: 'address',
+ },
+ {
+ internalType: 'enum Diamond.Action',
+ name: 'action',
+ type: 'uint8',
+ },
+ {
+ internalType: 'bool',
+ name: 'isFreezable',
+ type: 'bool',
+ },
+ {
+ internalType: 'bytes4[]',
+ name: 'selectors',
+ type: 'bytes4[]',
+ },
+ ],
+ internalType: 'struct Diamond.FacetCut[]',
+ name: 'facetCuts',
+ type: 'tuple[]',
+ },
+ {
+ internalType: 'address',
+ name: 'initAddress',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes',
+ name: 'initCalldata',
+ type: 'bytes',
+ },
+ ],
+ indexed: false,
+ internalType: 'struct Diamond.DiamondCutData',
+ name: 'diamondCut',
+ type: 'tuple',
+ },
+ ],
+ name: 'ExecuteUpgrade',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [],
+ name: 'Freeze',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'bool',
+ name: 'isPorterAvailable',
+ type: 'bool',
+ },
+ ],
+ name: 'IsPorterAvailableStatusUpdate',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'oldAdmin',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'newAdmin',
+ type: 'address',
+ },
+ ],
+ name: 'NewAdmin',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'uint128',
+ name: 'oldNominator',
+ type: 'uint128',
+ },
+ {
+ indexed: false,
+ internalType: 'uint128',
+ name: 'oldDenominator',
+ type: 'uint128',
+ },
+ {
+ indexed: false,
+ internalType: 'uint128',
+ name: 'newNominator',
+ type: 'uint128',
+ },
+ {
+ indexed: false,
+ internalType: 'uint128',
+ name: 'newDenominator',
+ type: 'uint128',
+ },
+ ],
+ name: 'NewBaseTokenMultiplier',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ components: [
+ {
+ internalType: 'enum PubdataPricingMode',
+ name: 'pubdataPricingMode',
+ type: 'uint8',
+ },
+ {
+ internalType: 'uint32',
+ name: 'batchOverheadL1Gas',
+ type: 'uint32',
+ },
+ {
+ internalType: 'uint32',
+ name: 'maxPubdataPerBatch',
+ type: 'uint32',
+ },
+ {
+ internalType: 'uint32',
+ name: 'maxL2GasPerBatch',
+ type: 'uint32',
+ },
+ {
+ internalType: 'uint32',
+ name: 'priorityTxMaxPubdata',
+ type: 'uint32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'minimalL2GasPrice',
+ type: 'uint64',
+ },
+ ],
+ indexed: false,
+ internalType: 'struct FeeParams',
+ name: 'oldFeeParams',
+ type: 'tuple',
+ },
+ {
+ components: [
+ {
+ internalType: 'enum PubdataPricingMode',
+ name: 'pubdataPricingMode',
+ type: 'uint8',
+ },
+ {
+ internalType: 'uint32',
+ name: 'batchOverheadL1Gas',
+ type: 'uint32',
+ },
+ {
+ internalType: 'uint32',
+ name: 'maxPubdataPerBatch',
+ type: 'uint32',
+ },
+ {
+ internalType: 'uint32',
+ name: 'maxL2GasPerBatch',
+ type: 'uint32',
+ },
+ {
+ internalType: 'uint32',
+ name: 'priorityTxMaxPubdata',
+ type: 'uint32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'minimalL2GasPrice',
+ type: 'uint64',
+ },
+ ],
+ indexed: false,
+ internalType: 'struct FeeParams',
+ name: 'newFeeParams',
+ type: 'tuple',
+ },
+ ],
+ name: 'NewFeeParams',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'oldPendingAdmin',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'newPendingAdmin',
+ type: 'address',
+ },
+ ],
+ name: 'NewPendingAdmin',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'txId',
+ type: 'uint256',
+ },
+ {
+ indexed: false,
+ internalType: 'bytes32',
+ name: 'txHash',
+ type: 'bytes32',
+ },
+ {
+ indexed: false,
+ internalType: 'uint64',
+ name: 'expirationTimestamp',
+ type: 'uint64',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint256',
+ name: 'txType',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'from',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'to',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'gasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'gasPerPubdataByteLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'maxFeePerGas',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'maxPriorityFeePerGas',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'paymaster',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'nonce',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'value',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256[4]',
+ name: 'reserved',
+ type: 'uint256[4]',
+ },
+ {
+ internalType: 'bytes',
+ name: 'data',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes',
+ name: 'signature',
+ type: 'bytes',
+ },
+ {
+ internalType: 'uint256[]',
+ name: 'factoryDeps',
+ type: 'uint256[]',
+ },
+ {
+ internalType: 'bytes',
+ name: 'paymasterInput',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes',
+ name: 'reservedDynamic',
+ type: 'bytes',
+ },
+ ],
+ indexed: false,
+ internalType: 'struct L2CanonicalTransaction',
+ name: 'transaction',
+ type: 'tuple',
+ },
+ {
+ indexed: false,
+ internalType: 'bytes[]',
+ name: 'factoryDeps',
+ type: 'bytes[]',
+ },
+ ],
+ name: 'NewPriorityRequest',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'oldPriorityTxMaxGasLimit',
+ type: 'uint256',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'newPriorityTxMaxGasLimit',
+ type: 'uint256',
+ },
+ ],
+ name: 'NewPriorityTxMaxGasLimit',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'address',
+ name: 'oldTransactionFilterer',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'address',
+ name: 'newTransactionFilterer',
+ type: 'address',
+ },
+ ],
+ name: 'NewTransactionFilterer',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ components: [
+ {
+ components: [
+ {
+ internalType: 'address',
+ name: 'facet',
+ type: 'address',
+ },
+ {
+ internalType: 'enum Diamond.Action',
+ name: 'action',
+ type: 'uint8',
+ },
+ {
+ internalType: 'bool',
+ name: 'isFreezable',
+ type: 'bool',
+ },
+ {
+ internalType: 'bytes4[]',
+ name: 'selectors',
+ type: 'bytes4[]',
+ },
+ ],
+ internalType: 'struct Diamond.FacetCut[]',
+ name: 'facetCuts',
+ type: 'tuple[]',
+ },
+ {
+ internalType: 'address',
+ name: 'initAddress',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes',
+ name: 'initCalldata',
+ type: 'bytes',
+ },
+ ],
+ indexed: false,
+ internalType: 'struct Diamond.DiamondCutData',
+ name: 'diamondCut',
+ type: 'tuple',
+ },
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'proposalId',
+ type: 'uint256',
+ },
+ {
+ indexed: false,
+ internalType: 'bytes32',
+ name: 'proposalSalt',
+ type: 'bytes32',
+ },
+ ],
+ name: 'ProposeTransparentUpgrade',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [],
+ name: 'Unfreeze',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'validatorAddress',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'bool',
+ name: 'isActive',
+ type: 'bool',
+ },
+ ],
+ name: 'ValidatorStatusUpdate',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'enum PubdataPricingMode',
+ name: 'validiumMode',
+ type: 'uint8',
+ },
+ ],
+ name: 'ValidiumModeStatusUpdate',
+ type: 'event',
+ },
+ {
+ inputs: [],
+ name: 'acceptAdmin',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'baseTokenGasPriceMultiplierDenominator',
+ outputs: [
+ {
+ internalType: 'uint128',
+ name: '',
+ type: 'uint128',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'baseTokenGasPriceMultiplierNominator',
+ outputs: [
+ {
+ internalType: 'uint128',
+ name: '',
+ type: 'uint128',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ components: [
+ {
+ internalType: 'address',
+ name: 'sender',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: 'contractL2',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: 'mintValue',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'l2Value',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes',
+ name: 'l2Calldata',
+ type: 'bytes',
+ },
+ {
+ internalType: 'uint256',
+ name: 'l2GasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: 'l2GasPerPubdataByteLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes[]',
+ name: 'factoryDeps',
+ type: 'bytes[]',
+ },
+ {
+ internalType: 'address',
+ name: 'refundRecipient',
+ type: 'address',
+ },
+ ],
+ internalType: 'struct BridgehubL2TransactionRequest',
+ name: '_request',
+ type: 'tuple',
+ },
+ ],
+ name: 'bridgehubRequestL2Transaction',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: 'canonicalTxHash',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ components: [
+ {
+ internalType: 'enum PubdataPricingMode',
+ name: 'pubdataPricingMode',
+ type: 'uint8',
+ },
+ {
+ internalType: 'uint32',
+ name: 'batchOverheadL1Gas',
+ type: 'uint32',
+ },
+ {
+ internalType: 'uint32',
+ name: 'maxPubdataPerBatch',
+ type: 'uint32',
+ },
+ {
+ internalType: 'uint32',
+ name: 'maxL2GasPerBatch',
+ type: 'uint32',
+ },
+ {
+ internalType: 'uint32',
+ name: 'priorityTxMaxPubdata',
+ type: 'uint32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'minimalL2GasPrice',
+ type: 'uint64',
+ },
+ ],
+ internalType: 'struct FeeParams',
+ name: '_newFeeParams',
+ type: 'tuple',
+ },
+ ],
+ name: 'changeFeeParams',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ components: [
+ {
+ internalType: 'uint64',
+ name: 'batchNumber',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'indexRepeatedStorageChanges',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint256',
+ name: 'numberOfLayer1Txs',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'priorityOperationsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'l2LogsTreeRoot',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: 'timestamp',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct IExecutor.StoredBatchInfo',
+ name: '_lastCommittedBatchData',
+ type: 'tuple',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint64',
+ name: 'batchNumber',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint64',
+ name: 'timestamp',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint64',
+ name: 'indexRepeatedStorageChanges',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'newStateRoot',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: 'numberOfLayer1Txs',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'priorityOperationsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'bootloaderHeapInitialContentsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'eventsQueueStateHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes',
+ name: 'systemLogs',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes',
+ name: 'pubdataCommitments',
+ type: 'bytes',
+ },
+ ],
+ internalType: 'struct IExecutor.CommitBatchInfo[]',
+ name: '_newBatchesData',
+ type: 'tuple[]',
+ },
+ ],
+ name: 'commitBatches',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint64',
+ name: 'batchNumber',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'indexRepeatedStorageChanges',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint256',
+ name: 'numberOfLayer1Txs',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'priorityOperationsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'l2LogsTreeRoot',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: 'timestamp',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct IExecutor.StoredBatchInfo',
+ name: '_lastCommittedBatchData',
+ type: 'tuple',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint64',
+ name: 'batchNumber',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint64',
+ name: 'timestamp',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint64',
+ name: 'indexRepeatedStorageChanges',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'newStateRoot',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: 'numberOfLayer1Txs',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'priorityOperationsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'bootloaderHeapInitialContentsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'eventsQueueStateHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes',
+ name: 'systemLogs',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes',
+ name: 'pubdataCommitments',
+ type: 'bytes',
+ },
+ ],
+ internalType: 'struct IExecutor.CommitBatchInfo[]',
+ name: '_newBatchesData',
+ type: 'tuple[]',
+ },
+ ],
+ name: 'commitBatchesSharedBridge',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ components: [
+ {
+ internalType: 'uint64',
+ name: 'batchNumber',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'indexRepeatedStorageChanges',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint256',
+ name: 'numberOfLayer1Txs',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'priorityOperationsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'l2LogsTreeRoot',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: 'timestamp',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct IExecutor.StoredBatchInfo[]',
+ name: '_batchesData',
+ type: 'tuple[]',
+ },
+ ],
+ name: 'executeBatches',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint64',
+ name: 'batchNumber',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'indexRepeatedStorageChanges',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint256',
+ name: 'numberOfLayer1Txs',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'priorityOperationsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'l2LogsTreeRoot',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: 'timestamp',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct IExecutor.StoredBatchInfo[]',
+ name: '_batchesData',
+ type: 'tuple[]',
+ },
+ ],
+ name: 'executeBatchesSharedBridge',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ components: [
+ {
+ components: [
+ {
+ internalType: 'address',
+ name: 'facet',
+ type: 'address',
+ },
+ {
+ internalType: 'enum Diamond.Action',
+ name: 'action',
+ type: 'uint8',
+ },
+ {
+ internalType: 'bool',
+ name: 'isFreezable',
+ type: 'bool',
+ },
+ {
+ internalType: 'bytes4[]',
+ name: 'selectors',
+ type: 'bytes4[]',
+ },
+ ],
+ internalType: 'struct Diamond.FacetCut[]',
+ name: 'facetCuts',
+ type: 'tuple[]',
+ },
+ {
+ internalType: 'address',
+ name: 'initAddress',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes',
+ name: 'initCalldata',
+ type: 'bytes',
+ },
+ ],
+ internalType: 'struct Diamond.DiamondCutData',
+ name: '_diamondCut',
+ type: 'tuple',
+ },
+ ],
+ name: 'executeUpgrade',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bytes4',
+ name: '_selector',
+ type: 'bytes4',
+ },
+ ],
+ name: 'facetAddress',
+ outputs: [
+ {
+ internalType: 'address',
+ name: 'facet',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'facetAddresses',
+ outputs: [
+ {
+ internalType: 'address[]',
+ name: 'facets',
+ type: 'address[]',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_facet',
+ type: 'address',
+ },
+ ],
+ name: 'facetFunctionSelectors',
+ outputs: [
+ {
+ internalType: 'bytes4[]',
+ name: '',
+ type: 'bytes4[]',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'facets',
+ outputs: [
+ {
+ components: [
+ {
+ internalType: 'address',
+ name: 'addr',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes4[]',
+ name: 'selectors',
+ type: 'bytes4[]',
+ },
+ ],
+ internalType: 'struct IGetters.Facet[]',
+ name: '',
+ type: 'tuple[]',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint16',
+ name: '_l2TxNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'bytes',
+ name: '_message',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_merkleProof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'finalizeEthWithdrawal',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'freezeDiamond',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getAdmin',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getBaseToken',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getBaseTokenBridge',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getBridgehub',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getFirstUnprocessedPriorityTx',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getL2BootloaderBytecodeHash',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: '',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getL2DefaultAccountBytecodeHash',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: '',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getL2SystemContractsUpgradeBatchNumber',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getL2SystemContractsUpgradeTxHash',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: '',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getName',
+ outputs: [
+ {
+ internalType: 'string',
+ name: '',
+ type: 'string',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getPendingAdmin',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getPriorityQueueSize',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getPriorityTxMaxGasLimit',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getProtocolVersion',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getPubdataPricingMode',
+ outputs: [
+ {
+ internalType: 'enum PubdataPricingMode',
+ name: '',
+ type: 'uint8',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getStateTransitionManager',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getTotalBatchesCommitted',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getTotalBatchesExecuted',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getTotalBatchesVerified',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getTotalPriorityTxs',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getVerifier',
+ outputs: [
+ {
+ internalType: 'address',
+ name: '',
+ type: 'address',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'getVerifierParams',
+ outputs: [
+ {
+ components: [
+ {
+ internalType: 'bytes32',
+ name: 'recursionNodeLevelVkHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'recursionLeafLevelVkHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'recursionCircuitsSetVksHash',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct VerifierParams',
+ name: '',
+ type: 'tuple',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'isDiamondStorageFrozen',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ ],
+ name: 'isEthWithdrawalFinalized',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_facet',
+ type: 'address',
+ },
+ ],
+ name: 'isFacetFreezable',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: 'isFreezable',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bytes4',
+ name: '_selector',
+ type: 'bytes4',
+ },
+ ],
+ name: 'isFunctionFreezable',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_address',
+ type: 'address',
+ },
+ ],
+ name: 'isValidator',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_batchNumber',
+ type: 'uint256',
+ },
+ ],
+ name: 'l2LogsRootHash',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: 'merkleRoot',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_gasPrice',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2GasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2GasPerPubdataByteLimit',
+ type: 'uint256',
+ },
+ ],
+ name: 'l2TransactionBaseCost',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'priorityQueueFrontOperation',
+ outputs: [
+ {
+ components: [
+ {
+ internalType: 'bytes32',
+ name: 'canonicalTxHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'expirationTimestamp',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint192',
+ name: 'layer2Tip',
+ type: 'uint192',
+ },
+ ],
+ internalType: 'struct PriorityOperation',
+ name: '',
+ type: 'tuple',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ components: [
+ {
+ internalType: 'uint64',
+ name: 'batchNumber',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'indexRepeatedStorageChanges',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint256',
+ name: 'numberOfLayer1Txs',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'priorityOperationsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'l2LogsTreeRoot',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: 'timestamp',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct IExecutor.StoredBatchInfo',
+ name: '_prevBatch',
+ type: 'tuple',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint64',
+ name: 'batchNumber',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'indexRepeatedStorageChanges',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint256',
+ name: 'numberOfLayer1Txs',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'priorityOperationsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'l2LogsTreeRoot',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: 'timestamp',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct IExecutor.StoredBatchInfo[]',
+ name: '_committedBatches',
+ type: 'tuple[]',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint256[]',
+ name: 'recursiveAggregationInput',
+ type: 'uint256[]',
+ },
+ {
+ internalType: 'uint256[]',
+ name: 'serializedProof',
+ type: 'uint256[]',
+ },
+ ],
+ internalType: 'struct IExecutor.ProofInput',
+ name: '_proof',
+ type: 'tuple',
+ },
+ ],
+ name: 'proveBatches',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint64',
+ name: 'batchNumber',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'indexRepeatedStorageChanges',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint256',
+ name: 'numberOfLayer1Txs',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'priorityOperationsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'l2LogsTreeRoot',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: 'timestamp',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct IExecutor.StoredBatchInfo',
+ name: '_prevBatch',
+ type: 'tuple',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint64',
+ name: 'batchNumber',
+ type: 'uint64',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'batchHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint64',
+ name: 'indexRepeatedStorageChanges',
+ type: 'uint64',
+ },
+ {
+ internalType: 'uint256',
+ name: 'numberOfLayer1Txs',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'priorityOperationsHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'l2LogsTreeRoot',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: 'timestamp',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'commitment',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct IExecutor.StoredBatchInfo[]',
+ name: '_committedBatches',
+ type: 'tuple[]',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint256[]',
+ name: 'recursiveAggregationInput',
+ type: 'uint256[]',
+ },
+ {
+ internalType: 'uint256[]',
+ name: 'serializedProof',
+ type: 'uint256[]',
+ },
+ ],
+ internalType: 'struct IExecutor.ProofInput',
+ name: '_proof',
+ type: 'tuple',
+ },
+ ],
+ name: 'proveBatchesSharedBridge',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bytes32',
+ name: '_l2TxHash',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2BatchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2MessageIndex',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint16',
+ name: '_l2TxNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_merkleProof',
+ type: 'bytes32[]',
+ },
+ {
+ internalType: 'enum TxStatus',
+ name: '_status',
+ type: 'uint8',
+ },
+ ],
+ name: 'proveL1ToL2TransactionStatus',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_batchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_index',
+ type: 'uint256',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint8',
+ name: 'l2ShardId',
+ type: 'uint8',
+ },
+ {
+ internalType: 'bool',
+ name: 'isService',
+ type: 'bool',
+ },
+ {
+ internalType: 'uint16',
+ name: 'txNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'address',
+ name: 'sender',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'key',
+ type: 'bytes32',
+ },
+ {
+ internalType: 'bytes32',
+ name: 'value',
+ type: 'bytes32',
+ },
+ ],
+ internalType: 'struct L2Log',
+ name: '_log',
+ type: 'tuple',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_proof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'proveL2LogInclusion',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_batchNumber',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_index',
+ type: 'uint256',
+ },
+ {
+ components: [
+ {
+ internalType: 'uint16',
+ name: 'txNumberInBatch',
+ type: 'uint16',
+ },
+ {
+ internalType: 'address',
+ name: 'sender',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes',
+ name: 'data',
+ type: 'bytes',
+ },
+ ],
+ internalType: 'struct L2Message',
+ name: '_message',
+ type: 'tuple',
+ },
+ {
+ internalType: 'bytes32[]',
+ name: '_proof',
+ type: 'bytes32[]',
+ },
+ ],
+ name: 'proveL2MessageInclusion',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_contractL2',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2Value',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes',
+ name: '_calldata',
+ type: 'bytes',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2GasLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_l2GasPerPubdataByteLimit',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes[]',
+ name: '_factoryDeps',
+ type: 'bytes[]',
+ },
+ {
+ internalType: 'address',
+ name: '_refundRecipient',
+ type: 'address',
+ },
+ ],
+ name: 'requestL2Transaction',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: 'canonicalTxHash',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_newLastBatch',
+ type: 'uint256',
+ },
+ ],
+ name: 'revertBatches',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_chainId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'uint256',
+ name: '_newLastBatch',
+ type: 'uint256',
+ },
+ ],
+ name: 'revertBatchesSharedBridge',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_newPendingAdmin',
+ type: 'address',
+ },
+ ],
+ name: 'setPendingAdmin',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'bool',
+ name: '_zkPorterIsAvailable',
+ type: 'bool',
+ },
+ ],
+ name: 'setPorterAvailability',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_newPriorityTxMaxGasLimit',
+ type: 'uint256',
+ },
+ ],
+ name: 'setPriorityTxMaxGasLimit',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint128',
+ name: '_nominator',
+ type: 'uint128',
+ },
+ {
+ internalType: 'uint128',
+ name: '_denominator',
+ type: 'uint128',
+ },
+ ],
+ name: 'setTokenMultiplier',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_transactionFilterer',
+ type: 'address',
+ },
+ ],
+ name: 'setTransactionFilterer',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_validator',
+ type: 'address',
+ },
+ {
+ internalType: 'bool',
+ name: '_active',
+ type: 'bool',
+ },
+ ],
+ name: 'setValidator',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'enum PubdataPricingMode',
+ name: '_validiumMode',
+ type: 'uint8',
+ },
+ ],
+ name: 'setValidiumMode',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_batchNumber',
+ type: 'uint256',
+ },
+ ],
+ name: 'storedBatchHash',
+ outputs: [
+ {
+ internalType: 'bytes32',
+ name: '',
+ type: 'bytes32',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'transferEthToSharedBridge',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'unfreezeDiamond',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_protocolVersion',
+ type: 'uint256',
+ },
+ {
+ components: [
+ {
+ components: [
+ {
+ internalType: 'address',
+ name: 'facet',
+ type: 'address',
+ },
+ {
+ internalType: 'enum Diamond.Action',
+ name: 'action',
+ type: 'uint8',
+ },
+ {
+ internalType: 'bool',
+ name: 'isFreezable',
+ type: 'bool',
+ },
+ {
+ internalType: 'bytes4[]',
+ name: 'selectors',
+ type: 'bytes4[]',
+ },
+ ],
+ internalType: 'struct Diamond.FacetCut[]',
+ name: 'facetCuts',
+ type: 'tuple[]',
+ },
+ {
+ internalType: 'address',
+ name: 'initAddress',
+ type: 'address',
+ },
+ {
+ internalType: 'bytes',
+ name: 'initCalldata',
+ type: 'bytes',
+ },
+ ],
+ internalType: 'struct Diamond.DiamondCutData',
+ name: '_cutData',
+ type: 'tuple',
+ },
+ ],
+ name: 'upgradeChainFromVersion',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+] as const;
diff --git a/src/plugin.ts b/src/plugin.ts
index 606d44f..d3cb9e7 100644
--- a/src/plugin.ts
+++ b/src/plugin.ts
@@ -1,10 +1,10 @@
import type { Address } from 'web3';
import { Web3PluginBase, Contract } from 'web3';
import type { Web3RequestManager } from 'web3-core';
-import { ERC20TokenAbi } from './contracts/ERC20Token';
+import { IERC20ABI } from './contracts/IERC20';
import { RpcMethods } from './rpc.methods';
import { ETH_ADDRESS, ZERO_ADDRESS } from './constants';
-import { L2BridgeAbi } from './contracts/L2Bridge';
+import { IL2BridgeABI } from './contracts/IL2Bridge';
export class ZkSyncPlugin extends Web3PluginBase {
public pluginNamespace = 'zkSync';
@@ -13,8 +13,8 @@ export class ZkSyncPlugin extends Web3PluginBase {
public wethBridgeL1: string;
public wethBridgeL2: string;
public _rpc?: RpcMethods;
- public _l2BridgeContracts: Record
>;
- public _erc20Contracts: Record>;
+ public _l2BridgeContracts: Record>;
+ public _erc20Contracts: Record>;
constructor() {
super();
@@ -32,9 +32,7 @@ export class ZkSyncPlugin extends Web3PluginBase {
*/
get rpc(): RpcMethods {
if (!this._rpc) {
- this._rpc = new RpcMethods(
- this.requestManager as unknown as Web3RequestManager,
- );
+ this._rpc = new RpcMethods(this.requestManager as unknown as Web3RequestManager);
}
return this._rpc;
}
@@ -43,9 +41,9 @@ export class ZkSyncPlugin extends Web3PluginBase {
* Get L2 bridge contract instance
* @param address - Contract address
*/
- getL2BridgeContract(address: Address): Contract {
+ getL2BridgeContract(address: Address): Contract {
if (!this._l2BridgeContracts[address]) {
- this._l2BridgeContracts[address] = new Contract(L2BridgeAbi, address);
+ this._l2BridgeContracts[address] = new Contract(IL2BridgeABI, address);
this._l2BridgeContracts[address].link(this);
}
return this._l2BridgeContracts[address];
@@ -55,9 +53,9 @@ export class ZkSyncPlugin extends Web3PluginBase {
* Get the ERC20 contract instance
* @param address - Contract address
*/
- erc20(address: string): Contract {
+ erc20(address: string): Contract {
if (!this._erc20Contracts[address]) {
- this._erc20Contracts[address] = new Contract(ERC20TokenAbi, address);
+ this._erc20Contracts[address] = new Contract(IERC20ABI, address);
this._erc20Contracts[address].link(this);
}
return this._erc20Contracts[address];
@@ -104,9 +102,7 @@ export class ZkSyncPlugin extends Web3PluginBase {
return l1Token;
}
} catch (e) {
- throw new Error(
- `Error getting L1 address for token ${token}. ${JSON.stringify(e)}`,
- );
+ throw new Error(`Error getting L1 address for token ${token}. ${JSON.stringify(e)}`);
}
}
@@ -132,9 +128,7 @@ export class ZkSyncPlugin extends Web3PluginBase {
return l2WethToken;
}
} catch (e) {
- throw new Error(
- `Error getting L2 address for token ${token}. ${JSON.stringify(e)}`,
- );
+ throw new Error(`Error getting L2 address for token ${token}. ${JSON.stringify(e)}`);
}
}
diff --git a/src/types.ts b/src/types.ts
index 2ea8086..0eee7e4 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,22 +1,235 @@
-import type { Address, Bytes, HexString, Numbers } from 'web3';
+export type { Bytes, HexString, Numbers } from 'web3';
+import { Bytes, HexString, Numbers } from 'web3-types';
-export interface BatchDetails {
- number: number;
- timestamp: number;
- l1TxCount: number;
- l2TxCount: number;
- rootHash?: string;
- status: string;
- commitTxHash?: string;
- committedAt?: Date;
- proveTxHash?: string;
- provenAt?: Date;
- executeTxHash?: string;
- executedAt?: Date;
- l1GasPrice: number;
- l2FairGasPrice: number;
+import { EIP1193Provider } from 'web3';
+
+import { FeeMarketEIP1559TxData } from 'web3-eth-accounts';
+
+import { RpcMethods } from './rpc.methods';
+
+/** 0x-prefixed, hex encoded, ethereum account address. */
+export type Address = string;
+
+/** 0x-prefixed, hex encoded, ECDSA signature. */
+export type Signature = string;
+
+/** Ethereum network. */
+export enum Network {
+ Mainnet = 1,
+ Ropsten = 3,
+ Rinkeby = 4,
+ Goerli = 5,
+ Sepolia = 6,
+ Localhost = 9,
+ EraTestNode = 10,
+}
+
+/** Enumerated list of priority queue types. */
+export enum PriorityQueueType {
+ Deque = 0,
+ HeapBuffer = 1,
+ Heap = 2,
+}
+
+/** Enumerated list of priority operation tree types. */
+export enum PriorityOpTree {
+ Full = 0,
+ Rollup = 1,
+}
+
+/** Enumerated list of transaction status types. */
+export enum TransactionStatus {
+ /** Transaction not found. */
+ NotFound = 'not-found',
+ /** Transaction is processing. */
+ Processing = 'processing',
+ /** Transaction has been committed. */
+ Committed = 'committed',
+ /** Transaction has been finalized. */
+ Finalized = 'finalized',
+}
+
+/** Type defining a paymaster by its address and the bytestream input. */
+export type PaymasterParams = {
+ /** The address of the paymaster. */
+ paymaster: Address;
+ /** The bytestream input for the paymaster. */
+ paymasterInput: Bytes;
+};
+
+/** Contains EIP712 transaction metadata. */
+export type Eip712Meta = {
+ /** The maximum amount of gas the user is willing to pay for a single byte of pubdata. */
+ gasPerPubdata?: Numbers;
+ /** An array of bytes containing the bytecode of the contract being deployed and any related contracts it can deploy. */
+ factoryDeps?: Bytes[];
+ /** Custom signature used for cases where the signer's account is not an EOA. */
+ customSignature?: Bytes;
+ /** Parameters for configuring the custom paymaster for the transaction. */
+ paymasterParams?: PaymasterParams;
+};
+
+/**
+ * Specifies a specific block. This can be represented by:
+ * - A numeric value (number, bigint, or hexadecimal string) representing the block height, where the genesis block is block 0.
+ * A negative value indicates the block number should be deducted from the most recent block.
+ * - A block hash as a string, specifying a specific block by its block hash.
+ * This allows potentially orphaned blocks to be specified without ambiguity, but many backends do not support this for some operations.
+ * - Constants representing special blocks such as 'committed', 'finalized', 'latest', 'earliest', or 'pending'.
+ */
+export type BlockTag =
+ | Numbers
+ | string // block hash
+ | 'committed'
+ | 'finalized'
+ | 'latest'
+ | 'earliest'
+ | 'pending';
+
+/** Pipe-delimited choice of deployment types. */
+export type DeploymentType = 'create' | 'createAccount' | 'create2' | 'create2Account';
+
+/** Bridged token. */
+export interface Token {
+ l1Address: Address;
+ l2Address: Address;
+ name: string;
+ symbol: string;
+ decimals: number;
+}
+
+/** Represents the transaction fee parameters. */
+export interface Fee {
+ /** The maximum amount of gas allowed for the transaction. */
+ gasLimit: bigint;
+ /** The maximum amount of gas the user is willing to pay for a single byte of pubdata. */
+ gasPerPubdataLimit: bigint;
+ /** The EIP1559 tip per gas. */
+ maxPriorityFeePerGas: bigint;
+ /** The EIP1559 fee cap per gas. */
+ maxFeePerGas: bigint;
+}
+
+/** Represents a message proof. */
+export interface MessageProof {
+ id: number;
+ proof: string[];
+ root: string;
+}
+
+export interface zkSyncTxData extends FeeMarketEIP1559TxData {
+ /** The batch number on the L1 network. */
+ readonly l1BatchNumber: null | number;
+ /** The transaction index within the batch on the L1 network. */
+ readonly l1BatchTxIndex: null | number;
+}
+
+/**
+ * Represents a L2 to L1 transaction log.
+ */
+export interface L2ToL1Log {
+ blockNumber: number;
+ blockHash: string;
+ l1BatchNumber: number;
+ transactionIndex: number;
+ shardId: number;
+ isService: boolean;
+ sender: string;
+ key: string;
+ value: string;
+ transactionHash: string;
+ logIndex: number;
+}
+
+/** A map containing accounts and their balances. */
+export type BalancesMap = { [key: string]: bigint };
+
+/** Represents deployment information. */
+export interface DeploymentInfo {
+ /** The account responsible for deployment. */
+ sender: Address;
+ /** The hash of the contract/account bytecode. */
+ bytecodeHash: string;
+ /** The deployed address of the contract/address. */
+ deployedAddress: Address;
+}
+
+/**
+ * Represents the input data structure for an approval-based paymaster.
+ */
+export interface ApprovalBasedPaymasterInput {
+ /** The type of the paymaster input. */
+ type: 'ApprovalBased';
+ /** The address of the token to be approved. */
+ token: Address;
+ /** The minimum allowance required for the token approval. */
+ minimalAllowance: Numbers;
+ /** The additional input data. */
+ innerInput: Bytes;
+}
+
+/**
+ * Represents the input data structure for a general paymaster.
+ */
+export interface GeneralPaymasterInput {
+ /** The type of the paymaster input. */
+ type: 'General';
+ /** The additional input data. */
+ innerInput: Bytes;
+}
+
+/**
+ * Represents an Ethereum signature consisting of the components `v`, `r`, and `s`.
+ */
+export interface EthereumSignature {
+ /** The recovery id. */
+ v: number;
+ /** The "r" value of the signature. */
+ r: Bytes;
+ /** The "s" value of the signature. */
+ s: Bytes;
+}
+
+/**
+ * Represents the input data structure for a paymaster.
+ * It can be either approval-based or general.
+ */
+export type PaymasterInput = ApprovalBasedPaymasterInput | GeneralPaymasterInput;
+
+/** Enumerated list of account abstraction versions. */
+export enum AccountAbstractionVersion {
+ /** Used for contracts that are not accounts */
+ None = 0,
+ /** Used for contracts that are accounts */
+ Version1 = 1,
}
+/**
+ * Enumerated list of account nonce ordering formats.
+ */
+export enum AccountNonceOrdering {
+ /**
+ * Nonces should be ordered in the same way as in externally owned accounts (EOAs).
+ * This means, for instance, that the operator will always wait for a transaction with nonce `X`
+ * before processing a transaction with nonce `X+1`.
+ */
+ Sequential = 0,
+ /** Nonces can be ordered in arbitrary order. */
+ Arbitrary = 1,
+}
+
+/**
+ * Interface representing contract account information containing details on the supported account abstraction version
+ * and nonce ordering format.
+ */
+export interface ContractAccountInfo {
+ /** The supported account abstraction version. */
+ supportedAAVersion: AccountAbstractionVersion;
+ /** The nonce ordering format. */
+ nonceOrdering: AccountNonceOrdering;
+}
+
+/** Contains batch information. */
export interface BlockDetails {
number: bigint;
timestamp: bigint;
@@ -33,6 +246,7 @@ export interface BlockDetails {
executedAt?: Date;
}
+/** Contains transaction details information. */
export interface TransactionDetails {
isL1Originated: boolean;
status: string;
@@ -44,6 +258,23 @@ export interface TransactionDetails {
ethExecuteTxHash?: string;
}
+/** Represents the full deposit fee containing fees for both L1 and L2 transactions. */
+export interface FullDepositFee {
+ /** The maximum fee per gas for L1 transaction. */
+ maxFeePerGas?: BigInt;
+ /** The maximum priority fee per gas for L1 transaction. */
+ maxPriorityFeePerGas?: BigInt;
+ /** The gas price for L2 transaction. */
+ gasPrice?: BigInt;
+ /** The base cost of the deposit transaction on L2. */
+ baseCost: BigInt;
+ /** The gas limit for L1 transaction. */
+ l1GasLimit: BigInt;
+ /** The gas limit for L2 transaction. */
+ l2GasLimit: BigInt;
+}
+
+/** Represents a raw block transaction. */
export interface RawBlockTransaction {
common_data: {
L2: {
@@ -77,6 +308,41 @@ export interface RawBlockTransaction {
raw_bytes: string;
}
+/** Contains parameters for finalizing the withdrawal transaction. */
+export interface FinalizeWithdrawalParams {
+ l1BatchNumber: number | null;
+ l2MessageIndex: number;
+ l2TxNumberInBlock: number | null;
+ message: any;
+ sender: string;
+ proof: string[];
+}
+
+/** Represents storage proof */
+export interface StorageProof {
+ address: Address;
+ storageProof: {
+ index: Numbers;
+ key: HexString;
+ value: HexString;
+ proof: HexString[];
+ }[];
+}
+
+/**
+ * Signs various types of payloads, optionally using a some kind of secret.
+ *
+ * @param payload The payload that needs to be sign already populated transaction to sign.
+ * @param [secret] The secret used for signing the `payload`.
+ * @param [provider] The provider is used to fetch data from the network if it is required for signing.
+ * @returns A promise that resolves to the serialized signature in hexadecimal format.
+ */
+export type PayloadSigner = (
+ payload: Bytes,
+ secret?: any,
+ provider?: null | EIP1193Provider,
+) => Promise;
+
export interface WalletBalances {
[key: Address]: Numbers;
}
@@ -101,16 +367,6 @@ export interface L2ToL1Proof {
root: HexString;
}
-export interface Proof {
- address: Address;
- storageProof: {
- index: Numbers;
- key: HexString;
- value: HexString;
- proof: HexString[];
- }[];
-}
-
export interface EstimateFee {
gas_limit: Numbers;
gas_per_pubdata_limit: Numbers;
diff --git a/test/fixtures.ts b/test/fixtures.ts
index f29387c..265d79a 100644
--- a/test/fixtures.ts
+++ b/test/fixtures.ts
@@ -1,5 +1,5 @@
import type { Address, HexString } from 'web3';
-import type { Proof } from '../src/types';
+import type { StorageProof } from '../src/types';
export const getRawBlockTransactionsData = {
input: 251491,
@@ -16,52 +16,46 @@ export const getRawBlockTransactionsData = {
},
initiatorAddress: '0x202bd724d72fd5a169c8930203e1b60870e4df95',
signature: [
- 137, 115, 161, 220, 2, 48, 185, 157, 125, 236, 198, 85, 99, 212, 128, 24,
- 126, 171, 22, 34, 146, 36, 193, 208, 83, 3, 134, 11, 74, 38, 89, 252, 37,
- 222, 4, 59, 169, 237, 144, 64, 12, 82, 61, 251, 40, 85, 42, 89, 21, 199, 71,
- 128, 151, 231, 166, 230, 60, 14, 17, 59, 67, 118, 175, 216, 28,
+ 137, 115, 161, 220, 2, 48, 185, 157, 125, 236, 198, 85, 99, 212, 128, 24, 126, 171, 22,
+ 34, 146, 36, 193, 208, 83, 3, 134, 11, 74, 38, 89, 252, 37, 222, 4, 59, 169, 237, 144,
+ 64, 12, 82, 61, 251, 40, 85, 42, 89, 21, 199, 71, 128, 151, 231, 166, 230, 60, 14, 17,
+ 59, 67, 118, 175, 216, 28,
],
transactionType: 'LegacyTransaction',
input: {
hash: '0x16d5e37b848eed8b33d927b7c3b9d974cc48e164af5f7a6d172334a1330a5e76',
data: [
- 249, 2, 206, 130, 104, 112, 132, 59, 154, 202, 0, 131, 61, 9, 0, 148,
- 187, 92, 48, 154, 58, 147, 71, 192, 19, 91, 147, 203, 213, 61, 57, 74,
- 168, 67, 69, 229, 128, 185, 2, 100, 201, 128, 117, 57, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 253, 105, 228, 93, 111, 81, 228, 130, 172, 79, 143, 46, 20, 242, 21, 82,
- 0, 0, 93, 139, 6, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 197, 134, 51, 192, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 197,
- 134, 51, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 56, 197, 134, 51, 192, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 197, 134,
- 51, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 221, 91, 114, 141, 15, 115, 53, 225,
- 115, 233, 223, 14, 194, 138, 27, 152, 228, 159, 84, 144, 67, 246, 163,
- 74, 17, 27, 161, 247, 5, 126, 30, 1, 243, 98, 134, 218, 191, 139, 161,
- 27, 167, 121, 27, 171, 231, 94, 248, 206, 73, 195, 156, 142, 246, 164,
- 198, 205, 78, 67, 188, 193, 147, 210, 209, 46, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
- 1, 56, 204, 92, 213, 109, 169, 231, 44, 75, 130, 19, 30, 79, 248, 50,
- 78, 119, 48, 161, 206, 119, 24, 74, 4, 190, 125, 90, 23, 101, 13, 83,
- 24, 231, 138, 70, 132, 57, 11, 124, 83, 101, 7, 232, 64, 136, 14, 187,
- 52, 224, 220, 104, 8, 215, 48, 44, 207, 203, 144, 38, 231, 116, 87, 208,
- 130, 2, 124, 160, 137, 115, 161, 220, 2, 48, 185, 157, 125, 236, 198,
- 85, 99, 212, 128, 24, 126, 171, 22, 34, 146, 36, 193, 208, 83, 3, 134,
- 11, 74, 38, 89, 252, 160, 37, 222, 4, 59, 169, 237, 144, 64, 12, 82, 61,
- 251, 40, 85, 42, 89, 21, 199, 71, 128, 151, 231, 166, 230, 60, 14, 17,
- 59, 67, 118, 175, 216,
+ 249, 2, 206, 130, 104, 112, 132, 59, 154, 202, 0, 131, 61, 9, 0, 148, 187, 92, 48,
+ 154, 58, 147, 71, 192, 19, 91, 147, 203, 213, 61, 57, 74, 168, 67, 69, 229, 128, 185,
+ 2, 100, 201, 128, 117, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 253, 105, 228, 93, 111, 81, 228, 130, 172, 79, 143, 46, 20,
+ 242, 21, 82, 0, 0, 93, 139, 6, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 197, 134, 51, 192, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 197,
+ 134, 51, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 56, 197, 134, 51, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 197, 134, 51, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 221, 91, 114, 141, 15,
+ 115, 53, 225, 115, 233, 223, 14, 194, 138, 27, 152, 228, 159, 84, 144, 67, 246, 163,
+ 74, 17, 27, 161, 247, 5, 126, 30, 1, 243, 98, 134, 218, 191, 139, 161, 27, 167, 121,
+ 27, 171, 231, 94, 248, 206, 73, 195, 156, 142, 246, 164, 198, 205, 78, 67, 188, 193,
+ 147, 210, 209, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 56, 204, 92, 213, 109, 169, 231, 44, 75, 130, 19, 30,
+ 79, 248, 50, 78, 119, 48, 161, 206, 119, 24, 74, 4, 190, 125, 90, 23, 101, 13, 83, 24,
+ 231, 138, 70, 132, 57, 11, 124, 83, 101, 7, 232, 64, 136, 14, 187, 52, 224, 220, 104,
+ 8, 215, 48, 44, 207, 203, 144, 38, 231, 116, 87, 208, 130, 2, 124, 160, 137, 115, 161,
+ 220, 2, 48, 185, 157, 125, 236, 198, 85, 99, 212, 128, 24, 126, 171, 22, 34, 146, 36,
+ 193, 208, 83, 3, 134, 11, 74, 38, 89, 252, 160, 37, 222, 4, 59, 169, 237, 144, 64, 12,
+ 82, 61, 251, 40, 85, 42, 89, 21, 199, 71, 128, 151, 231, 166, 230, 60, 14, 17, 59, 67,
+ 118, 175, 216,
],
},
paymasterParams: {
@@ -142,7 +136,10 @@ export const getL2ToL1LogProofData = {
root: '0x920c63cb0066a08da45f0a9bf934517141bd72d8e5a51421a94b517bf49a0d39',
},
};
-export const getProofData: { input: [Address, [HexString], number]; output: Proof } = {
+export const getProofData: {
+ input: [Address, [HexString], number];
+ output: StorageProof;
+} = {
input: [
'0x0000000000000000000000000000000000008003',
['0x8b65c0cf1012ea9f393197eb24619fd814379b298b238285649e14f936a5eb12'],
From 9279e2ebbaf33f973a4a64841a8821d86974d7dc Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Sat, 25 May 2024 20:09:35 +0200
Subject: [PATCH 02/30] add .history to .gitignore
---
.gitignore | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index 9b26ed0..0d57b0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
node_modules
-lib
\ No newline at end of file
+lib
+.history
From 758df6f2cf324a7c8e17730672f2ce0a831d407b Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Mon, 27 May 2024 15:27:26 +0200
Subject: [PATCH 03/30] add some util functions
---
package.json | 6 +-
src/plugin.ts | 2 +-
src/rpc.methods.ts | 34 +-
src/types.ts | 468 +++++++++++++++-
src/utils.ts | 1269 ++++++++++++++++++++++++++++++++++++++++++++
yarn.lock | 79 +++
6 files changed, 1837 insertions(+), 21 deletions(-)
create mode 100644 src/utils.ts
diff --git a/package.json b/package.json
index c7abdff..2c31b7c 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,11 @@
},
"dependencies": {
"hardhat": "^2.19.4",
- "web3-utils": "^4.1.1"
+ "web3-core": "^4.4.0",
+ "web3-eth-abi": "^4.2.2",
+ "web3-eth-accounts": "^4.1.2",
+ "web3-types": "^1.6.0",
+ "web3-utils": "^4.3.0"
},
"devDependencies": {
"@chainsafe/eslint-config": "^2.1.1",
diff --git a/src/plugin.ts b/src/plugin.ts
index d3cb9e7..fc922e8 100644
--- a/src/plugin.ts
+++ b/src/plugin.ts
@@ -140,7 +140,7 @@ export class ZkSyncPlugin extends Web3PluginBase {
// Module Augmentation
declare module 'web3' {
- interface Web3Context {
+ interface Web3 {
zkSync: ZkSyncPlugin;
}
}
diff --git a/src/rpc.methods.ts b/src/rpc.methods.ts
index 46f2c7d..53a1453 100644
--- a/src/rpc.methods.ts
+++ b/src/rpc.methods.ts
@@ -1,7 +1,16 @@
import type { Web3RequestManager } from 'web3-core';
import { format, toNumber } from 'web3-utils';
-import type { Address, Bytes, HexString32Bytes, Numbers, TransactionWithSenderAPI } from 'web3';
-import { DEFAULT_RETURN_FORMAT } from 'web3';
+import type {
+ Address,
+ Bytes,
+ HexString32Bytes,
+ Numbers,
+ TransactionWithSenderAPI,
+} from 'web3-types';
+import {
+ DEFAULT_RETURN_FORMAT,
+ // Web3BaseProvider
+} from 'web3';
import type { DataFormat } from 'web3-types/src/data_format_types';
import type {
BatchDetails,
@@ -9,7 +18,7 @@ import type {
BridgeAddresses,
EstimateFee,
L2ToL1Proof,
- Proof,
+ StorageProof,
RawBlockTransaction,
TransactionDetails,
WalletBalances,
@@ -32,6 +41,7 @@ import {
// The ZkSync methods described here https://docs.zksync.io/build/api.html
+// TODO: Think about inheritance from Web3Eth
export class RpcMethods {
requestManager: Web3RequestManager;
@@ -60,9 +70,7 @@ export class RpcMethods {
*
* @param returnFormat - The format of the return value.
*/
- public async getL1BatchNumber(
- returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
- ): Promise {
+ public async getL1BatchNumber(returnFormat: DataFormat = DEFAULT_RETURN_FORMAT): Promise {
return format(IntSchema, await this._send('zks_L1BatchNumber', []), returnFormat) as bigint;
}
@@ -227,9 +235,7 @@ export class RpcMethods {
*
* @param returnFormat - The format of the return value.
*/
- public async getMainContract(
- returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
- ): Promise {
+ public async getMainContract(returnFormat: DataFormat = DEFAULT_RETURN_FORMAT): Promise {
return format(
AddressSchema,
await this._send('zks_getMainContract', []),
@@ -271,13 +277,13 @@ export class RpcMethods {
keys: string[],
l1BatchNumber: Numbers,
returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
- ): Promise {
+ ): Promise {
const res = (await this._send('zks_getProof', [
address,
keys,
typeof l1BatchNumber === 'number' ? l1BatchNumber : Number(toNumber(l1BatchNumber)),
- ])) as Proof;
- const result = format(ProofSchema, res, returnFormat) as Proof;
+ ])) as StorageProof;
+ const result = format(ProofSchema, res, returnFormat) as StorageProof;
result.storageProof = [];
for (let i = 0; i < res.storageProof.length; i++) {
result.storageProof[i] = format(
@@ -325,9 +331,7 @@ export class RpcMethods {
const params: [HexString32Bytes, number?] = [txHash];
if (l2ToL1LogIndex) {
params.push(
- typeof l2ToL1LogIndex === 'number'
- ? l2ToL1LogIndex
- : Number(toNumber(l2ToL1LogIndex)),
+ typeof l2ToL1LogIndex === 'number' ? l2ToL1LogIndex : Number(toNumber(l2ToL1LogIndex)),
);
}
return format(
diff --git a/src/types.ts b/src/types.ts
index 0eee7e4..af647f1 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,11 +1,31 @@
-export type { Bytes, HexString, Numbers } from 'web3';
-import { Bytes, HexString, Numbers } from 'web3-types';
+export type { Bytes, HexString, Numbers } from 'web3-types';
-import { EIP1193Provider } from 'web3';
+// import { FMT_BYTES, FMT_NUMBER, TransactionReceipt, Web3Eth } from 'web3';
-import { FeeMarketEIP1559TxData } from 'web3-eth-accounts';
+// // TODO: // is it needed to be re-exported from web3
+// import { watchTransactionForConfirmations } from 'web3-eth/lib/types/utils/watch_transaction_for_confirmations.js';
+
+import { Bytes, HexString, Numbers, Transaction, EIP1193Provider } from 'web3-types';
+
+import {
+ // FeeMarketEIP1559Transaction,
+ FeeMarketEIP1559TxData,
+ // TxOptions
+} from 'web3-eth-accounts';
+
+// import {
+// EIP712_TX_TYPE,
+// parseEip712,
+// serializeEip712,
+// sleep,
+// eip712TxHash,
+// isAddressEq,
+// } from './utils';
import { RpcMethods } from './rpc.methods';
+export interface TransactionOverrides extends Omit {}
+
+export const ZeroAddress: Address = '0x0000000000000000000000000000000000000000';
/** 0x-prefixed, hex encoded, ethereum account address. */
export type Address = string;
@@ -124,6 +144,378 @@ export interface zkSyncTxData extends FeeMarketEIP1559TxData {
readonly l1BatchTxIndex: null | number;
}
+// /**
+// * A `TransactionResponse` is an extension of {@link TransactionResponse} with additional features for
+// * interacting with zkSync Era.
+// */
+// export class TransactionResponse extends FeeMarketEIP1559Transaction {
+// private web3Eth: Web3Eth;
+
+// /** The batch number on the L1 network. */
+// readonly l1BatchNumber: null | number;
+// /** The transaction index within the batch on the L1 network. */
+// readonly l1BatchTxIndex: null | number;
+
+// constructor(txData: zkSyncTxData, provider: EIP1193Provider, opts?: TxOptions) {
+// super(txData, opts);
+// this.web3Eth = new Web3Eth(provider);
+// this.l1BatchNumber = txData.l1BatchNumber;
+// this.l1BatchTxIndex = txData.l1BatchTxIndex;
+
+// // copied from old base ethers.TransactionResponse!
+// // this.blockNumber = tx.blockNumber != null ? tx.blockNumber : null;
+// // this.blockHash = tx.blockHash != null ? tx.blockHash : null;
+
+// // this.hash = tx.hash;
+// // this.index = tx.index;
+
+// // this.type = tx.type;
+
+// // this.from = tx.from;
+// // this.to = tx.to || null;
+
+// // this.gasLimit = tx.gasLimit;
+// // this.nonce = tx.nonce;
+// // this.data = tx.data;
+// // this.value = tx.value;
+
+// // this.gasPrice = tx.gasPrice;
+// // this.maxPriorityFeePerGas = tx.maxPriorityFeePerGas != null ? tx.maxPriorityFeePerGas : null;
+// // this.maxFeePerGas = tx.maxFeePerGas != null ? tx.maxFeePerGas : null;
+// // this.maxFeePerBlobGas = tx.maxFeePerBlobGas != null ? tx.maxFeePerBlobGas : null;
+
+// // this.chainId = tx.chainId;
+// // this.signature = tx.signature;
+
+// // this.accessList = tx.accessList != null ? tx.accessList : null;
+// // this.blobVersionedHashes = tx.blobVersionedHashes != null ? tx.blobVersionedHashes : null;
+
+// // this.#startBlock = -1;
+// }
+
+// /**
+// * Waits for this transaction to be mined and have a specified number of confirmation blocks.
+// * Resolves once the transaction has `confirmations` blocks including it.
+// * If `confirmations` is 0 and the transaction has not been mined, it resolves to `null`.
+// * Otherwise, it waits until enough confirmations have completed.
+// *
+// * @param confirmations The number of confirmation blocks. Defaults to 1.
+// * @returns A promise that resolves to the transaction receipt.
+// */
+// async wait(confirmations?: number): Promise {
+// // eslint-disable-next-line no-constant-condition
+// while (true) {
+// // // it needs to be replaced with something like:
+
+// // // const receipt = await this.web3Eth.getTransactionReceipt(this.hash);
+// // // eth.setConfig({ transactionConfirmationBlocks: waitConfirmations });
+// // // watchTransactionForConfirmations(this.web3Eth, , receipt, this.hash, 'hex');
+
+// // // Or far better: to be replaced with a wait for confirmations on the PromiEvent of the sent transaction
+
+// // const receipt = (await super.wait(confirmations)) as TransactionReceipt;
+
+// // if (receipt && receipt.blockNumber) {
+// // return receipt;
+// // }
+// await sleep(500);
+// }
+// }
+
+// async getTransaction() {
+// return await this.web3Eth.getTransaction(this.data, {
+// number: FMT_NUMBER.BIGINT,
+// bytes: FMT_BYTES.HEX,
+// });
+// }
+
+// // replaceableTransaction(startBlock: number): TransactionResponse {
+// // return new TransactionResponse(super.replaceableTransaction(startBlock), this.provider);
+// // }
+
+// // async getBlock(): Promise {
+// // return await base.getBlock(this.hash);
+// // }
+
+// // /** Waits for transaction to be finalized. */
+// // async waitFinalize(): Promise {
+// // // eslint-disable-next-line no-constant-condition
+// // while (true) {
+// // const receipt = await this.wait();
+// // if (receipt && receipt.blockNumber) {
+// // const block = await this.provider.getBlock('finalized');
+// // if (receipt.blockNumber <= block!.number) {
+// // return (await this.provider.getTransactionReceipt(receipt.hash)) as TransactionReceipt;
+// // }
+// // } else {
+// // await sleep(500);
+// // }
+// // }
+// // }
+
+// override toJSON(): any {
+// const { l1BatchNumber, l1BatchTxIndex } = this;
+
+// return {
+// ...super.toJSON(),
+// l1BatchNumber,
+// l1BatchTxIndex,
+// };
+// }
+// }
+
+// /**
+// * A `TransactionReceipt` is an extension of {@link ethers.TransactionReceipt} with additional features for
+// * interacting with zkSync Era.
+// */
+// export class TransactionReceipt extends FeeMarketEIP1559Transaction {
+// private web3Eth: Web3Eth;
+
+// /** The batch number on the L1 network. */
+// readonly l1BatchNumber: null | number;
+// /** The transaction index within the batch on the L1 network. */
+// readonly l1BatchTxIndex: null | number;
+// /** The logs of L2 to L1 messages. */
+// readonly l2ToL1Logs: L2ToL1Log[];
+// /** All logs included in the transaction receipt. */
+// readonly _logs: ReadonlyArray;
+
+// constructor(params: any, provider: EIP1193Provider) {
+// super(params);
+// this.web3Eth = new Web3Eth(provider);
+// this.l1BatchNumber = params.l1BatchNumber;
+// this.l1BatchTxIndex = params.l1BatchTxIndex;
+// this.l2ToL1Logs = params.l2ToL1Logs;
+// this._logs = Object.freeze(
+// params.logs.map((log: Log) => {
+// return new Log(log, provider);
+// }),
+// );
+// }
+
+// // override get logs(): ReadonlyArray {
+// // return this._logs;
+// // }
+
+// // override getBlock(): Promise {
+// // return super.getBlock(this.hash()) as Promise;
+// // }
+
+// // override getTransaction(): Promise {
+// // return super.getTransaction() as Promise;
+// // }
+
+// override toJSON(): any {
+// const { l1BatchNumber, l1BatchTxIndex, l2ToL1Logs } = this;
+// return {
+// ...super.toJSON(),
+// l1BatchNumber,
+// l1BatchTxIndex,
+// l2ToL1Logs,
+// };
+// }
+// }
+
+// /** A `Block` is an extension of {@link ethers.Block} with additional features for interacting with zkSync Era. */
+// export class Block extends ethers.Block {
+// /** The batch number on L1. */
+// readonly l1BatchNumber: null | number;
+// /** The timestamp of the batch on L1. */
+// readonly l1BatchTimestamp: null | number;
+
+// constructor(params: any, provider: ethers.Provider) {
+// super(params, provider);
+// this.l1BatchNumber = params.l1BatchNumber;
+// this.l1BatchTimestamp = params.l1BatchTxIndex;
+// }
+
+// override toJSON(): any {
+// const { l1BatchNumber, l1BatchTimestamp: l1BatchTxIndex } = this;
+// return {
+// ...super.toJSON(),
+// l1BatchNumber,
+// l1BatchTxIndex,
+// };
+// }
+
+// override get prefetchedTransactions(): TransactionResponse[] {
+// return super.prefetchedTransactions as TransactionResponse[];
+// }
+
+// override getTransaction(indexOrHash: number | string): Promise {
+// return super.getTransaction(indexOrHash) as Promise;
+// }
+// }
+
+// /** A `LogParams` is an extension of {@link ethers.LogParams} with additional features for interacting with zkSync Era. */
+// export interface LogParams extends ethers.LogParams {
+// /** The batch number on L1. */
+// readonly l1BatchNumber: null | number;
+// }
+
+// /** A `Log` is an extension of {@link ethers.Log} with additional features for interacting with zkSync Era. */
+// export class Log extends ethers.Log {
+// /** The batch number on L1. */
+// readonly l1BatchNumber: null | number;
+
+// constructor(params: LogParams, provider: ethers.Provider) {
+// super(params, provider);
+// this.l1BatchNumber = params.l1BatchNumber;
+// }
+
+// override toJSON(): any {
+// const { l1BatchNumber } = this;
+// return {
+// ...super.toJSON(),
+// l1BatchNumber,
+// };
+// }
+
+// override async getBlock(): Promise {
+// return (await super.getBlock()) as Block;
+// }
+
+// override async getTransaction(): Promise {
+// return (await super.getTransaction()) as TransactionResponse;
+// }
+
+// override async getTransactionReceipt(): Promise {
+// return (await super.getTransactionReceipt()) as TransactionReceipt;
+// }
+// }
+
+// /**
+// * A `TransactionLike` is an extension of {@link ethers.TransactionLike} with additional features for interacting
+// * with zkSync Era.
+// */
+// export interface TransactionLike extends ethers.TransactionLike {
+// /** The custom data for EIP712 transaction metadata. */
+// customData?: null | Eip712Meta;
+// }
+
+// /**
+// * A `Transaction` is an extension of {@link ethers.Transaction} with additional features for interacting
+// * with zkSync Era.
+// */
+// export class Transaction extends ethers.Transaction {
+// /** The custom data for EIP712 transaction metadata. */
+// customData?: null | Eip712Meta;
+// // super.#type is private and there is no way to override which enforced to
+// // introduce following variable
+// #type?: null | number;
+// #from?: null | string;
+
+// override get type(): number | null {
+// return this.#type === EIP712_TX_TYPE ? this.#type : super.type;
+// }
+
+// override set type(value: number | string | null) {
+// switch (value) {
+// case EIP712_TX_TYPE:
+// case 'eip-712':
+// this.#type = EIP712_TX_TYPE;
+// break;
+// default:
+// super.type = value;
+// }
+// }
+
+// static override from(tx: string | TransactionLike): Transaction {
+// if (typeof tx === 'string') {
+// const payload = ethers.getBytes(tx);
+// if (payload[0] !== EIP712_TX_TYPE) {
+// return Transaction.from(ethers.Transaction.from(tx));
+// } else {
+// return Transaction.from(parseEip712(payload));
+// }
+// } else {
+// const result = new Transaction();
+// if (tx.type === EIP712_TX_TYPE) {
+// result.type = EIP712_TX_TYPE;
+// result.customData = tx.customData;
+// result.from = tx.from!;
+// }
+// if (tx.type !== null && tx.type !== undefined) result.type = tx.type;
+// if (tx.to) result.to = tx.to;
+// if (tx.nonce) result.nonce = tx.nonce;
+// if (tx.gasLimit) result.gasLimit = tx.gasLimit;
+// if (tx.gasPrice) result.gasPrice = tx.gasPrice;
+// if (tx.maxPriorityFeePerGas) result.maxPriorityFeePerGas = tx.maxPriorityFeePerGas;
+// if (tx.maxFeePerGas) result.maxFeePerGas = tx.maxFeePerGas;
+// if (tx.data) result.data = tx.data;
+// if (tx.value) result.value = tx.value;
+// if (tx.chainId) result.chainId = tx.chainId;
+// if (tx.signature) result.signature = EthersSignature.from(tx.signature);
+// result.accessList = null;
+
+// if (tx.from) {
+// assertArgument(result.isSigned(), 'unsigned transaction cannot define from', 'tx', tx);
+// assertArgument(isAddressEq(result.from, tx.from), 'from mismatch', 'tx', tx);
+// }
+
+// if (tx.hash) {
+// assertArgument(result.isSigned(), 'unsigned transaction cannot define hash', 'tx', tx);
+// assertArgument(result.hash === tx.hash, 'hash mismatch', 'tx', tx);
+// }
+
+// return result;
+// }
+// }
+
+// override get serialized(): string {
+// if (!this.customData && this.#type !== EIP712_TX_TYPE) {
+// return super.serialized;
+// }
+// return serializeEip712(this, this.signature!);
+// }
+
+// override get unsignedSerialized(): string {
+// if (!this.customData && this.type !== EIP712_TX_TYPE) {
+// return super.unsignedSerialized;
+// }
+// return serializeEip712(this);
+// }
+
+// override toJSON(): any {
+// const { customData } = this;
+// return {
+// ...super.toJSON(),
+// type: !this.#type ? this.type : this.#type,
+// customData,
+// };
+// }
+
+// override get typeName(): string | null {
+// return this.#type === EIP712_TX_TYPE ? 'zksync' : super.typeName;
+// }
+
+// override isSigned(): this is Transaction & {
+// type: number;
+// typeName: string;
+// from: string;
+// signature: Signature;
+// } {
+// return this.#type === EIP712_TX_TYPE
+// ? this.customData?.customSignature !== null
+// : super.isSigned();
+// }
+
+// override get hash(): string | null {
+// if (this.#type === EIP712_TX_TYPE) {
+// return this.customData?.customSignature !== null ? eip712TxHash(this) : null;
+// } else {
+// return super.hash;
+// }
+// }
+
+// override get from(): string | null {
+// return this.#type === EIP712_TX_TYPE ? this.#from! : super.from;
+// }
+// override set from(value: string | null) {
+// this.#from = value;
+// }
+// }
+
/**
* Represents a L2 to L1 transaction log.
*/
@@ -141,6 +533,28 @@ export interface L2ToL1Log {
logIndex: number;
}
+// /**
+// * A `TransactionRequest` is an extension of {@link ethers.TransactionRequest} with additional features for interacting
+// * with zkSync Era.
+// */
+// export interface TransactionRequest extends EthersTransactionRequest {
+// /** The custom data for EIP712 transaction metadata. */
+// customData?: null | Eip712Meta;
+// }
+
+// /**
+// * Interface representation of priority op response that extends {@link ethers.TransactionResponse} and adds a function
+// * that waits to commit a L1 transaction, including when given on optional confirmation number.
+// */
+// export interface PriorityOpResponse extends TransactionResponse {
+// /**
+// * Waits for the L1 transaction to be committed, including waiting for the specified number of confirmations.
+// * @param confirmation The number of confirmations to wait for. Defaults to 1.
+// * @returns A promise that resolves to the transaction receipt once committed.
+// */
+// waitL1Commit(confirmation?: number): Promise;
+// }
+
/** A map containing accounts and their balances. */
export type BalancesMap = { [key: string]: bigint };
@@ -229,6 +643,24 @@ export interface ContractAccountInfo {
nonceOrdering: AccountNonceOrdering;
}
+/** Contains batch information. */
+export interface BatchDetails {
+ number: number;
+ timestamp: number;
+ l1TxCount: number;
+ l2TxCount: number;
+ rootHash?: string;
+ status: string;
+ commitTxHash?: string;
+ committedAt?: Date;
+ proveTxHash?: string;
+ provenAt?: Date;
+ executeTxHash?: string;
+ executedAt?: Date;
+ l1GasPrice: number;
+ l2FairGasPrice: number;
+}
+
/** Contains batch information. */
export interface BlockDetails {
number: bigint;
@@ -343,6 +775,34 @@ export type PayloadSigner = (
provider?: null | EIP1193Provider,
) => Promise;
+// /**
+// * Populates missing fields in a transaction with default values.
+// *
+// * @param transaction The transaction that needs to be populated.
+// * @param [secret] The secret used for populating the transaction.
+// * @param [provider] The provider is used to fetch data from the network if it is required for signing.
+// * @returns A promise that resolves to the populated transaction.
+// */
+// export type TransactionBuilder = (
+// transaction: TransactionRequest,
+// secret?: any,
+// provider?: null | EIP1193Provider,
+// ) => Promise;
+
+// /**
+// * Encapsulates the required input parameters for creating a signer for `SmartAccount`.
+// */
+// export interface SmartAccountSigner {
+// /** Address to which the `SmartAccount` is bound. */
+// address: string;
+// /** Secret in any form that can be used for signing different payloads. */
+// secret: any;
+// /** Custom method for signing different payloads. */
+// payloadSigner?: PayloadSigner;
+// /** Custom method for populating transaction requests. */
+// transactionBuilder?: TransactionBuilder;
+// }
+
export interface WalletBalances {
[key: Address]: Numbers;
}
diff --git a/src/utils.ts b/src/utils.ts
new file mode 100644
index 0000000..c77ddfa
--- /dev/null
+++ b/src/utils.ts
@@ -0,0 +1,1269 @@
+// import { AbiCoder, BigNumberish, Bytes, ethers, SignatureLike } from 'ethers';
+
+import { sha256 } from 'ethereum-cryptography/sha256.js';
+// import { RLP } from '@ethereumjs/rlp';
+// import { secp256k1 } from '@noble/curves/secp256k1';
+// import { keccak256 } from '@ethersproject/keccak256';
+
+import * as web3 from 'web3';
+
+import * as web3Utils from 'web3-utils';
+import * as web3Accounts from 'web3-eth-accounts';
+import * as web3Types from 'web3-types';
+import * as web3Abi from 'web3-eth-abi';
+
+import {
+ DeploymentInfo,
+ // Eip712Meta,
+ EthereumSignature,
+ // PaymasterParams,
+ PriorityOpTree,
+ PriorityQueueType,
+ // Transaction,
+ // TransactionLike,
+ // TransactionRequest,
+} from './types';
+// import { EIP712Signer } from './signer';
+// import { IERC20__factory } from './typechain';
+import { IZkSyncABI } from './contracts/IZkSyncStateTransition';
+import { IBridgehubABI } from './contracts/IBridgehub';
+import { IContractDeployerABI } from './contracts/IContractDeployer';
+import { IL1MessengerABI } from './contracts/IL1Messenger';
+import { IERC20ABI } from './contracts/IERC20';
+import { IERC1271ABI } from './contracts/IERC1271';
+import { IL1BridgeABI } from './contracts/IL1ERC20Bridge';
+import { IL2BridgeABI } from './contracts/IL2Bridge';
+import { INonceHolderABI } from './contracts/INonceHolder';
+
+// import { RpcMethods } from './rpc.methods'; // to be used instead of the one at zksync-ethers: Provider from ./provider
+
+// export * from './paymaster-utils';
+// export * from './smart-account-utils';
+// export { EIP712_TYPES } from './signer';
+
+/**
+ * The web3.js Contract instance for the `ZkSync` interface.
+ * @constant
+ */
+export const ZkSyncMainContract = new web3.Contract(IZkSyncABI);
+
+/**
+ * The ABI of the `Bridgehub` interface.
+ * @constant
+ */
+export const BridgehubContract = new web3.Contract(IBridgehubABI);
+
+/**
+ * The web3.js Contract instance for the `IContractDeployer` interface, which is utilized for deploying smart contracts.
+ * @constant
+ */
+export const ContractDeployerContract = new web3.Contract(IContractDeployerABI);
+
+/**
+ * The web3.js Contract instance for the `IL1Messenger` interface, which is utilized for sending messages from the L2 to L1.
+ * @constant
+ */
+export const L1MessengerContract = new web3.Contract(IL1MessengerABI);
+
+/**
+ * The web3.js Contract instance for the `IERC20` interface, which is utilized for interacting with ERC20 tokens.
+ * @constant
+ */
+export const IERC20Contract = new web3.Contract(IERC20ABI);
+
+/**
+ * The web3.js Contract instance for the `IERC1271` interface, which is utilized for signature validation by contracts.
+ * @constant
+ */
+export const IERC1271Contract = new web3.Contract(IERC1271ABI);
+
+/**
+ * The web3.js Contract instance for the `IL1Bridge` interface, which is utilized for transferring ERC20 tokens from L1 to L2.
+ * @constant
+ */
+export const L1BridgeContract = new web3.Contract(IL1BridgeABI);
+
+/**
+ * The web3.js Contract instance for the `IL2Bridge` interface, which is utilized for transferring ERC20 tokens from L2 to L1.
+ * @constant
+ */
+export const L2Bridge = new web3.Contract(IL2BridgeABI);
+
+/**
+ * The web3.js Contract instance for the `INonceHolder` interface, which is utilized for managing deployment nonces.
+ * @constant
+ */
+export const NonceHolderContract = new web3.Contract(INonceHolderABI);
+
+/**
+ * The address of the L1 `ETH` token.
+ * @constant
+ */
+export const ETH_ADDRESS: web3.Address = '0x0000000000000000000000000000000000000000';
+
+/**
+ * The address of the L1 `ETH` token.
+ * @constant
+ */
+export const LEGACY_ETH_ADDRESS: web3.Address = '0x0000000000000000000000000000000000000000';
+
+/**
+ * In the contracts the zero address can not be used, use one instead
+ * @constant
+ */
+export const ETH_ADDRESS_IN_CONTRACTS: web3.Address = '0x0000000000000000000000000000000000000001';
+
+/**
+ * The formal address for the `Bootloader`.
+ * @constant
+ */
+export const BOOTLOADER_FORMAL_ADDRESS: web3.Address = '0x0000000000000000000000000000000000008001';
+
+/**
+ * The address of the Contract deployer.
+ * @constant
+ */
+export const CONTRACT_DEPLOYER_ADDRESS: web3.Address = '0x0000000000000000000000000000000000008006';
+
+/**
+ * The address of the L1 messenger.
+ * @constant
+ */
+export const L1_MESSENGER_ADDRESS: web3.Address = '0x0000000000000000000000000000000000008008';
+
+/**
+ * The address of the L2 `ETH` token.
+ * @constant
+ * @deprecated In favor of {@link L2_BASE_TOKEN_ADDRESS}.
+ */
+export const L2_ETH_TOKEN_ADDRESS: web3.Address = '0x000000000000000000000000000000000000800a';
+
+/**
+ * The address of the base token.
+ * @constant
+ */
+export const L2_BASE_TOKEN_ADDRESS = '0x000000000000000000000000000000000000800a';
+
+/**
+ * The address of the Nonce holder.
+ * @constant
+ */
+export const NONCE_HOLDER_ADDRESS: web3.Address = '0x0000000000000000000000000000000000008003';
+
+/**
+ * Used for applying and undoing aliases on addresses during bridging from L1 to L2.
+ * @constant
+ */
+export const L1_TO_L2_ALIAS_OFFSET: web3.Address = '0x1111000000000000000000000000000000001111';
+
+/**
+ * The EIP1271 magic value used for signature validation in smart contracts.
+ * This predefined constant serves as a standardized indicator to signal successful
+ * signature validation by the contract.
+ *
+ * @constant
+ */
+export const EIP1271_MAGIC_VALUE = '0x1626ba7e';
+
+/**
+ * Represents an EIP712 transaction type.
+ *
+ * @constant
+ */
+export const EIP712_TX_TYPE = 0x71;
+
+/**
+ * Represents a priority transaction operation on L2.
+ *
+ * @constant
+ */
+export const PRIORITY_OPERATION_L2_TX_TYPE = 0xff;
+
+/**
+ * The maximum bytecode length in bytes that can be deployed.
+ *
+ * @constant
+ */
+export const MAX_BYTECODE_LEN_BYTES: number = ((1 << 16) - 1) * 32;
+
+/**
+ * Numerator used in scaling the gas limit to ensure acceptance of `L1->L2` transactions.
+ *
+ * This constant is part of a coefficient calculation to adjust the gas limit to account for variations
+ * in the SDK estimation, ensuring the transaction will be accepted.
+ *
+ * @constant
+ */
+export const L1_FEE_ESTIMATION_COEF_NUMERATOR = 12;
+
+/**
+ * Denominator used in scaling the gas limit to ensure acceptance of `L1->L2` transactions.
+ *
+ * This constant is part of a coefficient calculation to adjust the gas limit to account for variations
+ * in the SDK estimation, ensuring the transaction will be accepted.
+ *
+ * @constant
+ */
+export const L1_FEE_ESTIMATION_COEF_DENOMINATOR = 10;
+
+/**
+ * Gas limit used for displaying the error messages when the
+ * users do not have enough fee when depositing ERC20 token from L1 to L2.
+ *
+ * @constant
+ */
+export const L1_RECOMMENDED_MIN_ERC20_DEPOSIT_GAS_LIMIT = 400_000;
+
+/**
+ * Gas limit used for displaying the error messages when the
+ * users do not have enough fee when depositing `ETH` token from L1 to L2.
+ *
+ * @constant
+ */
+export const L1_RECOMMENDED_MIN_ETH_DEPOSIT_GAS_LIMIT = 200_000;
+
+/**
+ * Default gas per pubdata byte for L2 transactions.
+ * This value is utilized when inserting a default value for type 2
+ * and EIP712 type transactions.
+ *
+ * @constant
+ */
+// It is a realistic value, but it is large enough to fill into any batch regardless of the pubdata price.
+export const DEFAULT_GAS_PER_PUBDATA_LIMIT = 50_000;
+
+/**
+ * The `L1->L2` transactions are required to have the following gas per pubdata byte.
+ *
+ * @constant
+ */
+export const REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT = 800;
+
+/**
+ * consider adding the next few functions to web3.js:
+ * */
+
+export const NumberToByes = (number: web3Types.Numbers) =>
+ web3Utils.hexToBytes(web3Utils.numberToHex(number));
+
+export function concat(strings: web3Types.Bytes[]): string {
+ return '0x' + strings.map(d => web3Utils.toHex(d).substring(2)).join('');
+}
+
+export function contractFunctionId(value: string): string {
+ return web3Utils.keccak256(web3Utils.utf8ToBytes(value));
+}
+
+function recoverSignerAddress(
+ messageOrData: string | web3Types.Eip712TypedData,
+ signature: string | EthereumSignature,
+) {
+ let message;
+ if (typeof messageOrData !== 'string') {
+ message = web3Abi.getEncodedEip712Data(messageOrData, true);
+ } else {
+ message = messageOrData;
+ }
+
+ const r = web3Accounts.toUint8Array(
+ (signature as EthereumSignature).r ?? (signature as string).slice(0, 66),
+ );
+ const s = web3Accounts.toUint8Array(
+ (signature as EthereumSignature).s ?? `0x${(signature as string).slice(66, 130)}`,
+ );
+ const v = BigInt(
+ (signature as EthereumSignature).v ??
+ web3Utils.hexToNumber(`0x${(signature as string).slice(130, 132)}`),
+ );
+
+ const recoveredPublicKey = web3Utils.bytesToHex(
+ web3Accounts.ecrecover(web3Accounts.toUint8Array(message), v, r, s),
+ );
+
+ const recoveredAddress = `0x${web3Utils.keccak256(web3Utils.bytesToHex(recoveredPublicKey)).slice(-40)}`;
+ return recoveredAddress;
+}
+
+/**
+ * Returns true if token represents ETH on L1 or L2.
+ *
+ * @param token The token address.
+ *
+ * @example
+ *
+ * const isL1ETH = utils.isETH(utils.ETH_ADDRESS); // true
+ * const isL2ETH = utils.isETH(utils.ETH_ADDRESS_IN_CONTRACTS); // true
+ */
+export function isETH(token: web3.Address) {
+ return (
+ isAddressEq(token, LEGACY_ETH_ADDRESS) ||
+ isAddressEq(token, L2_BASE_TOKEN_ADDRESS) ||
+ isAddressEq(token, ETH_ADDRESS_IN_CONTRACTS)
+ );
+}
+
+/**
+ * Pauses execution for a specified number of milliseconds.
+ *
+ * @param millis The number of milliseconds to pause execution.
+ *
+ * @example
+ *
+ * await sleep(1_000);
+ */
+export function sleep(millis: number): Promise {
+ return new Promise(resolve => setTimeout(resolve, millis));
+}
+
+/**
+ * Returns the default settings for L1 transactions.
+ */
+export function layer1TxDefaults(): {
+ queueType: PriorityQueueType.Deque;
+ opTree: PriorityOpTree.Full;
+} {
+ return {
+ queueType: PriorityQueueType.Deque,
+ opTree: PriorityOpTree.Full,
+ };
+}
+
+/**
+ * Returns a `keccak` encoded message with a given sender address and block number from the L1 messenger contract.
+ *
+ * @param sender The sender of the message on L2.
+ * @param msg The encoded message.
+ * @param txNumberInBlock The index of the transaction in the block.
+ * @returns The hashed `L2->L1` message.
+ *
+ * @example
+ *
+ * const withdrawETHMessage = "0x6c0960f936615cf349d7f6344891b1e7ca7c72883f5dc04900000000000000000000000000000000000000000000000000000001a13b8600";
+ * const withdrawETHMessageHash = utils.getHashedL2ToL1Msg("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", withdrawETHMessage, 0);
+ * // withdrawETHMessageHash = "0xd8c80ecb64619e343f57c3b133c6c6d8dd0572dd3488f1ca3276c5b7fd3a938d"
+ */
+export function getHashedL2ToL1Msg(
+ sender: web3.Address,
+ msg: web3Types.Bytes,
+ txNumberInBlock: number,
+): string {
+ const encodedMsg = new Uint8Array([
+ 0, // l2ShardId
+ 1, // isService
+ ...web3Utils.hexToBytes(web3Utils.padLeft(web3Utils.toHex(txNumberInBlock), 2)),
+ ...web3Utils.hexToBytes(L1_MESSENGER_ADDRESS),
+ ...web3Utils.hexToBytes(web3Utils.padLeft(sender, 32)),
+ ...web3Utils.hexToBytes(web3Utils.keccak256(msg)),
+ ]);
+
+ return web3Utils.keccak256(encodedMsg);
+}
+
+/**
+ * Returns a log containing details of all deployed contracts related to a transaction receipt.
+ *
+ * @param receipt The transaction receipt containing deployment information.
+ *
+ * @example
+ *
+ *
+ */
+export function getDeployedContracts(receipt: web3Types.TransactionReceipt): DeploymentInfo[] {
+ const addressBytesLen = 40;
+ return (
+ receipt.logs
+ .filter(
+ log =>
+ log.topics &&
+ log.topics[0] === contractFunctionId('ContractDeployed(address,bytes32,address)') &&
+ log.address &&
+ isAddressEq(log.address, CONTRACT_DEPLOYER_ADDRESS),
+ )
+ // Take the last topic (deployed contract address as U256) and extract address from it (U160).
+ .map(log => {
+ if (!log.topics) throw new Error('No topics in log');
+ const sender = `0x${log.topics[1].slice(log.topics[1].length - addressBytesLen)}`;
+ const bytecodeHash = log.topics[2];
+ const address = `0x${log.topics[3].slice(log.topics[3].length - addressBytesLen)}`;
+ return {
+ sender: web3Utils.toChecksumAddress(sender),
+ bytecodeHash: web3Utils.toHex(bytecodeHash),
+ deployedAddress: web3Utils.toChecksumAddress(address),
+ };
+ })
+ );
+}
+
+/**
+ * Generates a future-proof contract address using a salt plus bytecode, allowing the determination of an address before deployment.
+ *
+ * @param sender The sender's address.
+ * @param bytecodeHash The hash of the bytecode, typically the output from `zkSolc`.
+ * @param salt A randomization element used to create the contract address.
+ * @param input The ABI-encoded constructor arguments, if any.
+ *
+ * @remarks The implementation of `create2Address` in zkSync Era may differ slightly from Ethereum.
+ *
+ * @example
+ *
+ * const address = utils.create2Address("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", "0x010001cb6a6e8d5f6829522f19fa9568660e0a9cd53b2e8be4deb0a679452e41", "0x01", "0x01");
+ * // address = "0x29bac3E5E8FFE7415F97C956BFA106D70316ad50"
+ */
+export function create2Address(
+ sender: web3Types.Address,
+ bytecodeHash: web3Types.Bytes,
+ salt: web3Types.Bytes,
+ input: web3Types.Bytes = '',
+): string {
+ const prefix = web3Utils.keccak256(web3Utils.utf8ToBytes('zksyncCreate2'));
+ const inputHash = web3Utils.keccak256(input);
+ const addressBytes = web3Utils
+ .keccak256(concat([prefix, web3Utils.padLeft(sender, 32), salt, bytecodeHash, inputHash]))
+ .slice(26);
+ return web3Utils.toChecksumAddress(addressBytes);
+}
+
+/**
+ * Generates a contract address from the deployer's account and nonce.
+ *
+ * @param sender The address of the deployer's account.
+ * @param senderNonce The nonce of the deployer's account.
+ *
+ * @example
+ *
+ * const address = utils.createAddress("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", 1);
+ * // address = "0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021"
+ */
+export function createAddress(sender: web3.Address, senderNonce: web3Types.Numbers): string {
+ const prefix = web3Utils.keccak256(web3Utils.utf8ToBytes('zksyncCreate'));
+ const addressBytes = web3Utils
+ .keccak256(
+ concat([
+ prefix,
+ web3Utils.padLeft(sender, 32),
+ web3Utils.padLeft(web3Utils.toHex(senderNonce), 32),
+ ]),
+ )
+ .slice(26);
+
+ return web3Utils.toChecksumAddress(addressBytes);
+}
+
+/**
+ * Checks if the transaction's base cost is greater than the provided value, which covers the transaction's cost.
+ *
+ * @param baseCost The base cost of the transaction.
+ * @param value The value covering the transaction's cost.
+ * @throws {Error} The base cost must be greater than the provided value.
+ *
+ * @example
+ *
+ * const baseCost = 100;
+ * const value = 99;
+ * try {
+ * await utils.checkBaseCost(baseCost, value);
+ * } catch (e) {
+ * // e.message = `The base cost of performing the priority operation is higher than the provided value parameter for the transaction: baseCost: ${baseCost}, provided value: ${value}`,
+ * }
+ */
+export async function checkBaseCost(
+ baseCost: web3Types.Numbers,
+ value: web3Types.Numbers | Promise,
+): Promise {
+ if (baseCost > (await value)) {
+ throw new Error(
+ 'The base cost of performing the priority operation is higher than the provided value parameter ' +
+ `for the transaction: baseCost: ${baseCost}, provided value: ${value}!`,
+ );
+ }
+}
+
+// /**
+// * Serializes an EIP712 transaction and includes a signature if provided.
+// *
+// * @param transaction The transaction that needs to be serialized.
+// * @param signature Ethers signature to be included in the transaction.
+// * @throws {Error} Throws an error if:
+// * - `transaction.customData.customSignature` is an empty string. The transaction should be signed, and the `transaction.customData.customSignature` field should be populated with the signature. It should not be specified if the transaction is not signed.
+// * - `transaction.chainId` is not provided.
+// * - `transaction.from` is not provided.
+// *
+// * @example Serialize EIP712 transaction without signature.
+// *
+// * const serializedTx = utils.serializeEip712({ chainId: 270, from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049" }, null);
+// *
+// * // serializedTx = "0x71ea8080808080808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0"
+// *
+// * @example Serialize EIP712 transaction with signature.
+// *
+// * const signature = ethers.Signature.from("0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a");
+// *
+// * const serializedTx = utils.serializeEip712(
+// * {
+// * chainId: 270,
+// * from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049",
+// * to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618",
+// * value: 1_000_000,
+// * },
+// * signature
+// * );
+// * // serializedTx = "0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0"
+// */
+// export function serializeEip712(
+// transaction: TransactionLike,
+// signature?: ethers.SignatureLike,
+// ): string {
+// if (!transaction.chainId) {
+// throw Error("Transaction chainId isn't set!");
+// }
+
+// if (!transaction.from) {
+// throw new Error('Explicitly providing `from` field is required for EIP712 transactions!');
+// }
+// const from = transaction.from;
+// const meta: Eip712Meta = transaction.customData ?? {};
+// const maxFeePerGas = transaction.maxFeePerGas || transaction.gasPrice || 0;
+// const maxPriorityFeePerGas = transaction.maxPriorityFeePerGas || maxFeePerGas;
+
+// const fields: any[] = [
+// NumberToByes(transaction.nonce || 0),
+// NumberToByes(maxPriorityFeePerGas),
+// NumberToByes(maxFeePerGas),
+// NumberToByes(transaction.gasLimit || 0),
+// transaction.to ? web3Utils.toChecksumAddress(transaction.to) : '0x',
+// NumberToByes(transaction.value || 0),
+// transaction.data || '0x',
+// ];
+
+// if (signature) {
+// const sig = ethers.Signature.from(signature);
+// fields.push(NumberToByes(sig.yParity));
+// fields.push(NumberToByes(sig.r));
+// fields.push(NumberToByes(sig.s));
+// } else {
+// fields.push(NumberToByes(transaction.chainId));
+// fields.push('0x');
+// fields.push('0x');
+// }
+// fields.push(NumberToByes(transaction.chainId));
+// fields.push(web3Utils.toChecksumAddress(from));
+
+// // Add meta
+// fields.push(NumberToByes(meta.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT));
+// fields.push((meta.factoryDeps ?? []).map(dep => web3Utils.toHex(dep)));
+
+// if (meta.customSignature && web3Utils.bytesToUint8Array(meta.customSignature).length === 0) {
+// throw new Error('Empty signatures are not supported!');
+// }
+// fields.push(meta.customSignature || '0x');
+
+// if (meta.paymasterParams) {
+// fields.push([
+// meta.paymasterParams.paymaster,
+// web3Utils.toHex(meta.paymasterParams.paymasterInput),
+// ]);
+// } else {
+// fields.push([]);
+// }
+
+// return concat([new Uint8Array([EIP712_TX_TYPE]), RLP.encode(fields)]);
+// }
+
+/**
+ * Returns the hash of the given bytecode.
+ *
+ * @param bytecode The bytecode to hash.
+ *
+ * @example
+ *
+ * const bytecode =
+ * "0x000200000000000200010000000103550000006001100270000000130010019d0000008001000039000000400010043f0000000101200190000000290000c13d0000000001000031000000040110008c000000420000413d0000000101000367000000000101043b000000e001100270000000150210009c000000310000613d000000160110009c000000420000c13d0000000001000416000000000110004c000000420000c13d000000040100008a00000000011000310000001702000041000000200310008c000000000300001900000000030240190000001701100197000000000410004c000000000200a019000000170110009c00000000010300190000000001026019000000000110004c000000420000c13d00000004010000390000000101100367000000000101043b000000000010041b0000000001000019000000490001042e0000000001000416000000000110004c000000420000c13d0000002001000039000001000010044300000120000004430000001401000041000000490001042e0000000001000416000000000110004c000000420000c13d000000040100008a00000000011000310000001702000041000000000310004c000000000300001900000000030240190000001701100197000000000410004c000000000200a019000000170110009c00000000010300190000000001026019000000000110004c000000440000613d00000000010000190000004a00010430000000000100041a000000800010043f0000001801000041000000490001042e0000004800000432000000490001042e0000004a00010430000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0000000200000000000000000000000000000040000001000000000000000000000000000000000000000000000000000000000000000000000000006d4ce63c0000000000000000000000000000000000000000000000000000000060fe47b18000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000080000000000000000000000000000000000000000000000000000000000000000000000000000000009c8c8fa789967eb514f3ec9def748480945cc9b10fcbd1a19597d924eb201083";
+ * const hashedBytecode = utils.hashBytecode(bytecode);
+ * /*
+ * hashedBytecode = new Uint8Array([
+ * 1, 0, 0, 27, 57, 231, 154, 55, 0, 164, 201, 96, 244, 120, 23, 112, 54, 34, 224, 133,
+ * 160, 122, 88, 164, 112, 80, 0, 134, 48, 138, 74, 16,
+ * ]),
+ * );
+ * *\/
+ */
+export function hashBytecode(bytecode: web3Types.Bytes): Uint8Array {
+ // For getting the consistent length we first convert the bytecode to UInt8Array
+ const bytecodeAsArray = web3Utils.bytesToUint8Array(bytecode);
+
+ if (bytecodeAsArray.length % 32 !== 0) {
+ throw new Error('The bytecode length in bytes must be divisible by 32!');
+ }
+
+ if (bytecodeAsArray.length > MAX_BYTECODE_LEN_BYTES) {
+ throw new Error(`Bytecode can not be longer than ${MAX_BYTECODE_LEN_BYTES} bytes!`);
+ }
+
+ const hashStr = sha256(Buffer.from(bytecodeAsArray));
+ const hash = web3Utils.bytesToUint8Array(hashStr);
+
+ // Note that the length of the bytecode
+ // should be provided in 32-byte words.
+ const bytecodeLengthInWords = bytecodeAsArray.length / 32;
+ if (bytecodeLengthInWords % 2 === 0) {
+ throw new Error('Bytecode length in 32-byte words must be odd!');
+ }
+
+ // The bytecode should always take the first 2 bytes of the bytecode hash,
+ // so we pad it from the left in case the length is smaller than 2 bytes.
+ const bytecodeLengthPadded = web3Utils.bytesToUint8Array(
+ web3Utils.padLeft(bytecodeLengthInWords, 2),
+ );
+
+ const codeHashVersion = new Uint8Array([1, 0]);
+ hash.set(codeHashVersion, 0);
+ hash.set(bytecodeLengthPadded, 2);
+
+ return hash;
+}
+
+// /**
+// * Parses an EIP712 transaction from a payload.
+// *
+// * @param payload The payload to parse.
+// *
+// * @example
+// *
+// * import { types } from "zksync-ethers";
+// *
+// * const serializedTx =
+// * "0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0";
+// * const tx: types.TransactionLike = utils.parseEip712(serializedTx);
+// * /*
+// * tx: types.TransactionLike = {
+// * type: 113,
+// * nonce: 0,
+// * maxPriorityFeePerGas: BigInt(0),
+// * maxFeePerGas: BigInt(0),
+// * gasLimit: BigInt(0),
+// * to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618",
+// * value: BigInt(1000000),
+// * data: "0x",
+// * chainId: BigInt(270),
+// * from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049",
+// * customData: {
+// * gasPerPubdata: BigInt(50000),
+// * factoryDeps: [],
+// * customSignature: "0x",
+// * paymasterParams: null,
+// * },
+// * hash: "0x9ed410ce33179ac1ff6b721060605afc72d64febfe0c08cacab5a246602131ee",
+// * };
+// * *\/
+// */
+// // TODO: extend ethers.Transaction and add custom fields
+// export function parseEip712(payload: web3Types.Bytes): TransactionLike {
+// function handleAddress(value: string): string | null {
+// if (value === '0x') {
+// return null;
+// }
+// return web3Utils.toChecksumAddress(value);
+// }
+
+// function handleNumber(value: string): bigint {
+// if (!value || value === '0x') {
+// return 0n;
+// }
+// return BigInt(value);
+// }
+
+// function arrayToPaymasterParams(arr: string[]): PaymasterParams | undefined {
+// if (arr.length === 0) {
+// return undefined;
+// }
+// if (arr.length !== 2) {
+// throw new Error(
+// `Invalid paymaster parameters, expected to have length of 2, found ${arr.length}!`,
+// );
+// }
+
+// return {
+// paymaster: web3Utils.toChecksumAddress(arr[0]),
+// paymasterInput: web3Utils.bytesToUint8Array(arr[1]),
+// };
+// }
+
+// const bytes = web3Utils.bytesToUint8Array(payload);
+
+// // try using: RLP.decode
+// const raw = ethers.decodeRlp(bytes.slice(1)) as string[];
+// const transaction: TransactionLike = {
+// type: EIP712_TX_TYPE,
+// nonce: Number(handleNumber(raw[0])),
+// maxPriorityFeePerGas: handleNumber(raw[1]),
+// maxFeePerGas: handleNumber(raw[2]),
+// gasLimit: handleNumber(raw[3]),
+// to: handleAddress(raw[4]),
+// value: handleNumber(raw[5]),
+// data: raw[6],
+// chainId: handleNumber(raw[10]),
+// from: handleAddress(raw[11]),
+// customData: {
+// gasPerPubdata: handleNumber(raw[12]),
+// factoryDeps: raw[13] as unknown as string[],
+// customSignature: raw[14],
+// paymasterParams: arrayToPaymasterParams(raw[15] as unknown as string[]),
+// },
+// };
+
+// const ethSignature = {
+// v: Number(handleNumber(raw[7])),
+// r: raw[8],
+// s: raw[9],
+// };
+
+// if (
+// (web3Utils.toHex(ethSignature.r) === '0x' || web3Utils.toHex(ethSignature.s) === '0x') &&
+// !transaction.customData?.customSignature
+// ) {
+// return transaction;
+// }
+
+// if (ethSignature.v !== 0 && ethSignature.v !== 1 && !transaction.customData?.customSignature) {
+// throw new Error('Failed to parse signature!');
+// }
+
+// if (!transaction.customData?.customSignature) {
+// // TODO: either try to use a string or a signature object
+// // const signatureStr = `${ethSignature.r}${ethSignature.s.slice(2)}${ethSignature.v.slice(2)}`;
+// // or maybe try to use @noble/curves (but it does not deal with `v`):
+// // import { SignatureType } from '@noble/curves';
+// // new secp256k1.Signature(ethSignature.r, ethSignature.s, ethSignature.v);
+// transaction.signature = ethers.Signature.from(ethSignature);
+// }
+
+// transaction.hash = eip712TxHash(transaction, ethSignature);
+
+// return transaction;
+// }
+
+export function getSignature(transaction: any, ethSignature?: EthereumSignature): Uint8Array {
+ if (transaction?.customData?.customSignature && transaction.customData.customSignature.length) {
+ return web3Utils.bytesToUint8Array(transaction.customData.customSignature);
+ }
+
+ if (!ethSignature) {
+ throw new Error('No signature provided!');
+ }
+
+ const r = web3Utils.bytesToUint8Array(web3Utils.padLeft(web3Utils.toHex(ethSignature.r), 32));
+ const s = web3Utils.bytesToUint8Array(web3Utils.padLeft(web3Utils.toHex(ethSignature.s), 32));
+ const v = ethSignature.v;
+
+ return new Uint8Array([...r, ...s, v]);
+}
+
+// /**
+// * Returns the hash of an EIP712 transaction.
+// *
+// * @param transaction The EIP-712 transaction.
+// * @param ethSignature The ECDSA signature of the transaction.
+// *
+// * @example
+// *
+// *
+// */
+// export function eip712TxHash(
+// transaction: Transaction | TransactionRequest,
+// ethSignature?: EthereumSignature,
+// ): string {
+// const signedDigest = EIP712Signer.getSignedDigest(transaction);
+// const hashedSignature = web3Utils.keccak256(getSignature(transaction, ethSignature));
+
+// return web3Utils.keccak256(concat([signedDigest, hashedSignature]));
+// }
+
+// /**
+// * Returns the hash of the L2 priority operation from a given transaction receipt and L2 address.
+// *
+// * @param txReceipt The receipt of the L1 transaction.
+// * @param zkSyncAddress The address of the zkSync Era main contract.
+// *
+// * @example
+// */
+// export function getL2HashFromPriorityOp(
+// txReceipt: web3Types.TransactionReceipt,
+// zkSyncAddress: web3.Address,
+// ): string {
+// let txHash: string | null = null;
+// for (const log of txReceipt.logs) {
+// if (!isAddressEq(log.address as string, zkSyncAddress)) {
+// continue;
+// }
+
+// try {
+// // TODO: implement at web3.js Contract the parsing of the logs similar to new ethers.Interface(ABI).parseLog(...)
+// const priorityQueueLog = ZkSyncMainContract.parseLog({
+// topics: log.topics as string[],
+// data: log.data,
+// });
+// if (priorityQueueLog && priorityQueueLog.args.txHash !== null) {
+// txHash = priorityQueueLog.args.txHash;
+// }
+// } catch {
+// // skip
+// }
+// }
+// if (!txHash) {
+// throw new Error('Failed to parse tx logs!');
+// }
+
+// return txHash;
+// }
+
+const ADDRESS_MODULO = 2n ** 160n;
+
+/**
+ * Converts the address that submitted a transaction to the inbox on L1 to the `msg.sender` viewed on L2.
+ * Returns the `msg.sender` of the `L1->L2` transaction as the address of the contract that initiated the transaction.
+ *
+ * All available cases:
+ * - During a normal transaction, if contract `A` calls contract `B`, the `msg.sender` is `A`.
+ * - During `L1->L2` communication, if an EOA `X` calls contract `B`, the `msg.sender` is `X`.
+ * - During `L1->L2` communication, if a contract `A` calls contract `B`, the `msg.sender` is `applyL1ToL2Alias(A)`.
+ *
+ * @param address The address of the contract.
+ * @returns The transformed address representing the `msg.sender` on L2.
+ *
+ * @see
+ * {@link undoL1ToL2Alias}.
+ *
+ * @example
+ *
+ * const l1ContractAddress = "0x702942B8205E5dEdCD3374E5f4419843adA76Eeb";
+ * const l2ContractAddress = utils.applyL1ToL2Alias(l1ContractAddress);
+ * // l2ContractAddress = "0x813A42B8205E5DedCd3374e5f4419843ADa77FFC"
+ *
+ */
+export function applyL1ToL2Alias(address: string): string {
+ return web3Utils.padLeft(
+ web3Utils.toHex((BigInt(address) + BigInt(L1_TO_L2_ALIAS_OFFSET)) % ADDRESS_MODULO),
+ 20,
+ );
+}
+
+/**
+ * Converts and returns the `msg.sender` viewed on L2 to the address that submitted a transaction to the inbox on L1.
+ *
+ * @param address The sender address viewed on L2.
+ *
+ * @see
+ * {@link applyL1ToL2Alias}.
+ *
+ * @example
+ *
+ * const l2ContractAddress = "0x813A42B8205E5DedCd3374e5f4419843ADa77FFC";
+ * const l1ContractAddress = utils.undoL1ToL2Alias(l2ContractAddress);
+ * // const l1ContractAddress = "0x702942B8205E5dEdCD3374E5f4419843adA76Eeb"
+ */
+export function undoL1ToL2Alias(address: string): string {
+ let result = BigInt(address) - BigInt(L1_TO_L2_ALIAS_OFFSET);
+ if (result < 0n) {
+ result += ADDRESS_MODULO;
+ }
+ return web3Utils.padLeft(web3Utils.toHex(result), 20);
+}
+
+// /**
+// * Returns the data needed for correct initialization of an L1 token counterpart on L2.
+// *
+// * @param l1TokenAddress The token address on L1.
+// * @param provider The client that is able to work with contracts on a read-write basis.
+// * @returns The encoded bytes which contains token name, symbol and decimals.
+// */
+// export async function getERC20DefaultBridgeData(
+// l1TokenAddress: string,
+// context: web3.Web3Context, // or maybe use RpcMethods?
+// ): Promise {
+// if (isAddressEq(l1TokenAddress, LEGACY_ETH_ADDRESS)) {
+// l1TokenAddress = ETH_ADDRESS_IN_CONTRACTS;
+// }
+// const token = IERC20__factory.connect(l1TokenAddress, context);
+
+// const name = isAddressEq(l1TokenAddress, ETH_ADDRESS_IN_CONTRACTS) ? 'Ether' : await token.name();
+// const symbol = isAddressEq(l1TokenAddress, ETH_ADDRESS_IN_CONTRACTS)
+// ? 'ETH'
+// : await token.symbol();
+// const decimals = isAddressEq(l1TokenAddress, ETH_ADDRESS_IN_CONTRACTS)
+// ? 18
+// : await token.decimals();
+
+// const coder = new AbiCoder();
+
+// const nameBytes = coder.encode(['string'], [name]);
+// const symbolBytes = coder.encode(['string'], [symbol]);
+// const decimalsBytes = coder.encode(['uint256'], [decimals]);
+
+// return coder.encode(['bytes', 'bytes', 'bytes'], [nameBytes, symbolBytes, decimalsBytes]);
+// }
+
+// /**
+// * Returns the calldata sent by an L1 ERC20 bridge to its L2 counterpart during token bridging.
+// *
+// * @param l1TokenAddress The token address on L1.
+// * @param l1Sender The sender address on L1.
+// * @param l2Receiver The recipient address on L2.
+// * @param amount The gas fee for the number of tokens to bridge.
+// * @param bridgeData Additional bridge data.
+// *
+// * @example
+// *
+// *
+// */
+// export async function getERC20BridgeCalldata(
+// l1TokenAddress: string,
+// l1Sender: string,
+// l2Receiver: string,
+// amount: web3Types.Numbers,
+// bridgeData: web3Types.Bytes,
+// ): Promise {
+// return L2_BRIDGE_ABI.encodeFunctionData('finalizeDeposit', [
+// l1Sender,
+// l2Receiver,
+// l1TokenAddress,
+// amount,
+// bridgeData,
+// ]);
+// }
+
+/**
+ * Validates signatures from non-contract account addresses (EOA).
+ * Provides similar functionality to `ethers.js` but returns `true`
+ * if the validation process succeeds, otherwise returns `false`.
+ *
+ * Called from {@link isSignatureCorrect} for non-contract account addresses.
+ *
+ * @param address The address which signs the `msgHash`.
+ * @param msgHash The hash of the message.
+ * @param signature The Ethers signature.
+ *
+ * @example
+ *
+ * import { Wallet, utils } from "zksync-ethers";
+ *
+ * const ADDRESS = "";
+ * const PRIVATE_KEY = "";
+ *
+ * const message = "Hello, world!";
+ * const signature = await new Wallet(PRIVATE_KEY).signMessage(message);
+ * // ethers.Wallet can be used as well
+ * // const signature = await new ethers.Wallet(PRIVATE_KEY).signMessage(message);
+ *
+ * const isValidSignature = await utils.isECDSASignatureCorrect(ADDRESS, message, signature);
+ * // isValidSignature = true
+ */
+function isECDSASignatureCorrect(
+ address: string,
+ msgHash: string,
+ signature: EthereumSignature,
+): boolean {
+ try {
+ return isAddressEq(address, recoverSignerAddress(msgHash, signature));
+ } catch {
+ // In case ECDSA signature verification has thrown an error,
+ // we simply consider the signature as incorrect.
+ return false;
+ }
+}
+
+/**
+ * Called from {@link isSignatureCorrect} for contract account addresses.
+ * The function returns `true` if the validation process results
+ * in the {@link EIP1271_MAGIC_VALUE}.
+ *
+ * @param context The web3 context.
+ * @param address The sender address.
+ * @param msgHash The hash of the message.
+ * @param signature The Ethers signature.
+ *
+ * @see
+ * {@link isMessageSignatureCorrect} and {@link isTypedDataSignatureCorrect} to validate signatures.
+ *
+ * @example
+ *
+ */
+async function isEIP1271SignatureCorrect(
+ context: web3.Web3Context, // or maybe use RpcMethods?
+ address: string,
+ msgHash: string,
+ signature: EthereumSignature,
+): Promise {
+ const accountContract = new web3.Contract(IERC1271ABI, address, context);
+
+ // This line may throw an exception if the contract does not implement the EIP1271 correctly.
+ // But it may also throw an exception in case the internet connection is lost.
+ // It is the caller's responsibility to handle the exception.
+ const result = await accountContract.methods.isValidSignature(msgHash, signature).call();
+
+ return result === EIP1271_MAGIC_VALUE;
+}
+
+/**
+ * Called from {@link isMessageSignatureCorrect} and {@link isTypedDataSignatureCorrect}.
+ * Returns whether the account abstraction signature is correct.
+ * Signature can be created using EIP1271 or ECDSA.
+ *
+ * @param context The web3 context.
+ * @param address The sender address.
+ * @param msgHash The hash of the message.
+ * @param signature The Ethers signature.
+ */
+async function isSignatureCorrect(
+ context: web3.Web3Context, // or maybe use RpcMethods?
+ address: string,
+ msgHash: string,
+ signature: EthereumSignature,
+): Promise {
+ const code = await web3.eth.getCode(context, address, undefined, web3Types.DEFAULT_RETURN_FORMAT);
+ const isContractAccount = web3Utils.bytesToUint8Array(code).length !== 0;
+
+ if (!isContractAccount) {
+ return isECDSASignatureCorrect(address, msgHash, signature);
+ } else {
+ return await isEIP1271SignatureCorrect(context, address, msgHash, signature);
+ }
+}
+
+// /**
+// * Returns whether the account abstraction message signature is correct.
+// * Signature can be created using EIP1271 or ECDSA.
+// *
+// * @param provider The provider.
+// * @param address The sender address.
+// * @param message The hash of the message.
+// * @param signature The Ethers signature.
+// *
+// * @example
+// *
+// * import { Wallet, utils, Provider } from "zksync-ethers";
+// *
+// * const ADDRESS = "";
+// * const PRIVATE_KEY = "";
+// * const context = Provider.getDefaultProvider(types.Network.Sepolia);
+// *
+// * const message = "Hello, world!";
+// * const signature = await new Wallet(PRIVATE_KEY).signMessage(message);
+// * // ethers.Wallet can be used as well
+// * // const signature = await new ethers.Wallet(PRIVATE_KEY).signMessage(message);
+// *
+// * const isValidSignature = await utils.isMessageSignatureCorrect(context, ADDRESS, message, signature);
+// * // isValidSignature = true
+// */
+// export async function isMessageSignatureCorrect(
+// context: web3.Web3Context, // or maybe use RpcMethods?
+// address: string,
+// message: Uint8Array | string,
+// signature: SignatureLike,
+// ): Promise {
+// // TODO: needs to implement this (similar to web3Abi.getEncodedEip712Data but for stings and Uint8Array)
+// const msgHash = ethers.getMessage(message);
+// return await isSignatureCorrect(context, address, msgHash, signature);
+// }
+
+// /**
+// * Returns whether the account abstraction EIP712 signature is correct.
+// *
+// * @param context The web3 context.
+// * @param address The sender address.
+// * @param domain The domain data.
+// * @param types A map of records pointing from field name to field type.
+// * @param value A single record value.
+// * @param signature The Ethers signature.
+// *
+// * @example
+// *
+// * import { Wallet, utils, Provider, EIP712Signer } from "zksync-ethers";
+// *
+// * const ADDRESS = "";
+// * const PRIVATE_KEY = "";
+// * const context = Provider.getDefaultProvider(types.Network.Sepolia);
+// *
+// * const tx: types.TransactionRequest = {
+// * type: 113,
+// * chainId: 270,
+// * from: ADDRESS,
+// * to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618",
+// * value: BigInt(7_000_000),
+// * };
+// *
+// * const eip712Signer = new EIP712Signer(
+// * new Wallet(PRIVATE_KEY), // or new ethers.Wallet(PRIVATE_KEY),
+// * Number((await context.getNetwork()).chainId)
+// * );
+// *
+// * const signature = await eip712Signer.sign(tx);
+// *
+// * const isValidSignature = await utils.isTypedDataSignatureCorrect(context, ADDRESS, await eip712Signer.getDomain(), utils.EIP712_TYPES, EIP712Signer.getSignInput(tx), signature);
+// * // isValidSignature = true
+// */
+// export async function isTypedDataSignatureCorrect(
+// context: web3.Web3Context, // or maybe use RpcMethods?
+// address: string,
+// domain: ethers.TypedDataDomain,
+// types: Record>,
+// value: Record,
+// signature: EthereumSignature,
+// ): Promise {
+// const msgHash = ethers.TypedDataEncoder.hash(domain, types, value);
+// return await isSignatureCorrect(context, address, msgHash, signature);
+// }
+
+// /**
+// * Returns an estimation of the L2 gas required for token bridging via the default ERC20 bridge.
+// *
+// * @param providerL1 The Ethers provider for the L1 network.
+// * @param providerL2 The zkSync provider for the L2 network.
+// * @param token The address of the token to be bridged.
+// * @param amount The deposit amount.
+// * @param to The recipient address on the L2 network.
+// * @param from The sender address on the L1 network.
+// * @param gasPerPubdataByte The current gas per byte of pubdata.
+// *
+// * @see
+// * {@link https://docs.zksync.io/build/developer-reference/bridging-asset.html#default-bridges Default bridges documentation}.
+// *
+// * @example
+// *
+// *
+// */
+// export async function estimateDefaultBridgeDepositL2Gas(
+// providerL1: Web3Eth,
+// providerL2: Provider,
+// token: web3.Address,
+// amount: web3Types.Numbers,
+// to: web3.Address,
+// from?: web3.Address,
+// gasPerPubdataByte?: web3Types.Numbers,
+// ): Promise {
+// // If the `from` address is not provided, we use a random address, because
+// // due to storage slot aggregation, the gas estimation will depend on the address
+// // and so estimation for the zero address may be smaller than for the sender.
+// from ??= web3Accounts.create().address;
+// if (await providerL2.isBaseToken(token)) {
+// return await providerL2.estimateL1ToL2Execute({
+// contractAddress: to,
+// gasPerPubdataByte: gasPerPubdataByte,
+// caller: from,
+// calldata: '0x',
+// l2Value: amount,
+// });
+// } else {
+// const bridgeAddresses = await providerL2.getDefaultBridgeAddresses();
+
+// const value = 0;
+// const l1BridgeAddress = bridgeAddresses.sharedL1;
+// const l2BridgeAddress = bridgeAddresses.sharedL2;
+// const bridgeData = await getERC20DefaultBridgeData(token, providerL1);
+
+// return await estimateCustomBridgeDepositL2Gas(
+// providerL2,
+// l1BridgeAddress,
+// l2BridgeAddress,
+// isAddressEq(token, LEGACY_ETH_ADDRESS) ? ETH_ADDRESS_IN_CONTRACTS : token,
+// amount,
+// to,
+// bridgeData,
+// from,
+// gasPerPubdataByte,
+// value,
+// );
+// }
+// }
+
+/**
+ * Scales the provided gas limit using a coefficient to ensure acceptance of L1->L2 transactions.
+ *
+ * This function adjusts the gas limit by multiplying it with a coefficient calculated from the
+ * `L1_FEE_ESTIMATION_COEF_NUMERATOR` and `L1_FEE_ESTIMATION_COEF_DENOMINATOR` constants.
+ *
+ * @param gasLimit - The gas limit to be scaled.
+ *
+ * @example
+ *
+ * const scaledGasLimit = utils.scaleGasLimit(10_000);
+ * // scaledGasLimit = 12_000
+ */
+export function scaleGasLimit(gasLimit: bigint): bigint {
+ return (
+ (gasLimit * BigInt(L1_FEE_ESTIMATION_COEF_NUMERATOR)) /
+ BigInt(L1_FEE_ESTIMATION_COEF_DENOMINATOR)
+ );
+}
+
+// /**
+// * Returns an estimation of the L2 gas required for token bridging via the custom ERC20 bridge.
+// *
+// * @param providerL2 The zkSync provider for the L2 network.
+// * @param l1BridgeAddress The address of the custom L1 bridge.
+// * @param l2BridgeAddress The address of the custom L2 bridge.
+// * @param token The address of the token to be bridged.
+// * @param amount The deposit amount.
+// * @param to The recipient address on the L2 network.
+// * @param bridgeData Additional bridge data.
+// * @param from The sender address on the L1 network.
+// * @param gasPerPubdataByte The current gas per byte of pubdata.
+// * @param l2Value The `msg.value` of L2 transaction.
+// *
+// * @see
+// * {@link https://docs.zksync.io/build/developer-reference/bridging-asset.html#custom-bridges-on-l1-and-l2 Custom bridges documentation}.
+// *
+// * @example
+// *
+// *
+// */
+// export async function estimateCustomBridgeDepositL2Gas(
+// providerL2: RpcMethods,
+// l1BridgeAddress: web3.Address,
+// l2BridgeAddress: web3.Address,
+// token: web3.Address,
+// amount: web3Types.Numbers,
+// to: web3.Address,
+// bridgeData: web3Types.Bytes,
+// from: web3.Address,
+// gasPerPubdataByte?: web3Types.Numbers,
+// l2Value?: web3Types.Numbers,
+// ): Promise {
+// const calldata = await getERC20BridgeCalldata(token, from, to, amount, bridgeData);
+// return await providerL2.estimateL1ToL2Execute({
+// caller: applyL1ToL2Alias(l1BridgeAddress),
+// contractAddress: l2BridgeAddress,
+// gasPerPubdataByte: gasPerPubdataByte,
+// calldata: calldata,
+// l2Value: l2Value,
+// });
+// }
+
+/**
+ * Creates a JSON string from an object, including support for serializing bigint types.
+ *
+ * @param object The object to serialize to JSON.
+ */
+export function toJSON(object: any): string {
+ return JSON.stringify(
+ object,
+ (_, value) => {
+ if (typeof value === 'bigint') {
+ return value.toString(); // Convert BigInt to string
+ }
+ return value;
+ },
+ 2,
+ );
+}
+
+/**
+ * Compares stringified addresses, taking into account the fact that
+ * addresses might be represented in different casing.
+ *
+ * @param a - The first address to compare.
+ * @param b - The second address to compare.
+ * @returns A boolean indicating whether the addresses are equal.
+ */
+export function isAddressEq(a: web3.Address, b: web3.Address): boolean {
+ return a.toLowerCase() === b.toLowerCase();
+}
diff --git a/yarn.lock b/yarn.lock
index 48c3688..05168b1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2988,6 +2988,11 @@ ethjs-util@0.1.6, ethjs-util@^0.1.6:
is-hex-prefixed "1.0.0"
strip-hex-prefix "1.0.0"
+eventemitter3@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4"
+ integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==
+
evp_bytestokey@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
@@ -5715,6 +5720,22 @@ web3-core@^4.3.2:
optionalDependencies:
web3-providers-ipc "^4.0.7"
+web3-core@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.4.0.tgz#f2cd48aecda5ec34170edf7470001f90fdea1dc6"
+ integrity sha512-sN1AkhTAFI89anOeCaO0c3GtiGeWtOGVc2tmTdQs2Rd14HuxLyDuLIF3/WwjtkDFRM2189uYy8HJJSWJvW2mYA==
+ dependencies:
+ web3-errors "^1.2.0"
+ web3-eth-accounts "^4.1.2"
+ web3-eth-iban "^4.0.7"
+ web3-providers-http "^4.1.0"
+ web3-providers-ws "^4.0.7"
+ web3-types "^1.6.0"
+ web3-utils "^4.3.0"
+ web3-validator "^2.0.6"
+ optionalDependencies:
+ web3-providers-ipc "^4.0.7"
+
web3-errors@^1.1.3, web3-errors@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/web3-errors/-/web3-errors-1.1.4.tgz#5667a0a5f66fc936e101ef32032ccc1e8ca4d5a1"
@@ -5722,6 +5743,13 @@ web3-errors@^1.1.3, web3-errors@^1.1.4:
dependencies:
web3-types "^1.3.1"
+web3-errors@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/web3-errors/-/web3-errors-1.2.0.tgz#441acfd7fd744c9beedf23f277f20759fae92433"
+ integrity sha512-58Kczou5zyjcm9LuSs5Hrm6VrG8t9p2J8X0yGArZrhKNPZL66gMGkOUpPx+EopE944Sk4yE+Q25hKv4H5BH+kA==
+ dependencies:
+ web3-types "^1.6.0"
+
web3-eth-abi@^4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.1.4.tgz#56ae7ebb1385db1a948e69fb35f4057bff6743af"
@@ -5744,6 +5772,17 @@ web3-eth-abi@^4.2.0:
web3-utils "^4.1.1"
web3-validator "^2.0.4"
+web3-eth-abi@^4.2.2:
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.2.2.tgz#d7592e2cc113fd34da3fb4c933537ddf8639d9b2"
+ integrity sha512-akbGi642UtKG3k3JuLbhl9KuG7LM/cXo/by2WfdwfOptGZrzRsWJNWje1d2xfw1n9kkVG9SAMvPJl1uSyR3dfw==
+ dependencies:
+ abitype "0.7.1"
+ web3-errors "^1.2.0"
+ web3-types "^1.6.0"
+ web3-utils "^4.3.0"
+ web3-validator "^2.0.6"
+
web3-eth-accounts@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-4.1.0.tgz#5b5e6c60d457e7b829ec590021fc87ada8585920"
@@ -5770,6 +5809,19 @@ web3-eth-accounts@^4.1.1:
web3-utils "^4.1.1"
web3-validator "^2.0.4"
+web3-eth-accounts@^4.1.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-4.1.2.tgz#652d6e3daf4d6cb3fe67cec6a878e768f6e8b8e8"
+ integrity sha512-y0JynDeTDnclyuE9mShXLeEj+BCrPHxPHOyPCgTchUBQsALF9+0OhP7WiS3IqUuu0Hle5bjG2f5ddeiPtNEuLg==
+ dependencies:
+ "@ethereumjs/rlp" "^4.0.1"
+ crc-32 "^1.2.2"
+ ethereum-cryptography "^2.0.0"
+ web3-errors "^1.1.4"
+ web3-types "^1.6.0"
+ web3-utils "^4.2.3"
+ web3-validator "^2.0.5"
+
web3-eth-contract@^4.1.2:
version "4.1.3"
resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-4.1.3.tgz#15dd4c978eaf0d8f894b2c1f3e9f94edd29ff57c"
@@ -5931,6 +5983,11 @@ web3-types@^1.3.0, web3-types@^1.3.1:
resolved "https://registry.yarnpkg.com/web3-types/-/web3-types-1.3.1.tgz#cf6148ad46b68c5c89714613380b270d31e297be"
integrity sha512-8fXi7h/t95VKRtgU4sxprLPZpsTh3jYDfSghshIDBgUD/OoGe5S+syP24SUzBZYllZ/L+hMr2gdp/0bGJa8pYQ==
+web3-types@^1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/web3-types/-/web3-types-1.6.0.tgz#ebe7f140c31f7cc0ad15f238ad7e7ac72797ff3b"
+ integrity sha512-qgOtADqlD5hw+KPKBUGaXAcdNLL0oh6qTeVgXwewCfbL/lG9R+/GrgMQB1gbTJ3cit8hMwtH8KX2Em6OwO0HRw==
+
web3-utils@^4.0.7:
version "4.0.7"
resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-4.0.7.tgz#7df497b7cdd06cdfe7d02036c45fecbe3382d137"
@@ -5951,6 +6008,17 @@ web3-utils@^4.1.0, web3-utils@^4.1.1:
web3-types "^1.3.1"
web3-validator "^2.0.4"
+web3-utils@^4.2.3, web3-utils@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-4.3.0.tgz#c18918f0d692f745d622d22172406f6102528860"
+ integrity sha512-fGG2IZr0XB1vEoWZiyJzoy28HpsIfZgz4mgPeQA9aj5rIx8z0o80qUPtIyrCYX/Bo2gYALlV5SWIJWxJNUQn9Q==
+ dependencies:
+ ethereum-cryptography "^2.0.0"
+ eventemitter3 "^5.0.1"
+ web3-errors "^1.2.0"
+ web3-types "^1.6.0"
+ web3-validator "^2.0.6"
+
web3-validator@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/web3-validator/-/web3-validator-2.0.3.tgz#e5dcd4b4902612cff21b7f8817dd233393999d97"
@@ -5973,6 +6041,17 @@ web3-validator@^2.0.4:
web3-types "^1.3.1"
zod "^3.21.4"
+web3-validator@^2.0.5, web3-validator@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/web3-validator/-/web3-validator-2.0.6.tgz#a0cdaa39e1d1708ece5fae155b034e29d6a19248"
+ integrity sha512-qn9id0/l1bWmvH4XfnG/JtGKKwut2Vokl6YXP5Kfg424npysmtRLe9DgiNBM9Op7QL/aSiaA0TVXibuIuWcizg==
+ dependencies:
+ ethereum-cryptography "^2.0.0"
+ util "^0.12.5"
+ web3-errors "^1.2.0"
+ web3-types "^1.6.0"
+ zod "^3.21.4"
+
web3@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/web3/-/web3-4.4.0.tgz#83e5906675608adf9a14841f257e441c9154a8c7"
From 5c999a7806c095d1c804f0655a0f898c480fb23d Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Tue, 28 May 2024 05:53:03 +0200
Subject: [PATCH 04/30] update a test
---
test/index.test.ts | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/test/index.test.ts b/test/index.test.ts
index c23ce91..4855aef 100644
--- a/test/index.test.ts
+++ b/test/index.test.ts
@@ -1,10 +1,10 @@
-import { core } from 'web3';
+import { Web3 } from 'web3';
import { ZkSyncPlugin } from '../src';
describe('ZkSyncPlugin tests', () => {
it('should register ZkSync plugin on Web3Context instance', () => {
- const web3Context = new core.Web3Context('http://127.0.0.1:8545');
- web3Context.registerPlugin(new ZkSyncPlugin());
- expect(web3Context.zkSync).toBeDefined();
+ const web3 = new Web3('http://127.0.0.1:8545');
+ web3.registerPlugin(new ZkSyncPlugin());
+ expect(web3.zkSync).toBeDefined();
});
});
From 5c57acf02ba2bf0b0c086497721dbb0f9ebbd810 Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Tue, 28 May 2024 06:21:26 +0200
Subject: [PATCH 05/30] fix spelling
---
src/utils.ts | 30 ++++++++++++++++++------------
1 file changed, 18 insertions(+), 12 deletions(-)
diff --git a/src/utils.ts b/src/utils.ts
index c77ddfa..8c4c716 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -240,10 +240,11 @@ export const DEFAULT_GAS_PER_PUBDATA_LIMIT = 50_000;
export const REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT = 800;
/**
+ * ------------------------------------------------------------
* consider adding the next few functions to web3.js:
* */
-export const NumberToByes = (number: web3Types.Numbers) =>
+export const NumberToBytes = (number: web3Types.Numbers) =>
web3Utils.hexToBytes(web3Utils.numberToHex(number));
export function concat(strings: web3Types.Bytes[]): string {
@@ -284,6 +285,11 @@ function recoverSignerAddress(
return recoveredAddress;
}
+/**
+ * ------------------------------------------------------------
+ * End of the function section that would be added to web3.js
+ */
+
/**
* Returns true if token represents ETH on L1 or L2.
*
@@ -526,30 +532,30 @@ export async function checkBaseCost(
// const maxPriorityFeePerGas = transaction.maxPriorityFeePerGas || maxFeePerGas;
// const fields: any[] = [
-// NumberToByes(transaction.nonce || 0),
-// NumberToByes(maxPriorityFeePerGas),
-// NumberToByes(maxFeePerGas),
-// NumberToByes(transaction.gasLimit || 0),
+// NumberToBytes(transaction.nonce || 0),
+// NumberToBytes(maxPriorityFeePerGas),
+// NumberToBytes(maxFeePerGas),
+// NumberToBytes(transaction.gasLimit || 0),
// transaction.to ? web3Utils.toChecksumAddress(transaction.to) : '0x',
-// NumberToByes(transaction.value || 0),
+// NumberToBytes(transaction.value || 0),
// transaction.data || '0x',
// ];
// if (signature) {
// const sig = ethers.Signature.from(signature);
-// fields.push(NumberToByes(sig.yParity));
-// fields.push(NumberToByes(sig.r));
-// fields.push(NumberToByes(sig.s));
+// fields.push(NumberToBytes(sig.yParity));
+// fields.push(NumberToBytes(sig.r));
+// fields.push(NumberToBytes(sig.s));
// } else {
-// fields.push(NumberToByes(transaction.chainId));
+// fields.push(NumberToBytes(transaction.chainId));
// fields.push('0x');
// fields.push('0x');
// }
-// fields.push(NumberToByes(transaction.chainId));
+// fields.push(NumberToBytes(transaction.chainId));
// fields.push(web3Utils.toChecksumAddress(from));
// // Add meta
-// fields.push(NumberToByes(meta.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT));
+// fields.push(NumberToBytes(meta.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT));
// fields.push((meta.factoryDeps ?? []).map(dep => web3Utils.toHex(dep)));
// if (meta.customSignature && web3Utils.bytesToUint8Array(meta.customSignature).length === 0) {
From c55f043b4c39550422038a004f1e24f236632bb4 Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Tue, 28 May 2024 06:38:05 +0200
Subject: [PATCH 06/30] tiny renaming
---
src/utils.ts | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/src/utils.ts b/src/utils.ts
index 8c4c716..9b1d320 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -244,11 +244,11 @@ export const REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT = 800;
* consider adding the next few functions to web3.js:
* */
-export const NumberToBytes = (number: web3Types.Numbers) =>
+export const numberToBytes = (number: web3Types.Numbers) =>
web3Utils.hexToBytes(web3Utils.numberToHex(number));
-export function concat(strings: web3Types.Bytes[]): string {
- return '0x' + strings.map(d => web3Utils.toHex(d).substring(2)).join('');
+export function concat(bytes: web3Types.Bytes[]): string {
+ return '0x' + bytes.map(d => web3Utils.toHex(d).substring(2)).join('');
}
export function contractFunctionId(value: string): string {
@@ -532,30 +532,30 @@ export async function checkBaseCost(
// const maxPriorityFeePerGas = transaction.maxPriorityFeePerGas || maxFeePerGas;
// const fields: any[] = [
-// NumberToBytes(transaction.nonce || 0),
-// NumberToBytes(maxPriorityFeePerGas),
-// NumberToBytes(maxFeePerGas),
-// NumberToBytes(transaction.gasLimit || 0),
+// numberToBytes(transaction.nonce || 0),
+// numberToBytes(maxPriorityFeePerGas),
+// numberToBytes(maxFeePerGas),
+// numberToBytes(transaction.gasLimit || 0),
// transaction.to ? web3Utils.toChecksumAddress(transaction.to) : '0x',
-// NumberToBytes(transaction.value || 0),
+// numberToBytes(transaction.value || 0),
// transaction.data || '0x',
// ];
// if (signature) {
// const sig = ethers.Signature.from(signature);
-// fields.push(NumberToBytes(sig.yParity));
-// fields.push(NumberToBytes(sig.r));
-// fields.push(NumberToBytes(sig.s));
+// fields.push(numberToBytes(sig.yParity));
+// fields.push(numberToBytes(sig.r));
+// fields.push(numberToBytes(sig.s));
// } else {
-// fields.push(NumberToBytes(transaction.chainId));
+// fields.push(numberToBytes(transaction.chainId));
// fields.push('0x');
// fields.push('0x');
// }
-// fields.push(NumberToBytes(transaction.chainId));
+// fields.push(numberToBytes(transaction.chainId));
// fields.push(web3Utils.toChecksumAddress(from));
// // Add meta
-// fields.push(NumberToBytes(meta.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT));
+// fields.push(numberToBytes(meta.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT));
// fields.push((meta.factoryDeps ?? []).map(dep => web3Utils.toHex(dep)));
// if (meta.customSignature && web3Utils.bytesToUint8Array(meta.customSignature).length === 0) {
From 3ba6c10dd1707f7c47335e90fdf97c9b438347f9 Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Tue, 28 May 2024 13:25:42 +0200
Subject: [PATCH 07/30] add unit test and apply padding fixes + organize test
files
---
src/index.ts | 5 +-
src/utils.ts | 24 +-
test/{ => integration}/mainnet.test.ts | 2 +-
test/{ => integration}/rpc.mainnet.test.ts | 4 +-
test/{ => integration}/rpc.test.ts | 8 +-
test/{ => unit}/index.test.ts | 4 +-
test/unit/utils.test.ts | 279 +++++++++++++++++++++
test/utils.ts | 13 +
8 files changed, 316 insertions(+), 23 deletions(-)
rename test/{ => integration}/mainnet.test.ts (95%)
rename test/{ => integration}/rpc.mainnet.test.ts (97%)
rename test/{ => integration}/rpc.test.ts (92%)
rename test/{ => unit}/index.test.ts (70%)
create mode 100644 test/unit/utils.test.ts
create mode 100644 test/utils.ts
diff --git a/src/index.ts b/src/index.ts
index 4098ae4..705e799 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,3 +1,4 @@
export { ZkSyncPlugin } from './plugin';
-export * from './types';
-export * from './constants';
+export * as utils from './utils';
+export * as types from './types';
+export * as constants from './constants';
diff --git a/src/utils.ts b/src/utils.ts
index 9b1d320..d3bdb9e 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -356,9 +356,9 @@ export function getHashedL2ToL1Msg(
const encodedMsg = new Uint8Array([
0, // l2ShardId
1, // isService
- ...web3Utils.hexToBytes(web3Utils.padLeft(web3Utils.toHex(txNumberInBlock), 2)),
+ ...web3Utils.hexToBytes(web3Utils.padLeft(web3Utils.toHex(txNumberInBlock), 2 * 2)),
...web3Utils.hexToBytes(L1_MESSENGER_ADDRESS),
- ...web3Utils.hexToBytes(web3Utils.padLeft(sender, 32)),
+ ...web3Utils.hexToBytes(web3Utils.padLeft(sender, 32 * 2)),
...web3Utils.hexToBytes(web3Utils.keccak256(msg)),
]);
@@ -424,7 +424,7 @@ export function create2Address(
const prefix = web3Utils.keccak256(web3Utils.utf8ToBytes('zksyncCreate2'));
const inputHash = web3Utils.keccak256(input);
const addressBytes = web3Utils
- .keccak256(concat([prefix, web3Utils.padLeft(sender, 32), salt, bytecodeHash, inputHash]))
+ .keccak256(concat([prefix, web3Utils.padLeft(sender, 32 * 2), salt, bytecodeHash, inputHash]))
.slice(26);
return web3Utils.toChecksumAddress(addressBytes);
}
@@ -446,8 +446,8 @@ export function createAddress(sender: web3.Address, senderNonce: web3Types.Numbe
.keccak256(
concat([
prefix,
- web3Utils.padLeft(sender, 32),
- web3Utils.padLeft(web3Utils.toHex(senderNonce), 32),
+ web3Utils.padLeft(sender, 32 * 2),
+ web3Utils.padLeft(web3Utils.toHex(senderNonce), 32 * 2),
]),
)
.slice(26);
@@ -605,8 +605,10 @@ export function hashBytecode(bytecode: web3Types.Bytes): Uint8Array {
throw new Error(`Bytecode can not be longer than ${MAX_BYTECODE_LEN_BYTES} bytes!`);
}
- const hashStr = sha256(Buffer.from(bytecodeAsArray));
+ const hashStr = web3Utils.toHex(sha256(Buffer.from(bytecodeAsArray)));
+ console.log('hashStr:', hashStr);
const hash = web3Utils.bytesToUint8Array(hashStr);
+ console.log('hash:', hash);
// Note that the length of the bytecode
// should be provided in 32-byte words.
@@ -618,7 +620,7 @@ export function hashBytecode(bytecode: web3Types.Bytes): Uint8Array {
// The bytecode should always take the first 2 bytes of the bytecode hash,
// so we pad it from the left in case the length is smaller than 2 bytes.
const bytecodeLengthPadded = web3Utils.bytesToUint8Array(
- web3Utils.padLeft(bytecodeLengthInWords, 2),
+ web3Utils.padLeft(bytecodeLengthInWords, 2 * 2),
);
const codeHashVersion = new Uint8Array([1, 0]);
@@ -757,8 +759,8 @@ export function getSignature(transaction: any, ethSignature?: EthereumSignature)
throw new Error('No signature provided!');
}
- const r = web3Utils.bytesToUint8Array(web3Utils.padLeft(web3Utils.toHex(ethSignature.r), 32));
- const s = web3Utils.bytesToUint8Array(web3Utils.padLeft(web3Utils.toHex(ethSignature.s), 32));
+ const r = web3Utils.bytesToUint8Array(web3Utils.padLeft(web3Utils.toHex(ethSignature.r), 32 * 2));
+ const s = web3Utils.bytesToUint8Array(web3Utils.padLeft(web3Utils.toHex(ethSignature.s), 32 * 2));
const v = ethSignature.v;
return new Uint8Array([...r, ...s, v]);
@@ -849,7 +851,7 @@ const ADDRESS_MODULO = 2n ** 160n;
export function applyL1ToL2Alias(address: string): string {
return web3Utils.padLeft(
web3Utils.toHex((BigInt(address) + BigInt(L1_TO_L2_ALIAS_OFFSET)) % ADDRESS_MODULO),
- 20,
+ 20 * 2,
);
}
@@ -872,7 +874,7 @@ export function undoL1ToL2Alias(address: string): string {
if (result < 0n) {
result += ADDRESS_MODULO;
}
- return web3Utils.padLeft(web3Utils.toHex(result), 20);
+ return web3Utils.padLeft(web3Utils.toHex(result), 20 * 2);
}
// /**
diff --git a/test/mainnet.test.ts b/test/integration/mainnet.test.ts
similarity index 95%
rename from test/mainnet.test.ts
rename to test/integration/mainnet.test.ts
index 96907f2..be37604 100644
--- a/test/mainnet.test.ts
+++ b/test/integration/mainnet.test.ts
@@ -1,5 +1,5 @@
import { Web3 } from 'web3';
-import { ZkSyncPlugin } from '../src';
+import { ZkSyncPlugin } from '../../src';
const EXAMPLE_ERC20_TOKEN = {
address: '0x3355df6D4c9C3035724Fd0e3914dE96A5a83aaf4',
diff --git a/test/rpc.mainnet.test.ts b/test/integration/rpc.mainnet.test.ts
similarity index 97%
rename from test/rpc.mainnet.test.ts
rename to test/integration/rpc.mainnet.test.ts
index 65ad652..559e422 100644
--- a/test/rpc.mainnet.test.ts
+++ b/test/integration/rpc.mainnet.test.ts
@@ -1,11 +1,11 @@
import { Web3 } from 'web3';
-import { ZkSyncPlugin } from '../src';
+import { ZkSyncPlugin } from '../../src';
import {
estimateData,
getL1BatchDetailsData,
getL2ToL1LogProofData,
getProofData,
-} from './fixtures';
+} from '../fixtures';
describe('ZkSyncPlugin rpc mainnet tests', () => {
let web3: Web3;
diff --git a/test/rpc.test.ts b/test/integration/rpc.test.ts
similarity index 92%
rename from test/rpc.test.ts
rename to test/integration/rpc.test.ts
index bb08fe7..f07553e 100644
--- a/test/rpc.test.ts
+++ b/test/integration/rpc.test.ts
@@ -1,11 +1,11 @@
import { Web3 } from 'web3';
-import { ZkSyncPlugin } from '../src';
+import { ZkSyncPlugin } from '../../src';
import {
getBlockDetailsData,
getBridgeContractsData,
getRawBlockTransactionsData,
getTransactionDetailsData,
-} from './fixtures';
+} from '../fixtures';
describe('ZkSyncPlugin rpc tests', () => {
let web3: Web3;
@@ -38,9 +38,7 @@ describe('ZkSyncPlugin rpc tests', () => {
expect(res).toBeDefined();
});
it('getRawBlockTransactions', async () => {
- const res = await web3.zkSync.rpc.getRawBlockTransactions(
- getRawBlockTransactionsData.input,
- );
+ const res = await web3.zkSync.rpc.getRawBlockTransactions(getRawBlockTransactionsData.input);
expect(res).toEqual(getRawBlockTransactionsData.output);
});
it('getMainContract', async () => {
diff --git a/test/index.test.ts b/test/unit/index.test.ts
similarity index 70%
rename from test/index.test.ts
rename to test/unit/index.test.ts
index 4855aef..a18b5a4 100644
--- a/test/index.test.ts
+++ b/test/unit/index.test.ts
@@ -1,9 +1,9 @@
import { Web3 } from 'web3';
-import { ZkSyncPlugin } from '../src';
+import { ZkSyncPlugin } from '../../src';
describe('ZkSyncPlugin tests', () => {
it('should register ZkSync plugin on Web3Context instance', () => {
- const web3 = new Web3('http://127.0.0.1:8545');
+ const web3 = new Web3('http://some-rpc-url.com');
web3.registerPlugin(new ZkSyncPlugin());
expect(web3.zkSync).toBeDefined();
});
diff --git a/test/unit/utils.test.ts b/test/unit/utils.test.ts
new file mode 100644
index 0000000..3e20363
--- /dev/null
+++ b/test/unit/utils.test.ts
@@ -0,0 +1,279 @@
+import {
+ // types,
+ utils,
+} from '../../src';
+import {
+ ADDRESS1,
+ // ADDRESS2
+} from '../utils';
+
+describe('utils', () => {
+ describe('#getHashedL2ToL1Msg()', () => {
+ it('should return a hashed L2 to L1 message', async () => {
+ const withdrawETHMessage =
+ '0x6c0960f936615cf349d7f6344891b1e7ca7c72883f5dc04900000000000000000000000000000000000000000000000000000001a13b8600';
+ const withdrawETHMessageHash =
+ '0x521bd25904766c83fe868d6a29cbffa011afd8a1754f6c9a52b053b693e42f18';
+ const result = utils.getHashedL2ToL1Msg(ADDRESS1, withdrawETHMessage, 0);
+ expect(result).toBe(withdrawETHMessageHash);
+ });
+ });
+
+ describe('#isETH()', () => {
+ it('should return true for legacy L1 ETH address', async () => {
+ const result = utils.isETH(utils.LEGACY_ETH_ADDRESS);
+ expect(result).toBeTruthy();
+ });
+
+ it('should return true for L1 ETH address', async () => {
+ const result = utils.isETH(utils.ETH_ADDRESS_IN_CONTRACTS);
+ expect(result).toBeTruthy();
+ });
+
+ it('should return true for L2 ETH address', async () => {
+ const result = utils.isETH(utils.L2_BASE_TOKEN_ADDRESS);
+ expect(result).toBeTruthy();
+ });
+ });
+
+ describe('#createAddress()', () => {
+ it('should return a correct address', async () => {
+ const address = utils.createAddress(ADDRESS1, 1);
+ expect(address).toBe('0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021');
+ });
+ });
+
+ describe('#create2Address()', () => {
+ it('should return a correct address', async () => {
+ const address = utils.create2Address(
+ ADDRESS1,
+ '0x010001cb6a6e8d5f6829522f19fa9568660e0a9cd53b2e8be4deb0a679452e41',
+ '0x01',
+ '0x01',
+ );
+ expect(address).toBe('0x29bac3E5E8FFE7415F97C956BFA106D70316ad50');
+ });
+ });
+
+ describe('#applyL1ToL2Alias()', () => {
+ it('should return the L2 contract address based on provided L1 contract address', async () => {
+ const l1ContractAddress = '0x702942B8205E5dEdCD3374E5f4419843adA76Eeb';
+ const l2ContractAddress = utils.applyL1ToL2Alias(l1ContractAddress);
+ expect(l2ContractAddress.toLowerCase()).toBe(
+ '0x813A42B8205E5DedCd3374e5f4419843ADa77FFC'.toLowerCase(),
+ );
+ });
+
+ it('should return the L2 contract address by padding zero to left', async () => {
+ const l1ContractAddress = '0xeeeeffffffffffffffffffffffffffffffffeeef';
+ const l2ContractAddress = utils.applyL1ToL2Alias(l1ContractAddress);
+ expect(l2ContractAddress.toLowerCase()).toBe(
+ '0x0000000000000000000000000000000000000000'.toLowerCase(),
+ );
+ });
+ });
+
+ describe('#undoL1ToL2Alias()', () => {
+ it('should return the L1 contract address based on provided L2 contract address', async () => {
+ const l2ContractAddress = '0x813A42B8205E5DedCd3374e5f4419843ADa77FFC';
+ const l1ContractAddress = utils.undoL1ToL2Alias(l2ContractAddress);
+ expect(l1ContractAddress.toLowerCase()).toBe(
+ '0x702942B8205E5dEdCD3374E5f4419843adA76Eeb'.toLowerCase(),
+ );
+ });
+
+ it('should return the L1 contract address by padding zero to left', async () => {
+ const l2ContractAddress = '0x1111000000000000000000000000000000001111';
+ const l1ContractAddress = utils.undoL1ToL2Alias(l2ContractAddress);
+ expect(l1ContractAddress.toLowerCase()).toBe(
+ '0x0000000000000000000000000000000000000000'.toLowerCase(),
+ );
+ });
+
+ it('should handle a case where L1_TO_L2_ALIAS_OFFSET is greater than the address', () => {
+ const l2ContractAddress = '0x100';
+ const l1ContractAddress = utils.undoL1ToL2Alias(l2ContractAddress);
+
+ expect(l1ContractAddress.toLowerCase()).toBe(
+ '0xeeeeffffffffffffffffffffffffffffffffefef'.toLowerCase(),
+ );
+ });
+ });
+
+ describe('#checkBaseCost()', () => {
+ it('should throw an error if the base cost bigger than value', async () => {
+ const baseCost = 100;
+ const value = 99;
+ try {
+ await utils.checkBaseCost(baseCost, value);
+ } catch (e) {
+ expect((e as Error).message).toBe(
+ `The base cost of performing the priority operation is higher than the provided value parameter for the transaction: baseCost: ${baseCost}, provided value: ${value}!`,
+ );
+ }
+ });
+ });
+
+ // describe('#serializeEip712()', () => {
+ // it('should throw an error when `tx.chainId` is not specified', async () => {
+ // try {
+ // utils.serializeEip712({});
+ // } catch (e) {
+ // expect((e as Error).message).toBe("Transaction chainId isn't set!");
+ // }
+ // });
+
+ // it('should throw an error when `tx.from` is not specified', async () => {
+ // try {
+ // utils.serializeEip712({ chainId: 270 });
+ // } catch (e) {
+ // expect((e as Error).message).toBe(
+ // 'Explicitly providing `from` field is required for EIP712 transactions!',
+ // );
+ // }
+ // });
+
+ // it('should throw an error when `tx.customData.customSignature` is empty string', async () => {
+ // try {
+ // utils.serializeEip712({
+ // chainId: 270,
+ // from: ADDRESS1,
+ // customData: {
+ // customSignature: '',
+ // },
+ // });
+ // } catch (e) {
+ // expect((e as Error).message).toBe('Empty signatures are not supported');
+ // }
+ // });
+
+ // it('should return a serialized transaction with populated default values', async () => {
+ // const tx =
+ // '0x71ea8080808080808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
+ // const result = utils.serializeEip712({
+ // chainId: 270,
+ // from: ADDRESS1,
+ // });
+ // expect(result).toBe(tx);
+ // });
+
+ // it('should return a serialized transaction with provided signature', async () => {
+ // const tx =
+ // '0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
+ // const signature = ethers.Signature.from(
+ // '0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a',
+ // );
+ // const result = utils.serializeEip712(
+ // {
+ // chainId: 270,
+ // from: ADDRESS1,
+ // to: ADDRESS2,
+ // value: 1_000_000,
+ // },
+ // signature,
+ // );
+ // expect(result).toBe(tx);
+ // });
+ // });
+
+ describe('#hashBytecode()', () => {
+ it('should return the hash of bytecode which length is not 2 bytes so padding needs to be performed', async () => {
+ const bytecode =
+ '0x000200000000000200010000000103550000006001100270000000130010019d0000008001000039000000400010043f0000000101200190000000290000c13d0000000001000031000000040110008c000000420000413d0000000101000367000000000101043b000000e001100270000000150210009c000000310000613d000000160110009c000000420000c13d0000000001000416000000000110004c000000420000c13d000000040100008a00000000011000310000001702000041000000200310008c000000000300001900000000030240190000001701100197000000000410004c000000000200a019000000170110009c00000000010300190000000001026019000000000110004c000000420000c13d00000004010000390000000101100367000000000101043b000000000010041b0000000001000019000000490001042e0000000001000416000000000110004c000000420000c13d0000002001000039000001000010044300000120000004430000001401000041000000490001042e0000000001000416000000000110004c000000420000c13d000000040100008a00000000011000310000001702000041000000000310004c000000000300001900000000030240190000001701100197000000000410004c000000000200a019000000170110009c00000000010300190000000001026019000000000110004c000000440000613d00000000010000190000004a00010430000000000100041a000000800010043f0000001801000041000000490001042e0000004800000432000000490001042e0000004a00010430000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0000000200000000000000000000000000000040000001000000000000000000000000000000000000000000000000000000000000000000000000006d4ce63c0000000000000000000000000000000000000000000000000000000060fe47b18000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000080000000000000000000000000000000000000000000000000000000000000000000000000000000009c8c8fa789967eb514f3ec9def748480945cc9b10fcbd1a19597d924eb201083';
+ const hashedBytecode = utils.hashBytecode(bytecode);
+ expect(hashedBytecode).toEqual(
+ new Uint8Array([
+ 1, 0, 0, 27, 57, 231, 154, 55, 0, 164, 201, 96, 244, 120, 23, 112, 54, 34, 224, 133, 160,
+ 122, 88, 164, 112, 80, 0, 134, 48, 138, 74, 16,
+ ]),
+ );
+ });
+
+ it('should return the hash of bytecode which length is 2 bytes so padding does not need to be performed', async () => {
+ const bytecode =
+ '0x0002000000000002000900000000000200010000000103550000006001100270000001980010019d0000008001000039000000400010043f0000000101200190000000340000c13d0000000001000031000000040110008c000003670000413d0000000101000367000000000101043b000000e0011002700000019d0210009c000001420000213d000001a50210009c000001720000213d000001a90210009c000001fd0000613d000001aa0210009c000002210000613d000001ab0110009c000003670000c13d0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000000310004c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d0000000201000039000000000101041a000000400200043d00000000001204350000019801000041000001980320009c00000000010240190000004001100210000001ad011001c70000065c0001042e0000000001000416000000000110004c000003670000c13d00000000020000310000001f01200039000000200a00008a0000000004a1016f000000400100043d0000000003140019000000000443004b00000000040000190000000104004039000001990530009c000003c90000213d0000000104400190000003c90000c13d000000400030043f0000001f0320018f00000001040003670000000505200272000000520000613d000000000600001900000005076002100000000008710019000000000774034f000000000707043b00000000007804350000000106600039000000000756004b0000004a0000413d000000000630004c000000610000613d0000000505500210000000000454034f00000000055100190000000303300210000000000605043300000000063601cf000000000636022f000000000404043b0000010003300089000000000434022f00000000033401cf000000000363019f00000000003504350000019a03000041000000600420008c000000000400001900000000040340190000019a05200197000000000650004c000000000300a0190000019a0550009c000000000304c019000000000330004c000003670000c13d0000000034010434000001990540009c000003670000213d000000000221001900000000041400190000001f054000390000019a06000041000000000725004b000000000700001900000000070680190000019a055001970000019a08200197000000000985004b0000000006008019000000000585013f0000019a0550009c00000000050700190000000005066019000000000550004c000003670000c13d0000000005040433000001990650009c000003c90000213d0000003f065000390000000006a6016f000000400b00043d00000000066b00190000000007b6004b00000000070000190000000107004039000001990860009c000003c90000213d0000000107700190000003c90000c13d000000400060043f000000000c5b043600000020065000390000000007460019000000000727004b000003670000213d000000000750004c0000009e0000613d000000000700001900000020077000390000000008b70019000000000947001900000000090904330000000000980435000000000857004b000000970000413d00000000046b001900000000000404350000000003030433000001990430009c000003670000213d00000000031300190000001f043000390000019a05000041000000000624004b000000000600001900000000060580190000019a044001970000019a07200197000000000874004b0000000005008019000000000474013f0000019a0440009c00000000040600190000000004056019000000000440004c000003670000c13d0000000004030433000001990540009c000003c90000213d0000003f054000390000000005a5016f000000400800043d0000000005580019000000000685004b00000000060000190000000106004039000001990750009c000003c90000213d0000000106600190000003c90000c13d000000400050043f0000000005480436000800000005001d00000020054000390000000006350019000000000226004b000003670000213d00060000000c001d00090000000b001d00070000000a001d000000000240004c000000d50000613d000000000200001900000020022000390000000006820019000000000732001900000000070704330000000000760435000000000642004b000000ce0000413d0000000002580019000000000002043500000040011000390000000001010433000500000001001d000000ff0110008c0000000901000029000003670000213d0000000001010433000400000001001d000001990110009c000003c90000213d000100000008001d0000000301000039000300000001001d000000000101041a000000010210019000000001011002700000007f0310018f0000000001036019000200000001001d0000001f0110008c00000000010000190000000101002039000000010110018f000000000112004b0000021b0000c13d0000000201000029000000200110008c000001100000413d0000000301000029000000000010043500000198010000410000000002000414000001980320009c0000000001024019000000c0011002100000019b011001c70000801002000039065b06560000040f0000000102200190000003670000613d00000004030000290000001f023000390000000502200270000000200330008c0000000002004019000000000301043b00000002010000290000001f01100039000000050110027000000000011300190000000002230019000000000312004b000001100000813d000000000002041b0000000102200039000000000312004b0000010c0000413d00000004010000290000001f0110008c000004990000a13d0000000301000029000000000010043500000198010000410000000002000414000001980320009c0000000001024019000000c0011002100000019b011001c70000801002000039065b06560000040f000000010220019000000007020000290000000906000029000003670000613d000000040300002900000000032301700000002002000039000000000101043b000001300000613d0000002002000039000000000400001900000000056200190000000005050433000000000051041b000000200220003900000001011000390000002004400039000000000534004b000001280000413d0000000404000029000000000343004b0000013e0000813d00000004030000290000000303300210000000f80330018f000000010400008a000000000334022f000000000343013f000000090400002900000000024200190000000002020433000000000232016f000000000021041b0000000401000029000000010110021000000001011001bf000004a70000013d0000019e0210009c000001c60000213d000001a20210009c000002440000613d000001a30210009c000002700000613d000001a40110009c000003670000c13d0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000000310004c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d0000000405000039000000000405041a000000010640019000000001014002700000007f0210018f00000000010260190000001f0210008c00000000020000190000000102002039000000000224013f00000001022001900000021b0000c13d000000400200043d0000000003120436000000000660004c000003800000c13d000001000500008a000000000454016f0000000000430435000000000110004c000000200400003900000000040060190000038d0000013d000001a60210009c000002940000613d000001a70210009c000002e30000613d000001a80110009c000003670000c13d0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000400310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000004010000390000000101100367000000000101043b000900000001001d000001ac0110009c000003670000213d0000000001000411000700000001001d00000000001004350000000101000039000800000001001d000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000003670000613d000000000101043b00000009020000290000000000200435000000200010043f00000024010000390000000101100367000000000101043b000600000001001d00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000003670000613d000000000101043b000000000101041a00000006020000290000000003210019000000000113004b000000000100001900000001010040390000000101100190000003ae0000c13d00000007010000290000000902000029065b05ea0000040f000000400100043d000000080200002900000000002104350000019802000041000001980310009c00000000010280190000004001100210000001ad011001c70000065c0001042e0000019f0210009c000002ff0000613d000001a00210009c000003510000613d000001a10110009c000003670000c13d0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000400310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000001020003670000000401200370000000000101043b000001ac0310009c000003670000213d0000002402200370000000000302043b000001ac0230009c000003670000213d00000000001004350000000101000039000000200010043f0000004002000039000900000002001d0000000001000019000800000003001d065b052b0000040f00000008020000290000000000200435000000200010043f00000000010000190000000902000029065b052b0000040f000000000101041a000000400200043d00000000001204350000019801000041000001980320009c00000000010240190000004001100210000001ad011001c70000065c0001042e0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000000310004c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d0000000303000039000000000203041a000000010420019000000001012002700000007f0510018f000000000601001900000000060560190000001f0560008c00000000050000190000000105002039000000000552013f0000000105500190000003690000613d000001b70100004100000000001004350000002201000039000000040010043f000001b8010000410000065d000104300000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000400310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000001010003670000000402100370000000000202043b000001ac0320009c000003670000213d0000002401100370000000000301043b0000000001000411065b05ea0000040f0000000101000039000000400200043d00000000001204350000019801000041000001980320009c00000000010240190000004001100210000001ad011001c70000065c0001042e0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000400310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000001010003670000000402100370000000000402043b000001ac0240009c000003670000213d0000002401100370000000000501043b000000000140004c000003a60000c13d000000400100043d0000004402100039000001b503000041000000000032043500000024021000390000001f030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b6011001c70000065d000104300000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000200310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000004010000390000000101100367000000000101043b000001ac0210009c000003670000213d0000000000100435000000200000043f00000040020000390000000001000019065b052b0000040f000000000101041a000000400200043d00000000001204350000019801000041000001980320009c00000000010240190000004001100210000001ad011001c70000065c0001042e0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000600310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000001010003670000000402100370000000000402043b000001ac0240009c000003670000213d0000002402100370000000000202043b000900000002001d000001ac0220009c000003670000213d0000004401100370000000000101043b000700000001001d00000000004004350000000101000039000600000001001d000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039000800000004001d065b06560000040f0000000102200190000003670000613d000000000101043b0000000002000411000500000002001d0000000000200435000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f00000008030000290000000102200190000003670000613d000000000101043b000000000201041a000000010100008a000000000112004b0000041c0000c13d000000000103001900000009020000290000000703000029065b05570000040f000000400100043d000000060200002900000000002104350000019802000041000001980310009c00000000010280190000004001100210000001ad011001c70000065c0001042e0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000000310004c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d0000000501000039000000000101041a000000ff0110018f000000400200043d00000000001204350000019801000041000001980320009c00000000010240190000004001100210000001ad011001c70000065c0001042e0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000400310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000001010003670000000402100370000000000202043b000900000002001d000001ac0220009c000003670000213d0000002401100370000000000101043b000800000001001d0000000001000411000600000001001d00000000001004350000000101000039000700000001001d000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000003670000613d000000000101043b00000009020000290000000000200435000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000003670000613d000000000101043b000000000101041a0000000803000029000000000231004b0000040f0000813d000000400100043d0000006402100039000001af0300004100000000003204350000004402100039000001b0030000410000000000320435000000240210003900000025030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d000104300000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000400310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000001010003670000000402100370000000000202043b000001ac0320009c000003730000a13d00000000010000190000065d00010430000000800060043f000000000440004c000003b40000c13d000001000300008a000000000232016f000000a00020043f000000000160004c000000c001000039000000a001006039000003c30000013d0000002401100370000000000301043b0000000001000411065b05570000040f0000000101000039000000400200043d00000000001204350000019801000041000001980320009c00000000010240190000004001100210000001ad011001c70000065c0001042e0000000000500435000000000410004c00000000040000190000038d0000613d000001b30500004100000000040000190000000006430019000000000705041a000000000076043500000001055000390000002004400039000000000614004b000003860000413d0000003f01400039000000200300008a000000000331016f0000000001230019000000000331004b00000000040000190000000104004039000001990310009c000003c90000213d0000000103400190000003c90000c13d000000400010043f000900000001001d065b05410000040f000000090400002900000000014100490000019802000041000001980310009c0000000001028019000001980340009c000000000204401900000040022002100000006001100210000000000121019f0000065c0001042e0000000201000039000000000301041a0000000002530019000000000332004b000000000300001900000001030040390000000103300190000003de0000613d000001b70100004100000000001004350000001101000039000000040010043f000001b8010000410000065d000104300000000000300435000000a001000039000000000260004c000003cf0000613d000001bf0200004100000000040000190000000003040019000000000402041a000000a005300039000000000045043500000001022000390000002004300039000000000564004b000003ba0000413d000000c0013000390000001f01100039000000200200008a000000000121016f000001c002100041000001c10220009c000003cf0000813d000001b70100004100000000001004350000004101000039000000040010043f000001b8010000410000065d00010430000900000001001d000000400010043f0000008002000039065b05410000040f000000090400002900000000014100490000019802000041000001980310009c0000000001028019000001980340009c000000000204401900000040022002100000006001100210000000000121019f0000065c0001042e000800000005001d000000000021041b0000000000400435000000200000043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039000900000004001d065b06560000040f00000009060000290000000102200190000003670000613d000000000101043b000000000201041a00000008030000290000000002320019000000000021041b000000400100043d000000000031043500000198020000410000000003000414000001980430009c0000000003028019000001980410009c00000000010280190000004001100210000000c002300210000000000112019f0000019b011001c70000800d020000390000000303000039000001b4040000410000000005000019065b06510000040f0000000101200190000003670000613d000000400100043d000000010200003900000000002104350000019802000041000001980310009c00000000010280190000004001100210000001ad011001c70000065c0001042e000000000331004900000006010000290000000902000029065b05ea0000040f000000400100043d000000070200002900000000002104350000019802000041000001980310009c00000000010280190000004001100210000001ad011001c70000065c0001042e0000000701000029000000000112004b000004310000813d000000400100043d0000004402100039000001be03000041000000000032043500000024021000390000001d030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b6011001c70000065d00010430000400000002001d000000000130004c000004490000c13d000000400100043d0000006402100039000001bc0300004100000000003204350000004402100039000001bd030000410000000000320435000000240210003900000024030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d000104300000000501000029000001ac01100198000500000001001d000004620000c13d000000400100043d0000006402100039000001ba0300004100000000003204350000004402100039000001bb030000410000000000320435000000240210003900000022030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d00010430000000080100002900000000001004350000000601000029000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000003670000613d000000000101043b00000005020000290000000000200435000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f00000004030000290000000102200190000003670000613d00000007020000290000000002230049000000000101043b000000000021041b000000400100043d000000000021043500000198020000410000000003000414000001980430009c0000000003028019000001980410009c00000000010280190000004001100210000000c002300210000000000112019f0000019b011001c70000800d020000390000000303000039000001b90400004100000008050000290000000506000029065b06510000040f00000008030000290000000101200190000002d60000c13d000003670000013d0000000401000029000000000110004c00000000010000190000049f0000613d0000000601000029000000000101043300000004040000290000000302400210000000010300008a000000000223022f000000000232013f000000000121016f0000000102400210000000000121019f0000000302000029000000000012041b00000001010000290000000001010433000900000001001d000001990110009c000003c90000213d0000000401000039000600000001001d000000000101041a000000010210019000000001021002700000007f0320018f0000000002036019000400000002001d0000001f0220008c00000000020000190000000102002039000000000121013f00000001011001900000021b0000c13d0000000401000029000000200110008c000004dc0000413d0000000601000029000000000010043500000198010000410000000002000414000001980320009c0000000001024019000000c0011002100000019b011001c70000801002000039065b06560000040f0000000102200190000003670000613d00000009030000290000001f023000390000000502200270000000200330008c0000000002004019000000000301043b00000004010000290000001f01100039000000050110027000000000011300190000000002230019000000000312004b000004dc0000813d000000000002041b0000000102200039000000000312004b000004d80000413d00000009010000290000001f0110008c0000050e0000a13d0000000601000029000000000010043500000198010000410000000002000414000001980320009c0000000001024019000000c0011002100000019b011001c70000801002000039065b06560000040f000000010220019000000007020000290000000106000029000003670000613d000000090300002900000000032301700000002002000039000000000101043b000004fc0000613d0000002002000039000000000400001900000000056200190000000005050433000000000051041b000000200220003900000001011000390000002004400039000000000534004b000004f40000413d0000000904000029000000000343004b0000050a0000813d00000009030000290000000303300210000000f80330018f000000010400008a000000000334022f000000000343013f000000010400002900000000024200190000000002020433000000000232016f000000000021041b0000000101000039000000090200002900000001022002100000051b0000013d0000000901000029000000000110004c0000000001000019000005140000613d0000000801000029000000000101043300000009040000290000000302400210000000010300008a000000000223022f000000000232013f000000000221016f0000000101400210000000000112019f0000000602000029000000000012041b0000000501000039000000000201041a000001000300008a000000000232016f0000000503000029000000ff0330018f000000000232019f000000000021041b0000002001000039000001000010044300000120000004430000019c010000410000065c0001042e0000019803000041000001980410009c00000000010380190000004001100210000001980420009c00000000020380190000006002200210000000000112019f0000000002000414000001980420009c0000000002038019000000c002200210000000000112019f000001c2011001c70000801002000039065b06560000040f00000001022001900000053f0000613d000000000101043b000000000001042d00000000010000190000065d0001043000000020030000390000000004310436000000000302043300000000003404350000004001100039000000000430004c000005500000613d000000000400001900000000054100190000002004400039000000000624001900000000060604330000000000650435000000000534004b000005490000413d000000000231001900000000000204350000001f02300039000000200300008a000000000232016f0000000001210019000000000001042d0004000000000002000400000003001d000001ac01100198000005ab0000613d000001ac02200198000200000002001d000005c00000613d000300000001001d0000000000100435000000200000043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000005a90000613d000000000101043b000000000201041a0000000401000029000100000002001d000000000112004b000005d50000413d00000003010000290000000000100435000000200000043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000005a90000613d000000040200002900000001030000290000000002230049000000000101043b000000000021041b0000000201000029000000000010043500000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000005a90000613d000000000101043b000000000201041a00000004030000290000000002320019000000000021041b000000400100043d000000000031043500000198020000410000000003000414000001980430009c0000000003028019000001980410009c00000000010280190000004001100210000000c002300210000000000112019f0000019b011001c70000800d020000390000000303000039000001b40400004100000003050000290000000206000029065b06510000040f0000000101200190000005a90000613d000000000001042d00000000010000190000065d00010430000000400100043d0000006402100039000001c70300004100000000003204350000004402100039000001c8030000410000000000320435000000240210003900000025030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d00010430000000400100043d0000006402100039000001c50300004100000000003204350000004402100039000001c6030000410000000000320435000000240210003900000023030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d00010430000000400100043d0000006402100039000001c30300004100000000003204350000004402100039000001c4030000410000000000320435000000240210003900000026030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d000104300003000000000002000001ac01100198000006270000613d000200000003001d000001ac02200198000300000002001d0000063c0000613d000100000001001d00000000001004350000000101000039000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f00000001022001900000000304000029000006250000613d000000000101043b0000000000400435000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f00000003060000290000000102200190000006250000613d000000000101043b0000000202000029000000000021041b000000400100043d000000000021043500000198020000410000000003000414000001980430009c0000000003028019000001980410009c00000000010280190000004001100210000000c002300210000000000112019f0000019b011001c70000800d020000390000000303000039000001b9040000410000000105000029065b06510000040f0000000101200190000006250000613d000000000001042d00000000010000190000065d00010430000000400100043d0000006402100039000001bc0300004100000000003204350000004402100039000001bd030000410000000000320435000000240210003900000024030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d00010430000000400100043d0000006402100039000001ba0300004100000000003204350000004402100039000001bb030000410000000000320435000000240210003900000022030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d0001043000000654002104210000000102000039000000000001042d0000000002000019000000000001042d00000659002104230000000102000039000000000001042d0000000002000019000000000001042d0000065b000004320000065c0001042e0000065d000104300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff000000000000000000000000000000000000000000000000ffffffffffffffff8000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000002000000000000000000000000000000002000000000000000000000000000000400000010000000000000000000000000000000000000000000000000000000000000000000000000040c10f1800000000000000000000000000000000000000000000000000000000a457c2d600000000000000000000000000000000000000000000000000000000a457c2d700000000000000000000000000000000000000000000000000000000a9059cbb00000000000000000000000000000000000000000000000000000000dd62ed3e0000000000000000000000000000000000000000000000000000000040c10f190000000000000000000000000000000000000000000000000000000070a082310000000000000000000000000000000000000000000000000000000095d89b410000000000000000000000000000000000000000000000000000000023b872dc0000000000000000000000000000000000000000000000000000000023b872dd00000000000000000000000000000000000000000000000000000000313ce56700000000000000000000000000000000000000000000000000000000395093510000000000000000000000000000000000000000000000000000000006fdde0300000000000000000000000000000000000000000000000000000000095ea7b30000000000000000000000000000000000000000000000000000000018160ddd000000000000000000000000ffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000200000000000000000000000000200000000000000000000000000000000000040000000000000000000000000207a65726f00000000000000000000000000000000000000000000000000000045524332303a2064656372656173656420616c6c6f77616e63652062656c6f7708c379a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000840000000000000000000000008a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19bddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef45524332303a206d696e7420746f20746865207a65726f20616464726573730000000000000000000000000000000000000000640000000000000000000000004e487b710000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000240000000000000000000000008c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925737300000000000000000000000000000000000000000000000000000000000045524332303a20617070726f766520746f20746865207a65726f206164647265726573730000000000000000000000000000000000000000000000000000000045524332303a20617070726f76652066726f6d20746865207a65726f2061646445524332303a20696e73756666696369656e7420616c6c6f77616e6365000000c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85bffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff00000000000000800200000000000000000000000000000000000000000000000000000000000000616c616e6365000000000000000000000000000000000000000000000000000045524332303a207472616e7366657220616d6f756e7420657863656564732062657373000000000000000000000000000000000000000000000000000000000045524332303a207472616e7366657220746f20746865207a65726f2061646472647265737300000000000000000000000000000000000000000000000000000045524332303a207472616e736665722066726f6d20746865207a65726f206164000000000000000000000000000000000000000000000000000000000000000018469939d00da7016fd24775544e09a6a1ad29697146a060aa4a0baa144c2ede';
+ const hashedBytecode = utils.hashBytecode(bytecode);
+ expect(hashedBytecode).toEqual(
+ new Uint8Array([
+ 1, 0, 1, 203, 106, 110, 141, 95, 104, 41, 82, 47, 25, 250, 149, 104, 102, 14, 10, 156,
+ 213, 59, 46, 139, 228, 222, 176, 166, 121, 69, 46, 65,
+ ]),
+ );
+ });
+
+ it('should throw an error when bytecode is not divisible by 32', async () => {
+ try {
+ utils.hashBytecode('0x0002');
+ } catch (e) {
+ expect((e as Error).message).toBe('The bytecode length in bytes must be divisible by 32!');
+ }
+ });
+
+ it('should throw an error when bytecode is has even number of 32-byte words', async () => {
+ try {
+ utils.hashBytecode(`0x${'00020000000000020009000000000002'.repeat(2)}`);
+ } catch (e) {
+ expect((e as Error).message).toBe(
+ `Bytecode can not be longer than ${utils.MAX_BYTECODE_LEN_BYTES} bytes`,
+ );
+ }
+ });
+ });
+
+ // describe('#parseEip712()', () => {
+ // it('should parse a transaction with a signature', async () => {
+ // const tx: types.TransactionLike = {
+ // type: 113,
+ // nonce: 0,
+ // maxPriorityFeePerGas: 0n,
+ // maxFeePerGas: 0n,
+ // gasLimit: 0n,
+ // to: ADDRESS2,
+ // value: 1_000_000n,
+ // data: '0x',
+ // chainId: 270n,
+ // from: ADDRESS1,
+ // customData: {
+ // gasPerPubdata: 50_000n,
+ // factoryDeps: [],
+ // customSignature: '0x',
+ // paymasterParams: undefined,
+ // },
+ // hash: '0x9ed410ce33179ac1ff6b721060605afc72d64febfe0c08cacab5a246602131ee',
+ // };
+
+ // const serializedTx =
+ // '0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
+ // const result = utils.parseEip712(serializedTx);
+ // expect(result).toEqual(tx);
+ // });
+
+ // it('should parse a transaction without a signature', async () => {
+ // const tx: types.TransactionLike = {
+ // type: 113,
+ // nonce: 0,
+ // maxPriorityFeePerGas: 0n,
+ // maxFeePerGas: 0n,
+ // gasLimit: 0n,
+ // to: ADDRESS2,
+ // value: 0n,
+ // data: '0x',
+ // chainId: 270n,
+ // from: ADDRESS1,
+ // customData: {
+ // gasPerPubdata: 50_000n,
+ // factoryDeps: [],
+ // customSignature: '0x',
+ // paymasterParams: undefined,
+ // },
+ // hash: '0x7d3aab3e3d06d6a702228d911c2a9afaccddd52514fb89dc9d0ff81a67bfff04',
+ // };
+
+ // const serializedTx =
+ // '0x71f83e8080808094a61464658afeaf65cccaafd3a512b69a83b77618808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
+
+ // const result = utils.parseEip712(serializedTx);
+ // expect(result).toEqual(tx);
+ // });
+ // });
+});
diff --git a/test/utils.ts b/test/utils.ts
new file mode 100644
index 0000000..0a72a7d
--- /dev/null
+++ b/test/utils.ts
@@ -0,0 +1,13 @@
+export const ADDRESS1 = '0x36615Cf349d7F6344891B1e7CA7C72883F5dc049';
+export const PRIVATE_KEY1 =
+ '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110';
+export const MNEMONIC1 =
+ 'stuff slice staff easily soup parent arm payment cotton trade scatter struggle';
+export const ADDRESS2 = '0xa61464658AfeAf65CccaaFD3a512b69A83B77618';
+export const PRIVATE_KEY2 =
+ '0xac1e735be8536c6534bb4f17f06f6afc73b2b5ba84ac2cfb12f7461b20c0bbe3';
+export const DAI_L1 = '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55';
+export const APPROVAL_TOKEN = '0x841c43Fa5d8fFfdB9efE3358906f7578d8700Dd4'; // Crown token
+export const PAYMASTER = '0xa222f0c183AFA73a8Bc1AFb48D34C88c9Bf7A174'; // Crown token paymaster
+
+export const IS_ETH_BASED = true;
From a3430636a9fba44c54961bd2e89718aafa09917c Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Tue, 28 May 2024 13:31:54 +0200
Subject: [PATCH 08/30] move constants to a separate file
---
src/constants.ts | 157 +++++++++++++++++++++++++++++++++++++--
src/utils.ts | 158 +++-------------------------------------
test/unit/utils.test.ts | 9 ++-
3 files changed, 167 insertions(+), 157 deletions(-)
diff --git a/src/constants.ts b/src/constants.ts
index f475531..739a3b9 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -1,8 +1,151 @@
-export const ETH_ADDRESS = '0x0000000000000000000000000000000000000000';
+import * as web3Types from 'web3-types';
+
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
-export const BOOTLOADER_FORMAL_ADDRESS = '0x0000000000000000000000000000000000008001';
-export const CONTRACT_DEPLOYER_ADDRESS = '0x0000000000000000000000000000000000008006';
-export const L1_MESSENGER_ADDRESS = '0x0000000000000000000000000000000000008008';
-export const L2_ETH_TOKEN_ADDRESS = '0x000000000000000000000000000000000000800a';
-export const NONCE_HOLDER_ADDRESS = '0x0000000000000000000000000000000000008003';
-export const L1_TO_L2_ALIAS_OFFSET = '0x1111000000000000000000000000000000001111';
+
+/**
+ * The address of the L1 `ETH` token.
+ * @constant
+ */
+export const ETH_ADDRESS: web3Types.Address = '0x0000000000000000000000000000000000000000';
+
+/**
+ * The address of the L1 `ETH` token.
+ * @constant
+ */
+export const LEGACY_ETH_ADDRESS: web3Types.Address = '0x0000000000000000000000000000000000000000';
+
+/**
+ * In the contracts the zero address can not be used, use one instead
+ * @constant
+ */
+export const ETH_ADDRESS_IN_CONTRACTS: web3Types.Address =
+ '0x0000000000000000000000000000000000000001';
+
+/**
+ * The formal address for the `Bootloader`.
+ * @constant
+ */
+export const BOOTLOADER_FORMAL_ADDRESS: web3Types.Address =
+ '0x0000000000000000000000000000000000008001';
+
+/**
+ * The address of the Contract deployer.
+ * @constant
+ */
+export const CONTRACT_DEPLOYER_ADDRESS: web3Types.Address =
+ '0x0000000000000000000000000000000000008006';
+
+/**
+ * The address of the L1 messenger.
+ * @constant
+ */
+export const L1_MESSENGER_ADDRESS: web3Types.Address = '0x0000000000000000000000000000000000008008';
+
+/**
+ * The address of the L2 `ETH` token.
+ * @constant
+ * @deprecated In favor of {@link L2_BASE_TOKEN_ADDRESS}.
+ */
+export const L2_ETH_TOKEN_ADDRESS: web3Types.Address = '0x000000000000000000000000000000000000800a';
+
+/**
+ * The address of the base token.
+ * @constant
+ */
+export const L2_BASE_TOKEN_ADDRESS = '0x000000000000000000000000000000000000800a';
+
+/**
+ * The address of the Nonce holder.
+ * @constant
+ */
+export const NONCE_HOLDER_ADDRESS: web3Types.Address = '0x0000000000000000000000000000000000008003';
+
+/**
+ * Used for applying and undoing aliases on addresses during bridging from L1 to L2.
+ * @constant
+ */
+export const L1_TO_L2_ALIAS_OFFSET: web3Types.Address =
+ '0x1111000000000000000000000000000000001111';
+
+/**
+ * The EIP1271 magic value used for signature validation in smart contracts.
+ * This predefined constant serves as a standardized indicator to signal successful
+ * signature validation by the contract.
+ *
+ * @constant
+ */
+export const EIP1271_MAGIC_VALUE = '0x1626ba7e';
+
+/**
+ * Represents an EIP712 transaction type.
+ *
+ * @constant
+ */
+export const EIP712_TX_TYPE = 0x71;
+
+/**
+ * Represents a priority transaction operation on L2.
+ *
+ * @constant
+ */
+export const PRIORITY_OPERATION_L2_TX_TYPE = 0xff;
+
+/**
+ * The maximum bytecode length in bytes that can be deployed.
+ *
+ * @constant
+ */
+export const MAX_BYTECODE_LEN_BYTES: number = ((1 << 16) - 1) * 32;
+
+/**
+ * Numerator used in scaling the gas limit to ensure acceptance of `L1->L2` transactions.
+ *
+ * This constant is part of a coefficient calculation to adjust the gas limit to account for variations
+ * in the SDK estimation, ensuring the transaction will be accepted.
+ *
+ * @constant
+ */
+export const L1_FEE_ESTIMATION_COEF_NUMERATOR = 12;
+
+/**
+ * Denominator used in scaling the gas limit to ensure acceptance of `L1->L2` transactions.
+ *
+ * This constant is part of a coefficient calculation to adjust the gas limit to account for variations
+ * in the SDK estimation, ensuring the transaction will be accepted.
+ *
+ * @constant
+ */
+export const L1_FEE_ESTIMATION_COEF_DENOMINATOR = 10;
+
+/**
+ * Gas limit used for displaying the error messages when the
+ * users do not have enough fee when depositing ERC20 token from L1 to L2.
+ *
+ * @constant
+ */
+export const L1_RECOMMENDED_MIN_ERC20_DEPOSIT_GAS_LIMIT = 400_000;
+
+/**
+ * Gas limit used for displaying the error messages when the
+ * users do not have enough fee when depositing `ETH` token from L1 to L2.
+ *
+ * @constant
+ */
+export const L1_RECOMMENDED_MIN_ETH_DEPOSIT_GAS_LIMIT = 200_000;
+
+/**
+ * Default gas per pubdata byte for L2 transactions.
+ * This value is utilized when inserting a default value for type 2
+ * and EIP712 type transactions.
+ *
+ * @constant
+ */
+// It is a realistic value, but it is large enough to fill into any batch regardless of the pubdata price.
+export const DEFAULT_GAS_PER_PUBDATA_LIMIT = 50_000;
+
+/**
+ * The `L1->L2` transactions are required to have the following gas per pubdata byte.
+ *
+ * @constant
+ */
+export const REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT = 800;
diff --git a/src/utils.ts b/src/utils.ts
index d3bdb9e..3177fdb 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -34,6 +34,18 @@ import { IERC1271ABI } from './contracts/IERC1271';
import { IL1BridgeABI } from './contracts/IL1ERC20Bridge';
import { IL2BridgeABI } from './contracts/IL2Bridge';
import { INonceHolderABI } from './contracts/INonceHolder';
+import {
+ LEGACY_ETH_ADDRESS,
+ L2_BASE_TOKEN_ADDRESS,
+ ETH_ADDRESS_IN_CONTRACTS,
+ L1_MESSENGER_ADDRESS,
+ CONTRACT_DEPLOYER_ADDRESS,
+ MAX_BYTECODE_LEN_BYTES,
+ L1_TO_L2_ALIAS_OFFSET,
+ EIP1271_MAGIC_VALUE,
+ L1_FEE_ESTIMATION_COEF_NUMERATOR,
+ L1_FEE_ESTIMATION_COEF_DENOMINATOR,
+} from './constants';
// import { RpcMethods } from './rpc.methods'; // to be used instead of the one at zksync-ethers: Provider from ./provider
@@ -95,150 +107,6 @@ export const L2Bridge = new web3.Contract(IL2BridgeABI);
*/
export const NonceHolderContract = new web3.Contract(INonceHolderABI);
-/**
- * The address of the L1 `ETH` token.
- * @constant
- */
-export const ETH_ADDRESS: web3.Address = '0x0000000000000000000000000000000000000000';
-
-/**
- * The address of the L1 `ETH` token.
- * @constant
- */
-export const LEGACY_ETH_ADDRESS: web3.Address = '0x0000000000000000000000000000000000000000';
-
-/**
- * In the contracts the zero address can not be used, use one instead
- * @constant
- */
-export const ETH_ADDRESS_IN_CONTRACTS: web3.Address = '0x0000000000000000000000000000000000000001';
-
-/**
- * The formal address for the `Bootloader`.
- * @constant
- */
-export const BOOTLOADER_FORMAL_ADDRESS: web3.Address = '0x0000000000000000000000000000000000008001';
-
-/**
- * The address of the Contract deployer.
- * @constant
- */
-export const CONTRACT_DEPLOYER_ADDRESS: web3.Address = '0x0000000000000000000000000000000000008006';
-
-/**
- * The address of the L1 messenger.
- * @constant
- */
-export const L1_MESSENGER_ADDRESS: web3.Address = '0x0000000000000000000000000000000000008008';
-
-/**
- * The address of the L2 `ETH` token.
- * @constant
- * @deprecated In favor of {@link L2_BASE_TOKEN_ADDRESS}.
- */
-export const L2_ETH_TOKEN_ADDRESS: web3.Address = '0x000000000000000000000000000000000000800a';
-
-/**
- * The address of the base token.
- * @constant
- */
-export const L2_BASE_TOKEN_ADDRESS = '0x000000000000000000000000000000000000800a';
-
-/**
- * The address of the Nonce holder.
- * @constant
- */
-export const NONCE_HOLDER_ADDRESS: web3.Address = '0x0000000000000000000000000000000000008003';
-
-/**
- * Used for applying and undoing aliases on addresses during bridging from L1 to L2.
- * @constant
- */
-export const L1_TO_L2_ALIAS_OFFSET: web3.Address = '0x1111000000000000000000000000000000001111';
-
-/**
- * The EIP1271 magic value used for signature validation in smart contracts.
- * This predefined constant serves as a standardized indicator to signal successful
- * signature validation by the contract.
- *
- * @constant
- */
-export const EIP1271_MAGIC_VALUE = '0x1626ba7e';
-
-/**
- * Represents an EIP712 transaction type.
- *
- * @constant
- */
-export const EIP712_TX_TYPE = 0x71;
-
-/**
- * Represents a priority transaction operation on L2.
- *
- * @constant
- */
-export const PRIORITY_OPERATION_L2_TX_TYPE = 0xff;
-
-/**
- * The maximum bytecode length in bytes that can be deployed.
- *
- * @constant
- */
-export const MAX_BYTECODE_LEN_BYTES: number = ((1 << 16) - 1) * 32;
-
-/**
- * Numerator used in scaling the gas limit to ensure acceptance of `L1->L2` transactions.
- *
- * This constant is part of a coefficient calculation to adjust the gas limit to account for variations
- * in the SDK estimation, ensuring the transaction will be accepted.
- *
- * @constant
- */
-export const L1_FEE_ESTIMATION_COEF_NUMERATOR = 12;
-
-/**
- * Denominator used in scaling the gas limit to ensure acceptance of `L1->L2` transactions.
- *
- * This constant is part of a coefficient calculation to adjust the gas limit to account for variations
- * in the SDK estimation, ensuring the transaction will be accepted.
- *
- * @constant
- */
-export const L1_FEE_ESTIMATION_COEF_DENOMINATOR = 10;
-
-/**
- * Gas limit used for displaying the error messages when the
- * users do not have enough fee when depositing ERC20 token from L1 to L2.
- *
- * @constant
- */
-export const L1_RECOMMENDED_MIN_ERC20_DEPOSIT_GAS_LIMIT = 400_000;
-
-/**
- * Gas limit used for displaying the error messages when the
- * users do not have enough fee when depositing `ETH` token from L1 to L2.
- *
- * @constant
- */
-export const L1_RECOMMENDED_MIN_ETH_DEPOSIT_GAS_LIMIT = 200_000;
-
-/**
- * Default gas per pubdata byte for L2 transactions.
- * This value is utilized when inserting a default value for type 2
- * and EIP712 type transactions.
- *
- * @constant
- */
-// It is a realistic value, but it is large enough to fill into any batch regardless of the pubdata price.
-export const DEFAULT_GAS_PER_PUBDATA_LIMIT = 50_000;
-
-/**
- * The `L1->L2` transactions are required to have the following gas per pubdata byte.
- *
- * @constant
- */
-export const REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT = 800;
-
/**
* ------------------------------------------------------------
* consider adding the next few functions to web3.js:
@@ -606,9 +474,7 @@ export function hashBytecode(bytecode: web3Types.Bytes): Uint8Array {
}
const hashStr = web3Utils.toHex(sha256(Buffer.from(bytecodeAsArray)));
- console.log('hashStr:', hashStr);
const hash = web3Utils.bytesToUint8Array(hashStr);
- console.log('hash:', hash);
// Note that the length of the bytecode
// should be provided in 32-byte words.
diff --git a/test/unit/utils.test.ts b/test/unit/utils.test.ts
index 3e20363..686865b 100644
--- a/test/unit/utils.test.ts
+++ b/test/unit/utils.test.ts
@@ -6,6 +6,7 @@ import {
ADDRESS1,
// ADDRESS2
} from '../utils';
+import * as constants from '../../src/constants';
describe('utils', () => {
describe('#getHashedL2ToL1Msg()', () => {
@@ -21,17 +22,17 @@ describe('utils', () => {
describe('#isETH()', () => {
it('should return true for legacy L1 ETH address', async () => {
- const result = utils.isETH(utils.LEGACY_ETH_ADDRESS);
+ const result = utils.isETH(constants.LEGACY_ETH_ADDRESS);
expect(result).toBeTruthy();
});
it('should return true for L1 ETH address', async () => {
- const result = utils.isETH(utils.ETH_ADDRESS_IN_CONTRACTS);
+ const result = utils.isETH(constants.ETH_ADDRESS_IN_CONTRACTS);
expect(result).toBeTruthy();
});
it('should return true for L2 ETH address', async () => {
- const result = utils.isETH(utils.L2_BASE_TOKEN_ADDRESS);
+ const result = utils.isETH(constants.L2_BASE_TOKEN_ADDRESS);
expect(result).toBeTruthy();
});
});
@@ -214,7 +215,7 @@ describe('utils', () => {
utils.hashBytecode(`0x${'00020000000000020009000000000002'.repeat(2)}`);
} catch (e) {
expect((e as Error).message).toBe(
- `Bytecode can not be longer than ${utils.MAX_BYTECODE_LEN_BYTES} bytes`,
+ `Bytecode can not be longer than ${constants.MAX_BYTECODE_LEN_BYTES} bytes`,
);
}
});
From fcb8734a3cf25ec8c4c6f6c9dd19608ab5e450bd Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Thu, 30 May 2024 00:36:47 +0200
Subject: [PATCH 09/30] add some utils constants and types
---
src/constants.ts | 21 ++
src/rpc.methods.ts | 284 ++++++++++++++++----
src/types.ts | 50 ++--
src/utils.ts | 640 +++++++++++++++++++++++++--------------------
4 files changed, 631 insertions(+), 364 deletions(-)
diff --git a/src/constants.ts b/src/constants.ts
index 739a3b9..133c88f 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -149,3 +149,24 @@ export const DEFAULT_GAS_PER_PUBDATA_LIMIT = 50_000;
* @constant
*/
export const REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT = 800;
+
+/**
+ * All typed data conforming to the EIP712 standard within zkSync Era.
+ */
+export const EIP712_TYPES = {
+ Transaction: [
+ {name: 'txType', type: 'uint256'},
+ {name: 'from', type: 'uint256'},
+ {name: 'to', type: 'uint256'},
+ {name: 'gasLimit', type: 'uint256'},
+ {name: 'gasPerPubdataByteLimit', type: 'uint256'},
+ {name: 'maxFeePerGas', type: 'uint256'},
+ {name: 'maxPriorityFeePerGas', type: 'uint256'},
+ {name: 'paymaster', type: 'uint256'},
+ {name: 'nonce', type: 'uint256'},
+ {name: 'value', type: 'uint256'},
+ {name: 'data', type: 'bytes'},
+ {name: 'factoryDeps', type: 'bytes32[]'},
+ {name: 'paymasterInput', type: 'bytes'},
+ ],
+ };
\ No newline at end of file
diff --git a/src/rpc.methods.ts b/src/rpc.methods.ts
index 53a1453..7f98606 100644
--- a/src/rpc.methods.ts
+++ b/src/rpc.methods.ts
@@ -1,12 +1,7 @@
import type { Web3RequestManager } from 'web3-core';
-import { format, toNumber } from 'web3-utils';
-import type {
- Address,
- Bytes,
- HexString32Bytes,
- Numbers,
- TransactionWithSenderAPI,
-} from 'web3-types';
+import * as web3Utils from 'web3-utils';
+import * as web3Types from 'web3-types';
+import * as web3Accounts from 'web3-eth-accounts';
import {
DEFAULT_RETURN_FORMAT,
// Web3BaseProvider
@@ -22,6 +17,8 @@ import type {
RawBlockTransaction,
TransactionDetails,
WalletBalances,
+ TransactionRequest,
+ TransactionOverrides,
} from './types';
import {
AddressSchema,
@@ -38,6 +35,12 @@ import {
TransactionDetailsSchema,
UintSchema,
} from './schemas';
+import {
+ ETH_ADDRESS_IN_CONTRACTS,
+ L2_BASE_TOKEN_ADDRESS,
+ REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT,
+} from './constants';
+import { isAddressEq } from './utils';
// The ZkSync methods described here https://docs.zksync.io/build/api.html
@@ -45,8 +48,36 @@ import {
export class RpcMethods {
requestManager: Web3RequestManager;
+ protected _contractAddresses: {
+ bridgehubContract?: web3Types.Address;
+ mainContract?: web3Types.Address;
+ erc20BridgeL1?: web3Types.Address;
+ erc20BridgeL2?: web3Types.Address;
+ wethBridgeL1?: web3Types.Address;
+ wethBridgeL2?: web3Types.Address;
+ sharedBridgeL1?: web3Types.Address;
+ sharedBridgeL2?: web3Types.Address;
+ baseToken?: web3Types.Address;
+ };
+
+ protected contractAddresses(): {
+ bridgehubContract?: web3Types.Address;
+ mainContract?: web3Types.Address;
+ erc20BridgeL1?: web3Types.Address;
+ erc20BridgeL2?: web3Types.Address;
+ wethBridgeL1?: web3Types.Address;
+ wethBridgeL2?: web3Types.Address;
+ sharedBridgeL1?: web3Types.Address;
+ sharedBridgeL2?: web3Types.Address;
+ baseToken?: web3Types.Address;
+ } {
+ return this._contractAddresses;
+ }
+
constructor(requestManager: Web3RequestManager) {
this.requestManager = requestManager;
+
+ this._contractAddresses = {};
}
private async _send(method: string, params: unknown[]): Promise {
@@ -62,7 +93,11 @@ export class RpcMethods {
* @param returnFormat - The format of the return value.
*/
public async l1ChainId(returnFormat: DataFormat = DEFAULT_RETURN_FORMAT): Promise {
- return format(IntSchema, await this._send('zks_L1ChainId', []), returnFormat) as bigint;
+ return web3Utils.format(
+ IntSchema,
+ await this._send('zks_L1ChainId', []),
+ returnFormat,
+ ) as bigint;
}
/**
@@ -71,7 +106,11 @@ export class RpcMethods {
* @param returnFormat - The format of the return value.
*/
public async getL1BatchNumber(returnFormat: DataFormat = DEFAULT_RETURN_FORMAT): Promise {
- return format(IntSchema, await this._send('zks_L1BatchNumber', []), returnFormat) as bigint;
+ return web3Utils.format(
+ IntSchema,
+ await this._send('zks_L1BatchNumber', []),
+ returnFormat,
+ ) as bigint;
}
/**
@@ -81,13 +120,13 @@ export class RpcMethods {
* @param returnFormat - The format of the return value.
*/
public async getL1BatchDetails(
- number: Numbers,
+ number: web3Types.Numbers,
returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
): Promise {
- return format(
+ return web3Utils.format(
BatchDetailsSchema,
await this._send('zks_getL1BatchDetails', [
- typeof number === 'number' ? number : Number(toNumber(number)),
+ typeof number === 'number' ? number : Number(web3Utils.toNumber(number)),
]),
returnFormat,
) as BatchDetails;
@@ -104,13 +143,13 @@ export class RpcMethods {
* @param returnFormat - The format of the return value.
*/
public async getBlockDetails(
- number: Numbers,
+ number: web3Types.Numbers,
returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
): Promise {
- return format(
+ return web3Utils.format(
BlockDetailsSchema,
await this._send('zks_getBlockDetails', [
- typeof number === 'number' ? number : Number(toNumber(number)),
+ typeof number === 'number' ? number : Number(web3Utils.toNumber(number)),
]),
returnFormat,
) as BlockDetails;
@@ -123,10 +162,10 @@ export class RpcMethods {
* @param returnFormat - The format of the return value.
*/
public async getTransactionDetails(
- txHash: Bytes,
+ txHash: web3Types.Bytes,
returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
): Promise {
- return format(
+ return web3Utils.format(
TransactionDetailsSchema,
await this._send('zks_getTransactionDetails', [txHash]),
returnFormat,
@@ -140,10 +179,10 @@ export class RpcMethods {
* @param returnFormat - The format of the return value.
*/
public async getBytecodeByHash(
- bytecodeHash: Bytes,
+ bytecodeHash: web3Types.Bytes,
returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
): Promise {
- return format(
+ return web3Utils.format(
BytesSchema,
await this._send('zks_getBytecodeByHash', [bytecodeHash]),
returnFormat,
@@ -157,15 +196,15 @@ export class RpcMethods {
* @param returnFormat - The format of the return value.
*/
public async getRawBlockTransactions(
- number: Numbers,
+ number: web3Types.Numbers,
returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
): Promise {
const result = await this._send('zks_getRawBlockTransactions', [
- typeof number === 'number' ? number : Number(toNumber(number)),
+ typeof number === 'number' ? number : Number(web3Utils.toNumber(number)),
]);
if (Array.isArray(result)) {
return result.map(tx => {
- return format(RawBlockTransactionSchema, tx, returnFormat) as RawBlockTransaction;
+ return web3Utils.format(RawBlockTransactionSchema, tx, returnFormat) as RawBlockTransaction;
});
}
return [];
@@ -178,16 +217,98 @@ export class RpcMethods {
* @param returnFormat - The format of the return value.
*/
public async estimateFee(
- transaction: Partial,
+ transaction: Partial,
returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
): Promise {
- return format(
+ return web3Utils.format(
EstimateFeeSchema,
await this._send('zks_estimateFee', [transaction]),
returnFormat,
) as EstimateFee;
}
+ /**
+ * Returns the L1 base token address.
+ */
+ async getBaseTokenContractAddress(): Promise {
+ if (!this.contractAddresses().baseToken) {
+ this.contractAddresses().baseToken = (await this._send(
+ 'zks_getBaseTokenL1Address',
+ [],
+ )) as string;
+ }
+ return web3Utils.toChecksumAddress(this.contractAddresses().baseToken!);
+ }
+
+ /**
+ * Returns whether the chain is ETH-based.
+ */
+ async isEthBasedChain(): Promise {
+ return isAddressEq(await this.getBaseTokenContractAddress(), ETH_ADDRESS_IN_CONTRACTS);
+ }
+
+ /**
+ * Returns whether the `token` is the base token.
+ */
+ async isBaseToken(token: web3Types.Address): Promise {
+ return (
+ isAddressEq(token, await this.getBaseTokenContractAddress()) ||
+ isAddressEq(token, L2_BASE_TOKEN_ADDRESS)
+ );
+ }
+
+ /**
+ * Returns the testnet {@link https://docs.zksync.io/build/developer-reference/account-abstraction.html#paymasters paymaster address}
+ * if available, or `null`.
+ *
+ * Calls the {@link https://docs.zksync.io/build/api.html#zks-gettestnetpaymaster zks_getTestnetPaymaster} JSON-RPC method.
+ */
+ async getTestnetPaymasterAddress(): Promise {
+ // Unlike contract's addresses, the testnet paymaster is not cached, since it can be trivially changed
+ // on the fly by the server and should not be relied on to be constant
+ return (await this._send('zks_getTestnetPaymaster', [])) as web3Types.Address | null;
+ }
+
+ /**
+ * Returns the addresses of the default zkSync Era bridge contracts on both L1 and L2.
+ *
+ * Calls the {@link https://docs.zksync.io/build/api.html#zks-getbridgecontracts zks_getBridgeContracts} JSON-RPC method.
+ */
+ async getDefaultBridgeAddresses(): Promise<{
+ erc20L1: string;
+ erc20L2: string;
+ wethL1: string;
+ wethL2: string;
+ sharedL1: string;
+ sharedL2: string;
+ }> {
+ if (!this.contractAddresses().erc20BridgeL1) {
+ const addresses: {
+ l1Erc20DefaultBridge: string;
+ l2Erc20DefaultBridge: string;
+ l1WethBridge: string;
+ l2WethBridge: string;
+ l1SharedDefaultBridge: string;
+ l2SharedDefaultBridge: string;
+ } = (await this._send('zks_getBridgeContracts', [])) as any;
+
+ this.contractAddresses().erc20BridgeL1 = addresses.l1Erc20DefaultBridge;
+ this.contractAddresses().erc20BridgeL2 = addresses.l2Erc20DefaultBridge;
+ this.contractAddresses().wethBridgeL1 = addresses.l1WethBridge;
+ this.contractAddresses().wethBridgeL2 = addresses.l2WethBridge;
+ this.contractAddresses().sharedBridgeL1 = addresses.l1SharedDefaultBridge;
+ this.contractAddresses().sharedBridgeL2 = addresses.l2SharedDefaultBridge;
+ }
+ return {
+ erc20L1: this.contractAddresses().erc20BridgeL1!,
+ erc20L2: this.contractAddresses().erc20BridgeL2!,
+ wethL1: this.contractAddresses().wethBridgeL1!,
+ wethL2: this.contractAddresses().wethBridgeL2!,
+ sharedL1: this.contractAddresses().sharedBridgeL1!,
+ sharedL2: this.contractAddresses().sharedBridgeL2!,
+ };
+ }
+
/**
* Returns an estimate of the gas required for a L1 to L2 transaction.
*
@@ -195,14 +316,14 @@ export class RpcMethods {
* @param returnFormat - The format of the return value.
*/
public async estimateGasL1ToL2(
- transaction: Partial,
+ transaction: Partial,
returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
- ): Promise {
- return format(
+ ): Promise {
+ return web3Utils.format(
UintSchema,
await this._send('zks_estimateGasL1ToL2', [transaction]),
returnFormat,
- ) as Numbers;
+ ) as web3Types.Numbers;
}
/**
@@ -212,7 +333,7 @@ export class RpcMethods {
* @param returnFormat - The format of the return value.
*/
public async getAllAccountBalances(
- address: Address,
+ address: web3Types.Address,
returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
): Promise {
const res = (await this._send('zks_getAllAccountBalances', [address])) as WalletBalances;
@@ -220,11 +341,11 @@ export class RpcMethods {
return {};
}
for (let i = 0; i < Object.keys(res).length; i++) {
- res[Object.keys(res)[i]] = format(
+ res[Object.keys(res)[i]] = web3Utils.format(
UintSchema,
res[Object.keys(res)[i]],
returnFormat,
- ) as Numbers;
+ ) as web3Types.Numbers;
}
return res;
@@ -235,12 +356,14 @@ export class RpcMethods {
*
* @param returnFormat - The format of the return value.
*/
- public async getMainContract(returnFormat: DataFormat = DEFAULT_RETURN_FORMAT): Promise {
- return format(
+ public async getMainContract(
+ returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
+ ): Promise {
+ return web3Utils.format(
AddressSchema,
await this._send('zks_getMainContract', []),
returnFormat,
- ) as Address;
+ ) as web3Types.Address;
}
/**
@@ -251,16 +374,16 @@ export class RpcMethods {
* @param returnFormat - The format of the return value.
*/
public async getL1BatchBlockRange(
- number: Numbers,
+ number: web3Types.Numbers,
returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
- ): Promise {
- return format(
+ ): Promise {
+ return web3Utils.format(
BytesArraySchema,
await this._send('zks_getL1BatchBlockRange', [
- typeof number === 'number' ? number : Number(toNumber(number)),
+ typeof number === 'number' ? number : Number(web3Utils.toNumber(number)),
]),
returnFormat,
- ) as Bytes[];
+ ) as web3Types.Bytes[];
}
/**
@@ -273,20 +396,20 @@ export class RpcMethods {
* @param returnFormat - The format of the return value.
*/
public async getProof(
- address: Address,
+ address: web3Types.Address,
keys: string[],
- l1BatchNumber: Numbers,
+ l1BatchNumber: web3Types.Numbers,
returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
): Promise {
const res = (await this._send('zks_getProof', [
address,
keys,
- typeof l1BatchNumber === 'number' ? l1BatchNumber : Number(toNumber(l1BatchNumber)),
+ typeof l1BatchNumber === 'number' ? l1BatchNumber : Number(web3Utils.toNumber(l1BatchNumber)),
])) as StorageProof;
- const result = format(ProofSchema, res, returnFormat) as StorageProof;
+ const result = web3Utils.format(ProofSchema, res, returnFormat) as StorageProof;
result.storageProof = [];
for (let i = 0; i < res.storageProof.length; i++) {
- result.storageProof[i] = format(
+ result.storageProof[i] = web3Utils.format(
{
type: 'object',
properties: ProofSchema.properties.storageProof.properties,
@@ -306,12 +429,12 @@ export class RpcMethods {
*/
public async getTestnetPaymaster(
returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
- ): Promise {
- return format(
+ ): Promise {
+ return web3Utils.format(
AddressSchema,
await this._send('zks_getTestnetPaymaster', []),
returnFormat,
- ) as Address;
+ ) as web3Types.Address;
}
/**
@@ -324,17 +447,19 @@ export class RpcMethods {
* @param returnFormat - The format of the return value.
*/
public async getL2ToL1LogProof(
- txHash: HexString32Bytes,
- l2ToL1LogIndex?: Numbers,
+ txHash: web3Types.HexString32Bytes,
+ l2ToL1LogIndex?: web3Types.Numbers,
returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
): Promise {
- const params: [HexString32Bytes, number?] = [txHash];
+ const params: [web3Types.HexString32Bytes, number?] = [txHash];
if (l2ToL1LogIndex) {
params.push(
- typeof l2ToL1LogIndex === 'number' ? l2ToL1LogIndex : Number(toNumber(l2ToL1LogIndex)),
+ typeof l2ToL1LogIndex === 'number'
+ ? l2ToL1LogIndex
+ : Number(web3Utils.toNumber(l2ToL1LogIndex)),
);
}
- return format(
+ return web3Utils.format(
L2ToL1ProofSchema,
await this._send('zks_getL2ToL1LogProof', params),
returnFormat,
@@ -349,10 +474,61 @@ export class RpcMethods {
public async getBridgeContracts(
returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
): Promise {
- return format(
+ return web3Utils.format(
BridgeAddressesSchema,
await this._send('zks_getBridgeContracts', []),
returnFormat,
) as BridgeAddresses;
}
+
+ /**
+ * Returns gas estimation for an L1 to L2 execute operation.
+ *
+ * @param transaction The transaction details.
+ * @param transaction.contractAddress The address of the contract.
+ * @param transaction.calldata The transaction call data.
+ * @param [transaction.caller] The caller's address.
+ * @param [transaction.l2Value] The current L2 gas value.
+ * @param [transaction.factoryDeps] An array of bytes containing contract bytecode.
+ * @param [transaction.gasPerPubdataByte] The current gas per byte value.
+ * @param [transaction.overrides] Transaction overrides including `gasLimit`, `gasPrice`, and `value`.
+ */
+ // TODO (EVM-3): support refundRecipient for fee estimation
+ async estimateL1ToL2Execute(
+ transaction: {
+ contractAddress: web3Types.Address;
+ calldata: string;
+ caller?: web3Types.Address;
+ l2Value?: web3Types.Numbers;
+ factoryDeps?: web3Types.Bytes[];
+ gasPerPubdataByte?: web3Types.Numbers;
+ overrides?: TransactionOverrides;
+ },
+ returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
+ ): Promise {
+ transaction.gasPerPubdataByte ??= REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT;
+
+ // If the `from` address is not provided, we use a random address, because
+ // due to storage slot aggregation, the gas estimation will depend on the address
+ // and so estimation for the zero address may be smaller than for the sender.
+ transaction.caller ??= web3Accounts.create().address;
+
+ const customData = {
+ gasPerPubdata: transaction.gasPerPubdataByte,
+ };
+ if (transaction.factoryDeps) {
+ Object.assign(customData, { factoryDeps: transaction.factoryDeps });
+ }
+
+ return await this.estimateGasL1ToL2(
+ {
+ from: transaction.caller,
+ data: transaction.calldata,
+ to: transaction.contractAddress,
+ value: transaction.l2Value ? web3Utils.toHex(transaction.l2Value) : undefined,
+ customData,
+ },
+ returnFormat,
+ );
+ }
}
diff --git a/src/types.ts b/src/types.ts
index af647f1..ec12339 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -5,7 +5,15 @@ export type { Bytes, HexString, Numbers } from 'web3-types';
// // TODO: // is it needed to be re-exported from web3
// import { watchTransactionForConfirmations } from 'web3-eth/lib/types/utils/watch_transaction_for_confirmations.js';
-import { Bytes, HexString, Numbers, Transaction, EIP1193Provider } from 'web3-types';
+import {
+ Bytes,
+ HexString,
+ Numbers,
+ Transaction,
+ EIP1193Provider,
+ TransactionWithSenderAPI,
+ TransactionReceipt,
+} from 'web3-types';
import {
// FeeMarketEIP1559Transaction,
@@ -533,27 +541,27 @@ export interface L2ToL1Log {
logIndex: number;
}
-// /**
-// * A `TransactionRequest` is an extension of {@link ethers.TransactionRequest} with additional features for interacting
-// * with zkSync Era.
-// */
-// export interface TransactionRequest extends EthersTransactionRequest {
-// /** The custom data for EIP712 transaction metadata. */
-// customData?: null | Eip712Meta;
-// }
+/**
+ * A `TransactionRequest` is an extension of {@link ethers.TransactionRequest} with additional features for interacting
+ * with zkSync Era.
+ */
+export declare type TransactionRequest = TransactionWithSenderAPI & {
+ /** The custom data for EIP712 transaction metadata. */
+ customData?: null | Eip712Meta;
+};
-// /**
-// * Interface representation of priority op response that extends {@link ethers.TransactionResponse} and adds a function
-// * that waits to commit a L1 transaction, including when given on optional confirmation number.
-// */
-// export interface PriorityOpResponse extends TransactionResponse {
-// /**
-// * Waits for the L1 transaction to be committed, including waiting for the specified number of confirmations.
-// * @param confirmation The number of confirmations to wait for. Defaults to 1.
-// * @returns A promise that resolves to the transaction receipt once committed.
-// */
-// waitL1Commit(confirmation?: number): Promise;
-// }
+/**
+ * Interface representation of priority op response that extends {@link ethers.TransactionResponse} and adds a function
+ * that waits to commit a L1 transaction, including when given on optional confirmation number.
+ */
+export interface PriorityOpResponse extends Transaction {
+ /**
+ * Waits for the L1 transaction to be committed, including waiting for the specified number of confirmations.
+ * @param confirmation The number of confirmations to wait for. Defaults to 1.
+ * @returns A promise that resolves to the transaction receipt once committed.
+ */
+ waitL1Commit(confirmation?: number): Promise;
+}
/** A map containing accounts and their balances. */
export type BalancesMap = { [key: string]: bigint };
diff --git a/src/utils.ts b/src/utils.ts
index 3177fdb..6602a89 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -11,6 +11,7 @@ import * as web3Utils from 'web3-utils';
import * as web3Accounts from 'web3-eth-accounts';
import * as web3Types from 'web3-types';
import * as web3Abi from 'web3-eth-abi';
+import * as web3Contract from 'web3-eth-contract';
import {
DeploymentInfo,
@@ -45,9 +46,11 @@ import {
EIP1271_MAGIC_VALUE,
L1_FEE_ESTIMATION_COEF_NUMERATOR,
L1_FEE_ESTIMATION_COEF_DENOMINATOR,
+ // EIP712_TX_TYPE,
+ // DEFAULT_GAS_PER_PUBDATA_LIMIT,
} from './constants';
-// import { RpcMethods } from './rpc.methods'; // to be used instead of the one at zksync-ethers: Provider from ./provider
+import { RpcMethods } from './rpc.methods'; // to be used instead of the one at zksync-ethers: Provider from ./provider
// export * from './paymaster-utils';
// export * from './smart-account-utils';
@@ -99,7 +102,7 @@ export const L1BridgeContract = new web3.Contract(IL1BridgeABI);
* The web3.js Contract instance for the `IL2Bridge` interface, which is utilized for transferring ERC20 tokens from L2 to L1.
* @constant
*/
-export const L2Bridge = new web3.Contract(IL2BridgeABI);
+export const L2BridgeContract = new web3.Contract(IL2BridgeABI);
/**
* The web3.js Contract instance for the `INonceHolder` interface, which is utilized for managing deployment nonces.
@@ -112,8 +115,12 @@ export const NonceHolderContract = new web3.Contract(INonceHolderABI);
* consider adding the next few functions to web3.js:
* */
-export const numberToBytes = (number: web3Types.Numbers) =>
- web3Utils.hexToBytes(web3Utils.numberToHex(number));
+export const toBytes = (number: web3Types.Numbers | Uint8Array) =>
+ web3Utils.hexToBytes(
+ typeof number === 'number' || typeof number === 'bigint'
+ ? web3Utils.numberToHex(number)
+ : web3Utils.bytesToHex(number),
+ );
export function concat(bytes: web3Types.Bytes[]): string {
return '0x' + bytes.map(d => web3Utils.toHex(d).substring(2)).join('');
@@ -123,9 +130,10 @@ export function contractFunctionId(value: string): string {
return web3Utils.keccak256(web3Utils.utf8ToBytes(value));
}
+// TODO: needs to test for the first parameter being Eip712TypedData
function recoverSignerAddress(
messageOrData: string | web3Types.Eip712TypedData,
- signature: string | EthereumSignature,
+ signature: SignatureLike,
) {
let message;
if (typeof messageOrData !== 'string') {
@@ -134,25 +142,87 @@ function recoverSignerAddress(
message = messageOrData;
}
- const r = web3Accounts.toUint8Array(
- (signature as EthereumSignature).r ?? (signature as string).slice(0, 66),
- );
- const s = web3Accounts.toUint8Array(
- (signature as EthereumSignature).s ?? `0x${(signature as string).slice(66, 130)}`,
- );
- const v = BigInt(
- (signature as EthereumSignature).v ??
- web3Utils.hexToNumber(`0x${(signature as string).slice(130, 132)}`),
- );
+ if (typeof signature === 'string') {
+ return web3Accounts.recover(message, signature);
+ }
- const recoveredPublicKey = web3Utils.bytesToHex(
- web3Accounts.ecrecover(web3Accounts.toUint8Array(message), v, r, s),
- );
+ const r = web3Utils.toHex(signature.r);
+ const s = web3Utils.toHex(signature.s);
+ const v = web3Utils.toHex(signature.v);
- const recoveredAddress = `0x${web3Utils.keccak256(web3Utils.bytesToHex(recoveredPublicKey)).slice(-40)}`;
+ const recoveredAddress = web3Accounts.recover(message, v, r, s);
return recoveredAddress;
}
+export class SignatureObject {
+ public r: Uint8Array;
+ public s: Uint8Array;
+ public v: bigint;
+
+ constructor(r: Uint8Array, s: Uint8Array, v: web3Types.Numbers);
+ constructor(signature: string | SignatureLike);
+ constructor(
+ rOrSignature: string | Uint8Array | SignatureLike,
+ s?: Uint8Array,
+ v?: web3Types.Numbers,
+ ) {
+ if (typeof rOrSignature === 'string') {
+ if (rOrSignature.length !== 132) {
+ throw new Error('Invalid signature length');
+ }
+ // Initialize with a single string parameter
+ const signature = rOrSignature as string;
+ this.r = web3Accounts.toUint8Array(signature.slice(0, 66));
+ this.s = web3Accounts.toUint8Array(`0x${signature.slice(66, 130)}`);
+ this.v = BigInt(web3Utils.hexToNumber(`0x${signature.slice(130, 132)}`));
+ } else if (
+ (rOrSignature as EthereumSignature).r &&
+ (rOrSignature as EthereumSignature).s &&
+ (rOrSignature as EthereumSignature).v
+ ) {
+ const ethereumSignature = rOrSignature as EthereumSignature;
+ this.r = web3Utils.bytesToUint8Array(ethereumSignature.r);
+ this.s = web3Utils.bytesToUint8Array(ethereumSignature.s);
+ this.v = BigInt(ethereumSignature.v);
+ } else {
+ const signature = { r: rOrSignature as Uint8Array, s: s, v: v };
+ // Initialize with individual parameters
+ this.r = signature.r!;
+ this.s = signature.s!;
+ this.v = BigInt(signature.v!);
+ }
+ }
+
+ public toString() {
+ return `${this.r}${this.s.slice(2)}${web3Utils.toHex(this.v).slice(2)}`;
+ }
+}
+
+export type SignatureLike = SignatureObject | EthereumSignature | string;
+
+/**
+ * eip-191 message prefix
+ */
+export const MessagePrefix: string = '\x19Ethereum Signed Message:\n';
+
+/**
+ * Has the message according to eip-191
+ * @param message - message to hash
+ * @returns hash of the message
+ */
+export function hashMessage(message: Uint8Array | string): string {
+ if (typeof message === 'string') {
+ message = web3Accounts.toUint8Array(message);
+ }
+ return web3Utils.keccak256(
+ concat([
+ web3Accounts.toUint8Array(MessagePrefix),
+ web3Accounts.toUint8Array(String(message.length)),
+ message,
+ ]),
+ );
+}
+
/**
* ------------------------------------------------------------
* End of the function section that would be added to web3.js
@@ -383,10 +453,7 @@ export async function checkBaseCost(
// * );
// * // serializedTx = "0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0"
// */
-// export function serializeEip712(
-// transaction: TransactionLike,
-// signature?: ethers.SignatureLike,
-// ): string {
+// export function serializeEip712(transaction: TransactionLike, signature?: SignatureLike): string {
// if (!transaction.chainId) {
// throw Error("Transaction chainId isn't set!");
// }
@@ -400,30 +467,30 @@ export async function checkBaseCost(
// const maxPriorityFeePerGas = transaction.maxPriorityFeePerGas || maxFeePerGas;
// const fields: any[] = [
-// numberToBytes(transaction.nonce || 0),
-// numberToBytes(maxPriorityFeePerGas),
-// numberToBytes(maxFeePerGas),
-// numberToBytes(transaction.gasLimit || 0),
+// toBytes(transaction.nonce || 0),
+// toBytes(maxPriorityFeePerGas),
+// toBytes(maxFeePerGas),
+// toBytes(transaction.gasLimit || 0),
// transaction.to ? web3Utils.toChecksumAddress(transaction.to) : '0x',
-// numberToBytes(transaction.value || 0),
+// toBytes(transaction.value || 0),
// transaction.data || '0x',
// ];
// if (signature) {
-// const sig = ethers.Signature.from(signature);
-// fields.push(numberToBytes(sig.yParity));
-// fields.push(numberToBytes(sig.r));
-// fields.push(numberToBytes(sig.s));
+// const sig = new SignatureObject(signature);
+// fields.push(toBytes(sig.yParity));
+// fields.push(toBytes(sig.r));
+// fields.push(toBytes(sig.s));
// } else {
-// fields.push(numberToBytes(transaction.chainId));
+// fields.push(toBytes(transaction.chainId));
// fields.push('0x');
// fields.push('0x');
// }
-// fields.push(numberToBytes(transaction.chainId));
+// fields.push(toBytes(transaction.chainId));
// fields.push(web3Utils.toChecksumAddress(from));
// // Add meta
-// fields.push(numberToBytes(meta.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT));
+// fields.push(toBytes(meta.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT));
// fields.push((meta.factoryDeps ?? []).map(dep => web3Utils.toHex(dep)));
// if (meta.customSignature && web3Utils.bytesToUint8Array(meta.customSignature).length === 0) {
@@ -603,12 +670,7 @@ export function hashBytecode(bytecode: web3Types.Bytes): Uint8Array {
// }
// if (!transaction.customData?.customSignature) {
-// // TODO: either try to use a string or a signature object
-// // const signatureStr = `${ethSignature.r}${ethSignature.s.slice(2)}${ethSignature.v.slice(2)}`;
-// // or maybe try to use @noble/curves (but it does not deal with `v`):
-// // import { SignatureType } from '@noble/curves';
-// // new secp256k1.Signature(ethSignature.r, ethSignature.s, ethSignature.v);
-// transaction.signature = ethers.Signature.from(ethSignature);
+// transaction.signature = new SignatureObject(ethSignature).toString();
// }
// transaction.hash = eip712TxHash(transaction, ethSignature);
@@ -743,67 +805,59 @@ export function undoL1ToL2Alias(address: string): string {
return web3Utils.padLeft(web3Utils.toHex(result), 20 * 2);
}
-// /**
-// * Returns the data needed for correct initialization of an L1 token counterpart on L2.
-// *
-// * @param l1TokenAddress The token address on L1.
-// * @param provider The client that is able to work with contracts on a read-write basis.
-// * @returns The encoded bytes which contains token name, symbol and decimals.
-// */
-// export async function getERC20DefaultBridgeData(
-// l1TokenAddress: string,
-// context: web3.Web3Context, // or maybe use RpcMethods?
-// ): Promise {
-// if (isAddressEq(l1TokenAddress, LEGACY_ETH_ADDRESS)) {
-// l1TokenAddress = ETH_ADDRESS_IN_CONTRACTS;
-// }
-// const token = IERC20__factory.connect(l1TokenAddress, context);
-
-// const name = isAddressEq(l1TokenAddress, ETH_ADDRESS_IN_CONTRACTS) ? 'Ether' : await token.name();
-// const symbol = isAddressEq(l1TokenAddress, ETH_ADDRESS_IN_CONTRACTS)
-// ? 'ETH'
-// : await token.symbol();
-// const decimals = isAddressEq(l1TokenAddress, ETH_ADDRESS_IN_CONTRACTS)
-// ? 18
-// : await token.decimals();
-
-// const coder = new AbiCoder();
-
-// const nameBytes = coder.encode(['string'], [name]);
-// const symbolBytes = coder.encode(['string'], [symbol]);
-// const decimalsBytes = coder.encode(['uint256'], [decimals]);
-
-// return coder.encode(['bytes', 'bytes', 'bytes'], [nameBytes, symbolBytes, decimalsBytes]);
-// }
+/**
+ * Returns the data needed for correct initialization of an L1 token counterpart on L2.
+ *
+ * @param l1TokenAddress The token address on L1.
+ * @param provider The client that is able to work with contracts on a read-write basis.
+ * @returns The encoded bytes which contains token name, symbol and decimals.
+ */
+export async function getERC20DefaultBridgeData(
+ l1TokenAddress: string,
+ context: web3.Web3Context, // or maybe use RpcMethods?
+): Promise {
+ if (isAddressEq(l1TokenAddress, LEGACY_ETH_ADDRESS)) {
+ l1TokenAddress = ETH_ADDRESS_IN_CONTRACTS;
+ }
+ const token = new web3Contract.Contract(IERC20ABI, l1TokenAddress, context);
+
+ const name = isAddressEq(l1TokenAddress, ETH_ADDRESS_IN_CONTRACTS)
+ ? 'Ether'
+ : await token.methods.name().call();
+ const symbol = isAddressEq(l1TokenAddress, ETH_ADDRESS_IN_CONTRACTS)
+ ? 'ETH'
+ : await token.methods.symbol().call();
+ const decimals = isAddressEq(l1TokenAddress, ETH_ADDRESS_IN_CONTRACTS)
+ ? 18
+ : await token.methods.decimals().call();
+
+ return web3Abi.encodeParameters(['string', 'string', 'uint256'], [name, symbol, decimals]);
+}
-// /**
-// * Returns the calldata sent by an L1 ERC20 bridge to its L2 counterpart during token bridging.
-// *
-// * @param l1TokenAddress The token address on L1.
-// * @param l1Sender The sender address on L1.
-// * @param l2Receiver The recipient address on L2.
-// * @param amount The gas fee for the number of tokens to bridge.
-// * @param bridgeData Additional bridge data.
-// *
-// * @example
-// *
-// *
-// */
-// export async function getERC20BridgeCalldata(
-// l1TokenAddress: string,
-// l1Sender: string,
-// l2Receiver: string,
-// amount: web3Types.Numbers,
-// bridgeData: web3Types.Bytes,
-// ): Promise {
-// return L2_BRIDGE_ABI.encodeFunctionData('finalizeDeposit', [
-// l1Sender,
-// l2Receiver,
-// l1TokenAddress,
-// amount,
-// bridgeData,
-// ]);
-// }
+/**
+ * Returns the calldata sent by an L1 ERC20 bridge to its L2 counterpart during token bridging.
+ *
+ * @param l1TokenAddress The token address on L1.
+ * @param l1Sender The sender address on L1.
+ * @param l2Receiver The recipient address on L2.
+ * @param amount The gas fee for the number of tokens to bridge.
+ * @param bridgeData Additional bridge data.
+ *
+ * @example
+ *
+ *
+ */
+export async function getERC20BridgeCalldata(
+ l1TokenAddress: string,
+ l1Sender: string,
+ l2Receiver: string,
+ amount: web3Types.Numbers,
+ bridgeData: web3Types.Bytes,
+): Promise {
+ return L2BridgeContract.methods
+ .finalizeDeposit(l1Sender, l2Receiver, l1TokenAddress, amount, bridgeData)
+ .encodeABI();
+}
/**
* Validates signatures from non-contract account addresses (EOA).
@@ -834,7 +888,7 @@ export function undoL1ToL2Alias(address: string): string {
function isECDSASignatureCorrect(
address: string,
msgHash: string,
- signature: EthereumSignature,
+ signature: SignatureLike,
): boolean {
try {
return isAddressEq(address, recoverSignerAddress(msgHash, signature));
@@ -865,7 +919,7 @@ async function isEIP1271SignatureCorrect(
context: web3.Web3Context, // or maybe use RpcMethods?
address: string,
msgHash: string,
- signature: EthereumSignature,
+ signature: SignatureLike,
): Promise {
const accountContract = new web3.Contract(IERC1271ABI, address, context);
@@ -891,7 +945,7 @@ async function isSignatureCorrect(
context: web3.Web3Context, // or maybe use RpcMethods?
address: string,
msgHash: string,
- signature: EthereumSignature,
+ signature: SignatureLike,
): Promise {
const code = await web3.eth.getCode(context, address, undefined, web3Types.DEFAULT_RETURN_FORMAT);
const isContractAccount = web3Utils.bytesToUint8Array(code).length !== 0;
@@ -903,151 +957,159 @@ async function isSignatureCorrect(
}
}
-// /**
-// * Returns whether the account abstraction message signature is correct.
-// * Signature can be created using EIP1271 or ECDSA.
-// *
-// * @param provider The provider.
-// * @param address The sender address.
-// * @param message The hash of the message.
-// * @param signature The Ethers signature.
-// *
-// * @example
-// *
-// * import { Wallet, utils, Provider } from "zksync-ethers";
-// *
-// * const ADDRESS = "";
-// * const PRIVATE_KEY = "";
-// * const context = Provider.getDefaultProvider(types.Network.Sepolia);
-// *
-// * const message = "Hello, world!";
-// * const signature = await new Wallet(PRIVATE_KEY).signMessage(message);
-// * // ethers.Wallet can be used as well
-// * // const signature = await new ethers.Wallet(PRIVATE_KEY).signMessage(message);
-// *
-// * const isValidSignature = await utils.isMessageSignatureCorrect(context, ADDRESS, message, signature);
-// * // isValidSignature = true
-// */
-// export async function isMessageSignatureCorrect(
-// context: web3.Web3Context, // or maybe use RpcMethods?
-// address: string,
-// message: Uint8Array | string,
-// signature: SignatureLike,
-// ): Promise {
-// // TODO: needs to implement this (similar to web3Abi.getEncodedEip712Data but for stings and Uint8Array)
-// const msgHash = ethers.getMessage(message);
-// return await isSignatureCorrect(context, address, msgHash, signature);
-// }
+/**
+ * Returns whether the account abstraction message signature is correct.
+ * Signature can be created using EIP1271 or ECDSA.
+ *
+ * @param provider The provider.
+ * @param address The sender address.
+ * @param message The hash of the message.
+ * @param signature The Ethers signature.
+ *
+ * @example
+ *
+ * import { Wallet, utils, Provider } from "zksync-ethers";
+ *
+ * const ADDRESS = "";
+ * const PRIVATE_KEY = "";
+ * const context = Provider.getDefaultProvider(types.Network.Sepolia);
+ *
+ * const message = "Hello, world!";
+ * const signature = await new Wallet(PRIVATE_KEY).signMessage(message);
+ * // ethers.Wallet can be used as well
+ * // const signature = await new ethers.Wallet(PRIVATE_KEY).signMessage(message);
+ *
+ * const isValidSignature = await utils.isMessageSignatureCorrect(context, ADDRESS, message, signature);
+ * // isValidSignature = true
+ */
+export async function isMessageSignatureCorrect(
+ context: web3.Web3Context, // or maybe use RpcMethods?
+ address: string,
+ message: Uint8Array | string,
+ signature: SignatureLike,
+): Promise {
+ const msgHash = web3Accounts.hashMessage(
+ typeof message === 'string' ? message : web3Utils.bytesToHex(message),
+ );
+ return await isSignatureCorrect(context, address, msgHash, signature);
+}
-// /**
-// * Returns whether the account abstraction EIP712 signature is correct.
-// *
-// * @param context The web3 context.
-// * @param address The sender address.
-// * @param domain The domain data.
-// * @param types A map of records pointing from field name to field type.
-// * @param value A single record value.
-// * @param signature The Ethers signature.
-// *
-// * @example
-// *
-// * import { Wallet, utils, Provider, EIP712Signer } from "zksync-ethers";
-// *
-// * const ADDRESS = "";
-// * const PRIVATE_KEY = "";
-// * const context = Provider.getDefaultProvider(types.Network.Sepolia);
-// *
-// * const tx: types.TransactionRequest = {
-// * type: 113,
-// * chainId: 270,
-// * from: ADDRESS,
-// * to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618",
-// * value: BigInt(7_000_000),
-// * };
-// *
-// * const eip712Signer = new EIP712Signer(
-// * new Wallet(PRIVATE_KEY), // or new ethers.Wallet(PRIVATE_KEY),
-// * Number((await context.getNetwork()).chainId)
-// * );
-// *
-// * const signature = await eip712Signer.sign(tx);
-// *
-// * const isValidSignature = await utils.isTypedDataSignatureCorrect(context, ADDRESS, await eip712Signer.getDomain(), utils.EIP712_TYPES, EIP712Signer.getSignInput(tx), signature);
-// * // isValidSignature = true
-// */
-// export async function isTypedDataSignatureCorrect(
-// context: web3.Web3Context, // or maybe use RpcMethods?
-// address: string,
-// domain: ethers.TypedDataDomain,
-// types: Record>,
-// value: Record,
-// signature: EthereumSignature,
-// ): Promise {
-// const msgHash = ethers.TypedDataEncoder.hash(domain, types, value);
-// return await isSignatureCorrect(context, address, msgHash, signature);
-// }
+/**
+ * Returns whether the account abstraction EIP712 signature is correct.
+ *
+ * @param context The web3 context.
+ * @param address The sender address.
+ * @param domain The domain data.
+ * @param types A map of records pointing from field name to field type.
+ * @param value A single record value.
+ * @param signature The Ethers signature.
+ *
+ * @example
+ *
+ * import { Wallet, utils, constants, Provider, EIP712Signer } from "zksync-ethers";
+ *
+ * const ADDRESS = "";
+ * const PRIVATE_KEY = "";
+ * const context = Provider.getDefaultProvider(types.Network.Sepolia);
+ *
+ * const tx: types.TransactionRequest = {
+ * type: 113,
+ * chainId: 270,
+ * from: ADDRESS,
+ * to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618",
+ * value: BigInt(7_000_000),
+ * };
+ *
+ * const eip712Signer = new EIP712Signer(
+ * new Wallet(PRIVATE_KEY), // or new ethers.Wallet(PRIVATE_KEY),
+ * Number((await context.getNetwork()).chainId)
+ * );
+ *
+ * const signature = await eip712Signer.sign(tx);
+ *
+ * const isValidSignature = await utils.isTypedDataSignatureCorrect(context, ADDRESS, await eip712Signer.getDomain(), constants.EIP712_TYPES, 'Transaction', EIP712Signer.getSignInput(tx), signature);
+ * // isValidSignature = true
+ */
+export async function isTypedDataSignatureCorrect(
+ context: web3.Web3Context, // or maybe use RpcMethods?
+ address: string,
+ domain: Extract,
+ types: Extract,
+ primaryType: Extract,
+ value: Record,
+ signature: EthereumSignature,
+): Promise {
+ const data: web3Types.Eip712TypedData = {
+ domain,
+ types,
+ primaryType,
+ message: value,
+ };
+ const msgHash = web3Abi.getEncodedEip712Data(data);
+ return await isSignatureCorrect(context, address, msgHash, signature);
+}
-// /**
-// * Returns an estimation of the L2 gas required for token bridging via the default ERC20 bridge.
-// *
-// * @param providerL1 The Ethers provider for the L1 network.
-// * @param providerL2 The zkSync provider for the L2 network.
-// * @param token The address of the token to be bridged.
-// * @param amount The deposit amount.
-// * @param to The recipient address on the L2 network.
-// * @param from The sender address on the L1 network.
-// * @param gasPerPubdataByte The current gas per byte of pubdata.
-// *
-// * @see
-// * {@link https://docs.zksync.io/build/developer-reference/bridging-asset.html#default-bridges Default bridges documentation}.
-// *
-// * @example
-// *
-// *
-// */
-// export async function estimateDefaultBridgeDepositL2Gas(
-// providerL1: Web3Eth,
-// providerL2: Provider,
-// token: web3.Address,
-// amount: web3Types.Numbers,
-// to: web3.Address,
-// from?: web3.Address,
-// gasPerPubdataByte?: web3Types.Numbers,
-// ): Promise {
-// // If the `from` address is not provided, we use a random address, because
-// // due to storage slot aggregation, the gas estimation will depend on the address
-// // and so estimation for the zero address may be smaller than for the sender.
-// from ??= web3Accounts.create().address;
-// if (await providerL2.isBaseToken(token)) {
-// return await providerL2.estimateL1ToL2Execute({
-// contractAddress: to,
-// gasPerPubdataByte: gasPerPubdataByte,
-// caller: from,
-// calldata: '0x',
-// l2Value: amount,
-// });
-// } else {
-// const bridgeAddresses = await providerL2.getDefaultBridgeAddresses();
-
-// const value = 0;
-// const l1BridgeAddress = bridgeAddresses.sharedL1;
-// const l2BridgeAddress = bridgeAddresses.sharedL2;
-// const bridgeData = await getERC20DefaultBridgeData(token, providerL1);
-
-// return await estimateCustomBridgeDepositL2Gas(
-// providerL2,
-// l1BridgeAddress,
-// l2BridgeAddress,
-// isAddressEq(token, LEGACY_ETH_ADDRESS) ? ETH_ADDRESS_IN_CONTRACTS : token,
-// amount,
-// to,
-// bridgeData,
-// from,
-// gasPerPubdataByte,
-// value,
-// );
-// }
-// }
+/**
+ * Returns an estimation of the L2 gas required for token bridging via the default ERC20 bridge.
+ *
+ * @param providerL1 The Ethers provider for the L1 network.
+ * @param providerL2 The zkSync provider for the L2 network.
+ * @param token The address of the token to be bridged.
+ * @param amount The deposit amount.
+ * @param to The recipient address on the L2 network.
+ * @param from The sender address on the L1 network.
+ * @param gasPerPubdataByte The current gas per byte of pubdata.
+ *
+ * @see
+ * {@link https://docs.zksync.io/build/developer-reference/bridging-asset.html#default-bridges Default bridges documentation}.
+ *
+ * @example
+ *
+ *
+ */
+export async function estimateDefaultBridgeDepositL2Gas(
+ providerL1: web3.Web3Eth,
+ providerL2: RpcMethods,
+ token: web3.Address,
+ amount: web3Types.Numbers,
+ to: web3.Address,
+ from?: web3.Address,
+ gasPerPubdataByte?: web3Types.Numbers,
+): Promise {
+ // If the `from` address is not provided, we use a random address, because
+ // due to storage slot aggregation, the gas estimation will depend on the address
+ // and so estimation for the zero address may be smaller than for the sender.
+ from ??= web3Accounts.create().address;
+ if (await providerL2.isBaseToken(token)) {
+ return await providerL2.estimateL1ToL2Execute({
+ contractAddress: to,
+ gasPerPubdataByte: gasPerPubdataByte,
+ caller: from,
+ calldata: '0x',
+ l2Value: amount,
+ });
+ } else {
+ const bridgeAddresses = await providerL2.getDefaultBridgeAddresses();
+
+ const value = 0;
+ const l1BridgeAddress = bridgeAddresses.sharedL1;
+ const l2BridgeAddress = bridgeAddresses.sharedL2;
+ const bridgeData = await getERC20DefaultBridgeData(token, providerL1);
+
+ return await estimateCustomBridgeDepositL2Gas(
+ providerL2,
+ l1BridgeAddress,
+ l2BridgeAddress,
+ isAddressEq(token, LEGACY_ETH_ADDRESS) ? ETH_ADDRESS_IN_CONTRACTS : token,
+ amount,
+ to,
+ bridgeData,
+ from,
+ gasPerPubdataByte,
+ value,
+ );
+ }
+}
/**
* Scales the provided gas limit using a coefficient to ensure acceptance of L1->L2 transactions.
@@ -1069,48 +1131,48 @@ export function scaleGasLimit(gasLimit: bigint): bigint {
);
}
-// /**
-// * Returns an estimation of the L2 gas required for token bridging via the custom ERC20 bridge.
-// *
-// * @param providerL2 The zkSync provider for the L2 network.
-// * @param l1BridgeAddress The address of the custom L1 bridge.
-// * @param l2BridgeAddress The address of the custom L2 bridge.
-// * @param token The address of the token to be bridged.
-// * @param amount The deposit amount.
-// * @param to The recipient address on the L2 network.
-// * @param bridgeData Additional bridge data.
-// * @param from The sender address on the L1 network.
-// * @param gasPerPubdataByte The current gas per byte of pubdata.
-// * @param l2Value The `msg.value` of L2 transaction.
-// *
-// * @see
-// * {@link https://docs.zksync.io/build/developer-reference/bridging-asset.html#custom-bridges-on-l1-and-l2 Custom bridges documentation}.
-// *
-// * @example
-// *
-// *
-// */
-// export async function estimateCustomBridgeDepositL2Gas(
-// providerL2: RpcMethods,
-// l1BridgeAddress: web3.Address,
-// l2BridgeAddress: web3.Address,
-// token: web3.Address,
-// amount: web3Types.Numbers,
-// to: web3.Address,
-// bridgeData: web3Types.Bytes,
-// from: web3.Address,
-// gasPerPubdataByte?: web3Types.Numbers,
-// l2Value?: web3Types.Numbers,
-// ): Promise {
-// const calldata = await getERC20BridgeCalldata(token, from, to, amount, bridgeData);
-// return await providerL2.estimateL1ToL2Execute({
-// caller: applyL1ToL2Alias(l1BridgeAddress),
-// contractAddress: l2BridgeAddress,
-// gasPerPubdataByte: gasPerPubdataByte,
-// calldata: calldata,
-// l2Value: l2Value,
-// });
-// }
+/**
+ * Returns an estimation of the L2 gas required for token bridging via the custom ERC20 bridge.
+ *
+ * @param providerL2 The zkSync provider for the L2 network.
+ * @param l1BridgeAddress The address of the custom L1 bridge.
+ * @param l2BridgeAddress The address of the custom L2 bridge.
+ * @param token The address of the token to be bridged.
+ * @param amount The deposit amount.
+ * @param to The recipient address on the L2 network.
+ * @param bridgeData Additional bridge data.
+ * @param from The sender address on the L1 network.
+ * @param gasPerPubdataByte The current gas per byte of pubdata.
+ * @param l2Value The `msg.value` of L2 transaction.
+ *
+ * @see
+ * {@link https://docs.zksync.io/build/developer-reference/bridging-asset.html#custom-bridges-on-l1-and-l2 Custom bridges documentation}.
+ *
+ * @example
+ *
+ *
+ */
+export async function estimateCustomBridgeDepositL2Gas(
+ providerL2: RpcMethods,
+ l1BridgeAddress: web3.Address,
+ l2BridgeAddress: web3.Address,
+ token: web3.Address,
+ amount: web3Types.Numbers,
+ to: web3.Address,
+ bridgeData: web3Types.Bytes,
+ from: web3.Address,
+ gasPerPubdataByte?: web3Types.Numbers,
+ l2Value?: web3Types.Numbers,
+): Promise {
+ const calldata = await getERC20BridgeCalldata(token, from, to, amount, bridgeData);
+ return await providerL2.estimateL1ToL2Execute({
+ caller: applyL1ToL2Alias(l1BridgeAddress),
+ contractAddress: l2BridgeAddress,
+ gasPerPubdataByte: gasPerPubdataByte,
+ calldata: calldata,
+ l2Value: l2Value,
+ });
+}
/**
* Creates a JSON string from an object, including support for serializing bigint types.
From 6fb6fdd5e74ad12a6c54c07add4cb6e1d736da38 Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Tue, 4 Jun 2024 13:27:41 -0400
Subject: [PATCH 10/30] update web3js version
---
.gitignore | 1 +
package.json | 2 +-
yarn.lock | 204 ++++++++++++++++++---------------------------------
3 files changed, 72 insertions(+), 135 deletions(-)
diff --git a/.gitignore b/.gitignore
index 0d57b0f..6db120b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
node_modules
lib
.history
+.idea
diff --git a/package.json b/package.json
index 2c31b7c..1bc3559 100644
--- a/package.json
+++ b/package.json
@@ -39,7 +39,7 @@
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
- "web3": "^4.4.0"
+ "web3": "^4.9.0"
},
"peerDependencies": {
"web3": ">= 4.0.3"
diff --git a/yarn.lock b/yarn.lock
index 05168b1..57f63e3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5689,7 +5689,7 @@ walker@^1.0.8:
dependencies:
makeerror "1.0.12"
-web3-core@^4.3.0, web3-core@^4.3.1:
+web3-core@^4.3.0:
version "4.3.1"
resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.3.1.tgz#5c3b5b59f1e31537a64237caa5fd83f5ffd7b0f3"
integrity sha512-xa3w5n/ESxp5HIbrwsYBhpAPx2KI5LprjRFEtRwP0GpqqhTcCSMMYoyItRqQQ+k9YnB0PoFpWJfJI6Qn5x8YUQ==
@@ -5704,22 +5704,6 @@ web3-core@^4.3.0, web3-core@^4.3.1:
optionalDependencies:
web3-providers-ipc "^4.0.7"
-web3-core@^4.3.2:
- version "4.3.2"
- resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.3.2.tgz#f24b11d6a57dee527de8d42c89de2a439f0c4bed"
- integrity sha512-uIMVd/j4BgOnwfpY8ZT+QKubOyM4xohEhFZXz9xB8wimXWMMlYVlIK/TbfHqFolS9uOerdSGhsMbcK9lETae8g==
- dependencies:
- web3-errors "^1.1.4"
- web3-eth-accounts "^4.1.0"
- web3-eth-iban "^4.0.7"
- web3-providers-http "^4.1.0"
- web3-providers-ws "^4.0.7"
- web3-types "^1.3.1"
- web3-utils "^4.1.0"
- web3-validator "^2.0.3"
- optionalDependencies:
- web3-providers-ipc "^4.0.7"
-
web3-core@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.4.0.tgz#f2cd48aecda5ec34170edf7470001f90fdea1dc6"
@@ -5761,17 +5745,6 @@ web3-eth-abi@^4.1.4:
web3-utils "^4.0.7"
web3-validator "^2.0.3"
-web3-eth-abi@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.2.0.tgz#398d415e7783442d06fb7939e40ce3de7a3f04e9"
- integrity sha512-x7dUCmk6th+5N63s5kUusoNtsDJKUUQgl9+jECvGTBOTiyHe/V6aOY0120FUjaAGaapOnR7BImQdhqHv6yT2YQ==
- dependencies:
- abitype "0.7.1"
- web3-errors "^1.1.4"
- web3-types "^1.3.1"
- web3-utils "^4.1.1"
- web3-validator "^2.0.4"
-
web3-eth-abi@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.2.2.tgz#d7592e2cc113fd34da3fb4c933537ddf8639d9b2"
@@ -5796,19 +5769,6 @@ web3-eth-accounts@^4.1.0:
web3-utils "^4.0.7"
web3-validator "^2.0.3"
-web3-eth-accounts@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-4.1.1.tgz#55225e5510b961e1cacb4eccc996544998e907fc"
- integrity sha512-9JqhRi1YhO1hQOEmmBHgEGsME/B1FHMxpA/AK3vhpvQ8QeP6KbJW+cForTLfPpUbkmPxnRunG4PNNaETNlZfrA==
- dependencies:
- "@ethereumjs/rlp" "^4.0.1"
- crc-32 "^1.2.2"
- ethereum-cryptography "^2.0.0"
- web3-errors "^1.1.4"
- web3-types "^1.3.1"
- web3-utils "^4.1.1"
- web3-validator "^2.0.4"
-
web3-eth-accounts@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-4.1.2.tgz#652d6e3daf4d6cb3fe67cec6a878e768f6e8b8e8"
@@ -5822,46 +5782,33 @@ web3-eth-accounts@^4.1.2:
web3-utils "^4.2.3"
web3-validator "^2.0.5"
-web3-eth-contract@^4.1.2:
- version "4.1.3"
- resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-4.1.3.tgz#15dd4c978eaf0d8f894b2c1f3e9f94edd29ff57c"
- integrity sha512-F6e3eyetUDiNOb78EDVJtNOb0H1GPz3xAZH8edSfYdhaxI9tTutP2V3p++kh2ZJ/RrdE2+xil7H/nPLgHymBvg==
- dependencies:
- web3-core "^4.3.1"
- web3-errors "^1.1.4"
- web3-eth "^4.3.1"
- web3-eth-abi "^4.1.4"
- web3-types "^1.3.1"
- web3-utils "^4.0.7"
- web3-validator "^2.0.3"
-
-web3-eth-contract@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-4.2.0.tgz#73f70b19217cd4854211c05846f0c841763b3b29"
- integrity sha512-K7bUypsomTs8/Oa0Lgvq5plsSB5afgKJ79NMuXxvC5jfV+AxNrQyKcr5Vd5yEGNqrdQuIPduGQa8DpuY+rMe1g==
+web3-eth-contract@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-4.5.0.tgz#954c9cf4c055f3f2c33189bdd69093a8e20a569a"
+ integrity sha512-AX6OiDrIryz/T28k9Xz0gXpUrlOUjcooEgGluu2s5dFDWCPM/zlN5RsUZlXZiXpQyj52VCUy5+bkvu3yDPA4fg==
dependencies:
- web3-core "^4.3.2"
- web3-errors "^1.1.4"
- web3-eth "^4.4.0"
- web3-eth-abi "^4.2.0"
- web3-types "^1.3.1"
- web3-utils "^4.1.1"
- web3-validator "^2.0.4"
+ web3-core "^4.4.0"
+ web3-errors "^1.2.0"
+ web3-eth "^4.7.0"
+ web3-eth-abi "^4.2.2"
+ web3-types "^1.6.0"
+ web3-utils "^4.3.0"
+ web3-validator "^2.0.6"
-web3-eth-ens@^4.0.8:
- version "4.0.8"
- resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-4.0.8.tgz#f4e0a018ce6cc69e37007ee92063867feb5994f0"
- integrity sha512-nj0JfeD45BbzVJcVYpUJnSo8iwDcY9CQ7CZhhIVVOFjvpMAPw0zEwjTvZEIQyCW61OoDG9xcBzwxe2tZoYhMRw==
+web3-eth-ens@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-4.3.0.tgz#f44f279a4a07eae36e3de8384989ed069201a795"
+ integrity sha512-QpiKT9GqJouH5kEI/pRFprh88YPCtbht2Ym6rrklZ+VoWl9D+wLfbwvW7Aox349FS7k0UX2qVins5tVNLJ5GCQ==
dependencies:
"@adraffy/ens-normalize" "^1.8.8"
- web3-core "^4.3.0"
- web3-errors "^1.1.3"
- web3-eth "^4.3.1"
- web3-eth-contract "^4.1.2"
- web3-net "^4.0.7"
- web3-types "^1.3.0"
- web3-utils "^4.0.7"
- web3-validator "^2.0.3"
+ web3-core "^4.4.0"
+ web3-errors "^1.2.0"
+ web3-eth "^4.7.0"
+ web3-eth-contract "^4.5.0"
+ web3-net "^4.1.0"
+ web3-types "^1.6.0"
+ web3-utils "^4.3.0"
+ web3-validator "^2.0.6"
web3-eth-iban@^4.0.7:
version "4.0.7"
@@ -5902,22 +5849,22 @@ web3-eth@^4.3.1:
web3-utils "^4.0.7"
web3-validator "^2.0.3"
-web3-eth@^4.4.0:
- version "4.4.0"
- resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.4.0.tgz#755c34a769109836d122a53b33814d63f9ec5a65"
- integrity sha512-HswKdzF44wUrciRAtEJaml9O7rDYDxElHmFs+27WcO3nel2zku+n0xs4e2ZAehfrCZ+05/y7TgnOZnaDU8Fb/A==
+web3-eth@^4.7.0:
+ version "4.7.0"
+ resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.7.0.tgz#7d18815c7a79c200552bd0df8d3127f7532b3cc2"
+ integrity sha512-gqlWq4Xjz+yKL2MdxQ+BgR3F4CRo4AXWDXzftb3LDzvauEfjk/yRyoxkMSK4S9RIG96ylRImS172cV6cYzcukw==
dependencies:
setimmediate "^1.0.5"
- web3-core "^4.3.2"
- web3-errors "^1.1.4"
- web3-eth-abi "^4.2.0"
- web3-eth-accounts "^4.1.1"
- web3-net "^4.0.7"
+ web3-core "^4.4.0"
+ web3-errors "^1.2.0"
+ web3-eth-abi "^4.2.2"
+ web3-eth-accounts "^4.1.2"
+ web3-net "^4.1.0"
web3-providers-ws "^4.0.7"
- web3-rpc-methods "^1.1.4"
- web3-types "^1.3.1"
- web3-utils "^4.1.1"
- web3-validator "^2.0.4"
+ web3-rpc-methods "^1.3.0"
+ web3-types "^1.6.0"
+ web3-utils "^4.3.0"
+ web3-validator "^2.0.6"
web3-net@^4.0.7:
version "4.0.7"
@@ -5929,6 +5876,16 @@ web3-net@^4.0.7:
web3-types "^1.3.0"
web3-utils "^4.0.7"
+web3-net@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-4.1.0.tgz#db7bde675e58b153339e4f149f29ec0410d6bab2"
+ integrity sha512-WWmfvHVIXWEoBDWdgKNYKN8rAy6SgluZ0abyRyXOL3ESr7ym7pKWbfP4fjApIHlYTh8tNqkrdPfM4Dyi6CA0SA==
+ dependencies:
+ web3-core "^4.4.0"
+ web3-rpc-methods "^1.3.0"
+ web3-types "^1.6.0"
+ web3-utils "^4.3.0"
+
web3-providers-http@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-4.1.0.tgz#8d7afda67d1d8542ca85b30f60a3d1fe1993b561"
@@ -5969,14 +5926,14 @@ web3-rpc-methods@^1.1.3:
web3-types "^1.3.0"
web3-validator "^2.0.3"
-web3-rpc-methods@^1.1.4:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/web3-rpc-methods/-/web3-rpc-methods-1.1.4.tgz#0b478e38231d3f3260ac504307c6dc4059af6fda"
- integrity sha512-LTFNg4LFaeU8K9ecuT8fHDp/LOXyxCneeZjCrRYIW1u82Ly52SrY55FIzMIISGoG/iT5Wh7UiHOB3CQsWLBmbQ==
+web3-rpc-methods@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/web3-rpc-methods/-/web3-rpc-methods-1.3.0.tgz#d5ee299a69389d63822d354ddee2c6a121a6f670"
+ integrity sha512-/CHmzGN+IYgdBOme7PdqzF+FNeMleefzqs0LVOduncSaqsppeOEoskLXb2anSpzmQAP3xZJPaTrkQPWSJMORig==
dependencies:
- web3-core "^4.3.2"
- web3-types "^1.3.1"
- web3-validator "^2.0.3"
+ web3-core "^4.4.0"
+ web3-types "^1.6.0"
+ web3-validator "^2.0.6"
web3-types@^1.3.0, web3-types@^1.3.1:
version "1.3.1"
@@ -5998,16 +5955,6 @@ web3-utils@^4.0.7:
web3-types "^1.3.0"
web3-validator "^2.0.3"
-web3-utils@^4.1.0, web3-utils@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-4.1.1.tgz#88c0fe404abc3f038b7318f3a31141676cefeb63"
- integrity sha512-5AOmLKH6QuwHunLCNdVFlPSDE+T88bJYRQP+HWYoKNbI4STALCYQiJvj7LXE+Ed6cPfqANaK/LwKNbMPLCPFwA==
- dependencies:
- ethereum-cryptography "^2.0.0"
- web3-errors "^1.1.4"
- web3-types "^1.3.1"
- web3-validator "^2.0.4"
-
web3-utils@^4.2.3, web3-utils@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-4.3.0.tgz#c18918f0d692f745d622d22172406f6102528860"
@@ -6030,17 +5977,6 @@ web3-validator@^2.0.3:
web3-types "^1.3.0"
zod "^3.21.4"
-web3-validator@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/web3-validator/-/web3-validator-2.0.4.tgz#66f34c94f21a3c94d0dc2a2d30deb8a379825d38"
- integrity sha512-qRxVePwdW+SByOmTpDZFWHIUAa7PswvxNszrOua6BoGqAhERo5oJZBN+EbWtK/+O+ApNxt5FR3nCPmiZldiOQA==
- dependencies:
- ethereum-cryptography "^2.0.0"
- util "^0.12.5"
- web3-errors "^1.1.4"
- web3-types "^1.3.1"
- zod "^3.21.4"
-
web3-validator@^2.0.5, web3-validator@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/web3-validator/-/web3-validator-2.0.6.tgz#a0cdaa39e1d1708ece5fae155b034e29d6a19248"
@@ -6052,27 +5988,27 @@ web3-validator@^2.0.5, web3-validator@^2.0.6:
web3-types "^1.6.0"
zod "^3.21.4"
-web3@^4.4.0:
- version "4.4.0"
- resolved "https://registry.yarnpkg.com/web3/-/web3-4.4.0.tgz#83e5906675608adf9a14841f257e441c9154a8c7"
- integrity sha512-WcFA3RgE+cyM96MHLPs5Ec155nkYQuwhjk3JIhxcy6J1QrZK2zvKGzTQCCYHe3JhraduNTMbWy+EeNNGGji9+Q==
+web3@^4.9.0:
+ version "4.9.0"
+ resolved "https://registry.yarnpkg.com/web3/-/web3-4.9.0.tgz#2d18f6a48f366601aef32308260542ed14bd2b1b"
+ integrity sha512-O0R90ijjyqUlG1Wk3SXqfYMU1ZGJvLCAF/WfSg/isDz/0Fkpqxoj893wauZ+ieRvTXITlbQHVXGfpp8qrhWZ1g==
dependencies:
- web3-core "^4.3.2"
- web3-errors "^1.1.4"
- web3-eth "^4.4.0"
- web3-eth-abi "^4.2.0"
- web3-eth-accounts "^4.1.1"
- web3-eth-contract "^4.2.0"
- web3-eth-ens "^4.0.8"
+ web3-core "^4.4.0"
+ web3-errors "^1.2.0"
+ web3-eth "^4.7.0"
+ web3-eth-abi "^4.2.2"
+ web3-eth-accounts "^4.1.2"
+ web3-eth-contract "^4.5.0"
+ web3-eth-ens "^4.3.0"
web3-eth-iban "^4.0.7"
web3-eth-personal "^4.0.8"
- web3-net "^4.0.7"
+ web3-net "^4.1.0"
web3-providers-http "^4.1.0"
web3-providers-ws "^4.0.7"
- web3-rpc-methods "^1.1.4"
- web3-types "^1.3.1"
- web3-utils "^4.1.1"
- web3-validator "^2.0.4"
+ web3-rpc-methods "^1.3.0"
+ web3-types "^1.6.0"
+ web3-utils "^4.3.0"
+ web3-validator "^2.0.6"
webidl-conversions@^3.0.0:
version "3.0.1"
From 3dcaa3d966baaf4640db1fb7560dd776bec5b726 Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Mon, 3 Jun 2024 17:31:49 +0200
Subject: [PATCH 11/30] fix and test the signature validation function
---
src/utils.ts | 65 ++++++++++++++++++++++++-----------------
test/unit/utils.test.ts | 19 ++++++++++++
2 files changed, 57 insertions(+), 27 deletions(-)
diff --git a/src/utils.ts b/src/utils.ts
index 6602a89..e13d6d1 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -867,31 +867,30 @@ export async function getERC20BridgeCalldata(
* Called from {@link isSignatureCorrect} for non-contract account addresses.
*
* @param address The address which signs the `msgHash`.
- * @param msgHash The hash of the message.
+ * @param message The message which its hash had been signed early.
* @param signature The Ethers signature.
*
* @example
*
- * import { Wallet, utils } from "zksync-ethers";
- *
- * const ADDRESS = "";
- * const PRIVATE_KEY = "";
+ * import * as web3Accounts from 'web3-eth-accounts';
+ *
+ * const wallet = web3Accounts.create();
+ * const ADDRESS = wallet.address;
+ * const PRIVATE_KEY = wallet.privateKey;
*
* const message = "Hello, world!";
- * const signature = await new Wallet(PRIVATE_KEY).signMessage(message);
- * // ethers.Wallet can be used as well
- * // const signature = await new ethers.Wallet(PRIVATE_KEY).signMessage(message);
*
+ * const signature = web3Accounts.sign(message, PRIVATE_KEY).signature;
* const isValidSignature = await utils.isECDSASignatureCorrect(ADDRESS, message, signature);
* // isValidSignature = true
*/
function isECDSASignatureCorrect(
address: string,
- msgHash: string,
+ message: string,
signature: SignatureLike,
): boolean {
try {
- return isAddressEq(address, recoverSignerAddress(msgHash, signature));
+ return isAddressEq(address, recoverSignerAddress(message, signature));
} catch {
// In case ECDSA signature verification has thrown an error,
// we simply consider the signature as incorrect.
@@ -938,21 +937,32 @@ async function isEIP1271SignatureCorrect(
*
* @param context The web3 context.
* @param address The sender address.
- * @param msgHash The hash of the message.
+ * @param message The message which its hash had been signed early.
* @param signature The Ethers signature.
*/
async function isSignatureCorrect(
context: web3.Web3Context, // or maybe use RpcMethods?
address: string,
- msgHash: string,
+ message: string,
signature: SignatureLike,
): Promise {
- const code = await web3.eth.getCode(context, address, undefined, web3Types.DEFAULT_RETURN_FORMAT);
- const isContractAccount = web3Utils.bytesToUint8Array(code).length !== 0;
+ let isContractAccount;
+ if (context.provider) {
+ const code = await web3.eth.getCode(
+ context,
+ address,
+ undefined,
+ web3Types.DEFAULT_RETURN_FORMAT,
+ );
+ isContractAccount = web3Utils.bytesToUint8Array(code).length !== 0;
+ }
if (!isContractAccount) {
- return isECDSASignatureCorrect(address, msgHash, signature);
+ return isECDSASignatureCorrect(address, message, signature);
} else {
+ const msgHash = web3Accounts.hashMessage(
+ typeof message === 'string' ? message : web3Utils.bytesToHex(message),
+ );
return await isEIP1271SignatureCorrect(context, address, msgHash, signature);
}
}
@@ -968,18 +978,22 @@ async function isSignatureCorrect(
*
* @example
*
- * import { Wallet, utils, Provider } from "zksync-ethers";
+ * import { Web3 } from 'web3';
+ * import * as web3Accounts from 'web3-eth-accounts';
*
- * const ADDRESS = "";
- * const PRIVATE_KEY = "";
- * const context = Provider.getDefaultProvider(types.Network.Sepolia);
+ * const wallet = web3Accounts.create();
+ * const ADDRESS = wallet.address;
+ * const PRIVATE_KEY = wallet.privateKey;
+ * const context = new Web3('some-rpc-url');
*
* const message = "Hello, world!";
* const signature = await new Wallet(PRIVATE_KEY).signMessage(message);
- * // ethers.Wallet can be used as well
- * // const signature = await new ethers.Wallet(PRIVATE_KEY).signMessage(message);
- *
- * const isValidSignature = await utils.isMessageSignatureCorrect(context, ADDRESS, message, signature);
+ * const isValidSignature = await utils.isMessageSignatureCorrect(
+ * web3,
+ * ADDRESS,
+ * message,
+ * signature,
+ * );
* // isValidSignature = true
*/
export async function isMessageSignatureCorrect(
@@ -988,10 +1002,7 @@ export async function isMessageSignatureCorrect(
message: Uint8Array | string,
signature: SignatureLike,
): Promise {
- const msgHash = web3Accounts.hashMessage(
- typeof message === 'string' ? message : web3Utils.bytesToHex(message),
- );
- return await isSignatureCorrect(context, address, msgHash, signature);
+ return await isSignatureCorrect(context, address, web3Utils.toHex(message), signature);
}
/**
diff --git a/test/unit/utils.test.ts b/test/unit/utils.test.ts
index 686865b..291a307 100644
--- a/test/unit/utils.test.ts
+++ b/test/unit/utils.test.ts
@@ -1,3 +1,6 @@
+import { Web3 } from 'web3';
+import * as web3Accounts from 'web3-eth-accounts';
+
import {
// types,
utils,
@@ -277,4 +280,20 @@ describe('utils', () => {
// expect(result).toEqual(tx);
// });
// });
+ describe('#isMessageSignatureCorrect()', () => {
+ it.only('should return true if signature made by a private key was correct', async () => {
+ const wallet = web3Accounts.create();
+ const ADDRESS = wallet.address;
+ const message = 'Hello, world!';
+ const signature = wallet.sign(message).signature;
+ const web3 = new Web3();
+ const isValidSignature = await utils.isMessageSignatureCorrect(
+ web3,
+ ADDRESS,
+ message,
+ signature,
+ );
+ expect(isValidSignature).toBe(true);
+ });
+ });
});
From 1610afa93cad92c420c2699671bacd64a4675b74 Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Thu, 6 Jun 2024 14:27:43 +0200
Subject: [PATCH 12/30] fix and add a test case but still need investigation
---
src/constants.ts | 33 +++++++++++---------
src/utils.ts | 36 +++++++++++-----------
test/unit/utils.test.ts | 68 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 106 insertions(+), 31 deletions(-)
diff --git a/src/constants.ts b/src/constants.ts
index 133c88f..bacbace 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -154,19 +154,24 @@ export const REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT = 800;
* All typed data conforming to the EIP712 standard within zkSync Era.
*/
export const EIP712_TYPES = {
+ EIP712Domain: [
+ { name: 'name', type: 'string' },
+ { name: 'version', type: 'string' },
+ { name: 'chainId', type: 'uint256' },
+ ],
Transaction: [
- {name: 'txType', type: 'uint256'},
- {name: 'from', type: 'uint256'},
- {name: 'to', type: 'uint256'},
- {name: 'gasLimit', type: 'uint256'},
- {name: 'gasPerPubdataByteLimit', type: 'uint256'},
- {name: 'maxFeePerGas', type: 'uint256'},
- {name: 'maxPriorityFeePerGas', type: 'uint256'},
- {name: 'paymaster', type: 'uint256'},
- {name: 'nonce', type: 'uint256'},
- {name: 'value', type: 'uint256'},
- {name: 'data', type: 'bytes'},
- {name: 'factoryDeps', type: 'bytes32[]'},
- {name: 'paymasterInput', type: 'bytes'},
+ { name: 'txType', type: 'uint256' },
+ { name: 'from', type: 'uint256' },
+ { name: 'to', type: 'uint256' },
+ { name: 'gasLimit', type: 'uint256' },
+ { name: 'gasPerPubdataByteLimit', type: 'uint256' },
+ { name: 'maxFeePerGas', type: 'uint256' },
+ { name: 'maxPriorityFeePerGas', type: 'uint256' },
+ { name: 'paymaster', type: 'uint256' },
+ { name: 'nonce', type: 'uint256' },
+ { name: 'value', type: 'uint256' },
+ { name: 'data', type: 'bytes' },
+ { name: 'factoryDeps', type: 'bytes32[]' },
+ { name: 'paymasterInput', type: 'bytes' },
],
- };
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/src/utils.ts b/src/utils.ts
index e13d6d1..4aa287f 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -137,7 +137,7 @@ function recoverSignerAddress(
) {
let message;
if (typeof messageOrData !== 'string') {
- message = web3Abi.getEncodedEip712Data(messageOrData, true);
+ message = web3Abi.getEncodedEip712Data(messageOrData);
} else {
message = messageOrData;
}
@@ -860,8 +860,8 @@ export async function getERC20BridgeCalldata(
}
/**
- * Validates signatures from non-contract account addresses (EOA).
- * Provides similar functionality to `ethers.js` but returns `true`
+ * Validates signatures from non-contract account addresses (EOA: Externally Owned Account).
+ * Provides similar functionality to `new Web3().eth.accounts.recover(message, v, r, s)` but returns `true`
* if the validation process succeeds, otherwise returns `false`.
*
* Called from {@link isSignatureCorrect} for non-contract account addresses.
@@ -873,20 +873,20 @@ export async function getERC20BridgeCalldata(
* @example
*
* import * as web3Accounts from 'web3-eth-accounts';
- *
+ *
* const wallet = web3Accounts.create();
* const ADDRESS = wallet.address;
* const PRIVATE_KEY = wallet.privateKey;
*
* const message = "Hello, world!";
*
- * const signature = web3Accounts.sign(message, PRIVATE_KEY).signature;
+ * const signature = web3Accounts.sign(message, PRIVATE_KEY).signature;
* const isValidSignature = await utils.isECDSASignatureCorrect(ADDRESS, message, signature);
* // isValidSignature = true
*/
function isECDSASignatureCorrect(
address: string,
- message: string,
+ message: string | web3Types.Eip712TypedData,
signature: SignatureLike,
): boolean {
try {
@@ -943,7 +943,7 @@ async function isEIP1271SignatureCorrect(
async function isSignatureCorrect(
context: web3.Web3Context, // or maybe use RpcMethods?
address: string,
- message: string,
+ message: string | web3Types.Eip712TypedData,
signature: SignatureLike,
): Promise {
let isContractAccount;
@@ -961,7 +961,7 @@ async function isSignatureCorrect(
return isECDSASignatureCorrect(address, message, signature);
} else {
const msgHash = web3Accounts.hashMessage(
- typeof message === 'string' ? message : web3Utils.bytesToHex(message),
+ typeof message === 'string' ? message : web3Utils.bytesToHex(message as unknown as string),
);
return await isEIP1271SignatureCorrect(context, address, msgHash, signature);
}
@@ -1017,7 +1017,7 @@ export async function isMessageSignatureCorrect(
*
* @example
*
- * import { Wallet, utils, constants, Provider, EIP712Signer } from "zksync-ethers";
+ * import { Wallet, utils, constants, Provider, EIP712Signer } from "web3-plugin-zksync";
*
* const ADDRESS = "";
* const PRIVATE_KEY = "";
@@ -1032,7 +1032,7 @@ export async function isMessageSignatureCorrect(
* };
*
* const eip712Signer = new EIP712Signer(
- * new Wallet(PRIVATE_KEY), // or new ethers.Wallet(PRIVATE_KEY),
+ * new Wallet(PRIVATE_KEY), // or web3Accounts.privateKeyToAccount(PRIVATE_KEY),
* Number((await context.getNetwork()).chainId)
* );
*
@@ -1044,20 +1044,22 @@ export async function isMessageSignatureCorrect(
export async function isTypedDataSignatureCorrect(
context: web3.Web3Context, // or maybe use RpcMethods?
address: string,
- domain: Extract,
- types: Extract,
- primaryType: Extract,
+ domain: web3Types.Eip712TypedData['domain'],
+ types: web3Types.Eip712TypedData['types'],
value: Record,
- signature: EthereumSignature,
+ signature: SignatureLike,
): Promise {
const data: web3Types.Eip712TypedData = {
domain,
types,
- primaryType,
+ primaryType: 'Transaction',
message: value,
};
- const msgHash = web3Abi.getEncodedEip712Data(data);
- return await isSignatureCorrect(context, address, msgHash, signature);
+ // could be also:
+ // const message = web3Abi.getEncodedEip712Data(data);
+ // return await isSignatureCorrect(context, address, message, signature);
+
+ return await isSignatureCorrect(context, address, data, signature);
}
/**
diff --git a/test/unit/utils.test.ts b/test/unit/utils.test.ts
index 291a307..a7fd6e8 100644
--- a/test/unit/utils.test.ts
+++ b/test/unit/utils.test.ts
@@ -296,4 +296,72 @@ describe('utils', () => {
expect(isValidSignature).toBe(true);
});
});
+
+ describe('#isTypedDataSignatureCorrect()', () => {
+ // TODO: Needs investigation
+ it.skip('should return true if correct', async () => {
+ // const wallet = web3Accounts.create();
+ // const ADDRESS = wallet.address;
+ // const PRIVATE_KEY = wallet.privateKey;
+
+ const ADDRESS = '0x99F3629e38c617cb619682f721Aaf9F61a3DE3d3';
+ const PRIVATE_KEY = '0x5b032dc95add073bbacb2a2cbe0d667855cca807abe1461c72257b7ee0d7d334';
+ console.log('ADDRESS', ADDRESS);
+ console.log('PRIVATE_KEY', PRIVATE_KEY);
+
+ const web3 = new Web3();
+
+ const tx = {
+ type: 113,
+ chainId: 300,
+ from: ADDRESS,
+ to: '0xa61464658AfeAf65CccaaFD3a512b69A83B77618',
+ value: BigInt(7_000_000),
+ };
+
+ // const eip712Signer = new EIP712Signer(new Wallet(PRIVATE_KEY), web3.config.chainId);
+
+ // const signInput = EIP712Signer.getSignInput(tx);
+
+ const signInput = {
+ txType: 113,
+ from: '0x99F3629e38c617cb619682f721Aaf9F61a3DE3d3',
+ to: '0xa61464658AfeAf65CccaaFD3a512b69A83B77618',
+ gasLimit: 0n,
+ gasPerPubdataByteLimit: 50000,
+ maxFeePerGas: 0n,
+ maxPriorityFeePerGas: 0n,
+ paymaster: '0x0000000000000000000000000000000000000000',
+ nonce: 0,
+ value: 7000000n,
+ data: '0x',
+ factoryDeps: [],
+ paymasterInput: '0x',
+ };
+ const domain = {
+ name: 'zkSync',
+ version: '2',
+ chainId: tx.chainId,
+ };
+ console.log('domain', domain);
+
+ // const signature = await eip712Signer.sign(tx);
+
+ const signature =
+ '0x14920a5306c8b739fd2fbdd6bb933c54c05391ab9454741b5fa1132c31c6f35d4f8a716939e43df6eb660b5298d8354d08710a0d94e8d0c14dc88032cac5deda1b';
+ console.log('signature\n\t', signature);
+
+ const isValidSignature = await utils.isTypedDataSignatureCorrect(
+ web3,
+ ADDRESS,
+ domain,
+ constants.EIP712_TYPES,
+ signInput,
+ signature,
+ );
+ console.log('isValidSignature', isValidSignature);
+ expect(isValidSignature).toBe(true);
+ });
+ });
+
});
From 8798d6edd4f24d1a9c0c5ee1dd7be0a597b52613 Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Thu, 6 Jun 2024 16:44:04 +0200
Subject: [PATCH 13/30] add contracts inside the plugin instance
---
package.json | 3 +-
src/plugin.ts | 128 ++++++++++++++++++++++++++++++-
yarn.lock | 204 +++++++++++++++++---------------------------------
3 files changed, 198 insertions(+), 137 deletions(-)
diff --git a/package.json b/package.json
index d4c57a4..5a9160c 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
"web3-core": "^4.4.0",
"web3-eth-abi": "^4.2.2",
"web3-eth-accounts": "^4.1.2",
+ "web3-eth-contract": "^4.5.0",
"web3-types": "^1.6.0",
"web3-utils": "^4.3.0"
},
@@ -39,7 +40,7 @@
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
- "web3": "^4.4.0"
+ "web3": "^4.9.0"
},
"peerDependencies": {
"web3": ">= 4.0.3"
diff --git a/src/plugin.ts b/src/plugin.ts
index fc922e8..dbe69a5 100644
--- a/src/plugin.ts
+++ b/src/plugin.ts
@@ -1,10 +1,21 @@
-import type { Address } from 'web3';
-import { Web3PluginBase, Contract } from 'web3';
import type { Web3RequestManager } from 'web3-core';
+import type { Address } from 'web3-types';
+
+import { Contract } from 'web3-eth-contract';
+import { Web3Context, Web3PluginBase } from 'web3-core';
+
import { IERC20ABI } from './contracts/IERC20';
import { RpcMethods } from './rpc.methods';
import { ETH_ADDRESS, ZERO_ADDRESS } from './constants';
+
import { IL2BridgeABI } from './contracts/IL2Bridge';
+import { IZkSyncABI } from './contracts/IZkSyncStateTransition';
+import { IBridgehubABI } from './contracts/IBridgehub';
+import { IContractDeployerABI } from './contracts/IContractDeployer';
+import { IL1MessengerABI } from './contracts/IL1Messenger';
+import { IERC1271ABI } from './contracts/IERC1271';
+import { IL1BridgeABI } from './contracts/IL1ERC20Bridge';
+import { INonceHolderABI } from './contracts/INonceHolder';
export class ZkSyncPlugin extends Web3PluginBase {
public pluginNamespace = 'zkSync';
@@ -15,6 +26,44 @@ export class ZkSyncPlugin extends Web3PluginBase {
public _rpc?: RpcMethods;
public _l2BridgeContracts: Record>;
public _erc20Contracts: Record>;
+ Contracts: {
+ /**
+ * The web3.js Contract instance for the `ZkSync` interface.
+ */
+ ZkSyncMainContract: Contract;
+ /**
+ * The ABI of the `Bridgehub` interface.
+ */
+ BridgehubContract: Contract;
+ /**
+ * The web3.js Contract instance for the `IContractDeployer` interface, which is utilized for deploying smart contracts.
+ */
+ ContractDeployerContract: Contract;
+ /**
+ * The web3.js Contract instance for the `IL1Messenger` interface, which is utilized for sending messages from the L2 to L1.
+ */
+ L1MessengerContract: Contract;
+ /**
+ * The web3.js Contract instance for the `IERC20` interface, which is utilized for interacting with ERC20 tokens.
+ */
+ IERC20Contract: Contract;
+ /**
+ * The web3.js Contract instance for the `IERC1271` interface, which is utilized for signature validation by contracts.
+ */
+ IERC1271Contract: Contract;
+ /**
+ * The web3.js Contract instance for the `IL1Bridge` interface, which is utilized for transferring ERC20 tokens from L1 to L2.
+ */
+ L1BridgeContract: Contract;
+ /**
+ * The web3.js Contract instance for the `IL2Bridge` interface, which is utilized for transferring ERC20 tokens from L2 to L1.
+ */
+ L2BridgeContract: Contract;
+ /**
+ * The web3.js Contract instance for the `INonceHolder` interface, which is utilized for managing deployment nonces.
+ */
+ NonceHolderContract: Contract;
+ };
constructor() {
super();
@@ -25,6 +74,81 @@ export class ZkSyncPlugin extends Web3PluginBase {
this.wethBridgeL2 = '';
this._l2BridgeContracts = {};
this._erc20Contracts = {};
+
+ this.initializeContractsInstances();
+ }
+
+ private async initializeContractsInstances() {
+ // TODO: optionally fetch and set the contract addresses from the Adapter once methods like getBridgehubContract and getDefaultBridgeAddresses are implemented
+ this.Contracts = {
+ /**
+ * The web3.js Contract instance for the `ZkSync` interface.
+ * @constant
+ */
+ ZkSyncMainContract: new Contract(IZkSyncABI),
+
+ /**
+ * The ABI of the `Bridgehub` interface.
+ * @constant
+ */
+ BridgehubContract: new Contract(IBridgehubABI),
+
+ /**
+ * The web3.js Contract instance for the `IContractDeployer` interface, which is utilized for deploying smart contracts.
+ * @constant
+ */
+ ContractDeployerContract: new Contract(IContractDeployerABI),
+
+ /**
+ * The web3.js Contract instance for the `IL1Messenger` interface, which is utilized for sending messages from the L2 to L1.
+ * @constant
+ */
+ L1MessengerContract: new Contract(IL1MessengerABI),
+
+ /**
+ * The web3.js Contract instance for the `IERC20` interface, which is utilized for interacting with ERC20 tokens.
+ * @constant
+ */
+ IERC20Contract: new Contract(IERC20ABI),
+
+ /**
+ * The web3.js Contract instance for the `IERC1271` interface, which is utilized for signature validation by contracts.
+ * @constant
+ */
+ IERC1271Contract: new Contract(IERC1271ABI),
+
+ /**
+ * The web3.js Contract instance for the `IL1Bridge` interface, which is utilized for transferring ERC20 tokens from L1 to L2.
+ * @constant
+ */
+ L1BridgeContract: new Contract(IL1BridgeABI),
+
+ /**
+ * The web3.js Contract instance for the `IL2Bridge` interface, which is utilized for transferring ERC20 tokens from L2 to L1.
+ * @constant
+ */
+ L2BridgeContract: new Contract(IL2BridgeABI),
+
+ /**
+ * The web3.js Contract instance for the `INonceHolder` interface, which is utilized for managing deployment nonces.
+ * @constant
+ */
+ NonceHolderContract: new Contract(INonceHolderABI),
+ };
+ }
+
+ public link(parentContext: Web3Context): void {
+ super.link(parentContext);
+
+ this.Contracts.ZkSyncMainContract.link(parentContext);
+ this.Contracts.BridgehubContract.link(parentContext);
+ this.Contracts.ContractDeployerContract.link(parentContext);
+ this.Contracts.L1MessengerContract.link(parentContext);
+ this.Contracts.IERC20Contract.link(parentContext);
+ this.Contracts.IERC1271Contract.link(parentContext);
+ this.Contracts.L1BridgeContract.link(parentContext);
+ this.Contracts.L2BridgeContract.link(parentContext);
+ this.Contracts.NonceHolderContract.link(parentContext);
}
/**
diff --git a/yarn.lock b/yarn.lock
index 05168b1..57f63e3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5689,7 +5689,7 @@ walker@^1.0.8:
dependencies:
makeerror "1.0.12"
-web3-core@^4.3.0, web3-core@^4.3.1:
+web3-core@^4.3.0:
version "4.3.1"
resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.3.1.tgz#5c3b5b59f1e31537a64237caa5fd83f5ffd7b0f3"
integrity sha512-xa3w5n/ESxp5HIbrwsYBhpAPx2KI5LprjRFEtRwP0GpqqhTcCSMMYoyItRqQQ+k9YnB0PoFpWJfJI6Qn5x8YUQ==
@@ -5704,22 +5704,6 @@ web3-core@^4.3.0, web3-core@^4.3.1:
optionalDependencies:
web3-providers-ipc "^4.0.7"
-web3-core@^4.3.2:
- version "4.3.2"
- resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.3.2.tgz#f24b11d6a57dee527de8d42c89de2a439f0c4bed"
- integrity sha512-uIMVd/j4BgOnwfpY8ZT+QKubOyM4xohEhFZXz9xB8wimXWMMlYVlIK/TbfHqFolS9uOerdSGhsMbcK9lETae8g==
- dependencies:
- web3-errors "^1.1.4"
- web3-eth-accounts "^4.1.0"
- web3-eth-iban "^4.0.7"
- web3-providers-http "^4.1.0"
- web3-providers-ws "^4.0.7"
- web3-types "^1.3.1"
- web3-utils "^4.1.0"
- web3-validator "^2.0.3"
- optionalDependencies:
- web3-providers-ipc "^4.0.7"
-
web3-core@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.4.0.tgz#f2cd48aecda5ec34170edf7470001f90fdea1dc6"
@@ -5761,17 +5745,6 @@ web3-eth-abi@^4.1.4:
web3-utils "^4.0.7"
web3-validator "^2.0.3"
-web3-eth-abi@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.2.0.tgz#398d415e7783442d06fb7939e40ce3de7a3f04e9"
- integrity sha512-x7dUCmk6th+5N63s5kUusoNtsDJKUUQgl9+jECvGTBOTiyHe/V6aOY0120FUjaAGaapOnR7BImQdhqHv6yT2YQ==
- dependencies:
- abitype "0.7.1"
- web3-errors "^1.1.4"
- web3-types "^1.3.1"
- web3-utils "^4.1.1"
- web3-validator "^2.0.4"
-
web3-eth-abi@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.2.2.tgz#d7592e2cc113fd34da3fb4c933537ddf8639d9b2"
@@ -5796,19 +5769,6 @@ web3-eth-accounts@^4.1.0:
web3-utils "^4.0.7"
web3-validator "^2.0.3"
-web3-eth-accounts@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-4.1.1.tgz#55225e5510b961e1cacb4eccc996544998e907fc"
- integrity sha512-9JqhRi1YhO1hQOEmmBHgEGsME/B1FHMxpA/AK3vhpvQ8QeP6KbJW+cForTLfPpUbkmPxnRunG4PNNaETNlZfrA==
- dependencies:
- "@ethereumjs/rlp" "^4.0.1"
- crc-32 "^1.2.2"
- ethereum-cryptography "^2.0.0"
- web3-errors "^1.1.4"
- web3-types "^1.3.1"
- web3-utils "^4.1.1"
- web3-validator "^2.0.4"
-
web3-eth-accounts@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-4.1.2.tgz#652d6e3daf4d6cb3fe67cec6a878e768f6e8b8e8"
@@ -5822,46 +5782,33 @@ web3-eth-accounts@^4.1.2:
web3-utils "^4.2.3"
web3-validator "^2.0.5"
-web3-eth-contract@^4.1.2:
- version "4.1.3"
- resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-4.1.3.tgz#15dd4c978eaf0d8f894b2c1f3e9f94edd29ff57c"
- integrity sha512-F6e3eyetUDiNOb78EDVJtNOb0H1GPz3xAZH8edSfYdhaxI9tTutP2V3p++kh2ZJ/RrdE2+xil7H/nPLgHymBvg==
- dependencies:
- web3-core "^4.3.1"
- web3-errors "^1.1.4"
- web3-eth "^4.3.1"
- web3-eth-abi "^4.1.4"
- web3-types "^1.3.1"
- web3-utils "^4.0.7"
- web3-validator "^2.0.3"
-
-web3-eth-contract@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-4.2.0.tgz#73f70b19217cd4854211c05846f0c841763b3b29"
- integrity sha512-K7bUypsomTs8/Oa0Lgvq5plsSB5afgKJ79NMuXxvC5jfV+AxNrQyKcr5Vd5yEGNqrdQuIPduGQa8DpuY+rMe1g==
+web3-eth-contract@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-4.5.0.tgz#954c9cf4c055f3f2c33189bdd69093a8e20a569a"
+ integrity sha512-AX6OiDrIryz/T28k9Xz0gXpUrlOUjcooEgGluu2s5dFDWCPM/zlN5RsUZlXZiXpQyj52VCUy5+bkvu3yDPA4fg==
dependencies:
- web3-core "^4.3.2"
- web3-errors "^1.1.4"
- web3-eth "^4.4.0"
- web3-eth-abi "^4.2.0"
- web3-types "^1.3.1"
- web3-utils "^4.1.1"
- web3-validator "^2.0.4"
+ web3-core "^4.4.0"
+ web3-errors "^1.2.0"
+ web3-eth "^4.7.0"
+ web3-eth-abi "^4.2.2"
+ web3-types "^1.6.0"
+ web3-utils "^4.3.0"
+ web3-validator "^2.0.6"
-web3-eth-ens@^4.0.8:
- version "4.0.8"
- resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-4.0.8.tgz#f4e0a018ce6cc69e37007ee92063867feb5994f0"
- integrity sha512-nj0JfeD45BbzVJcVYpUJnSo8iwDcY9CQ7CZhhIVVOFjvpMAPw0zEwjTvZEIQyCW61OoDG9xcBzwxe2tZoYhMRw==
+web3-eth-ens@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-4.3.0.tgz#f44f279a4a07eae36e3de8384989ed069201a795"
+ integrity sha512-QpiKT9GqJouH5kEI/pRFprh88YPCtbht2Ym6rrklZ+VoWl9D+wLfbwvW7Aox349FS7k0UX2qVins5tVNLJ5GCQ==
dependencies:
"@adraffy/ens-normalize" "^1.8.8"
- web3-core "^4.3.0"
- web3-errors "^1.1.3"
- web3-eth "^4.3.1"
- web3-eth-contract "^4.1.2"
- web3-net "^4.0.7"
- web3-types "^1.3.0"
- web3-utils "^4.0.7"
- web3-validator "^2.0.3"
+ web3-core "^4.4.0"
+ web3-errors "^1.2.0"
+ web3-eth "^4.7.0"
+ web3-eth-contract "^4.5.0"
+ web3-net "^4.1.0"
+ web3-types "^1.6.0"
+ web3-utils "^4.3.0"
+ web3-validator "^2.0.6"
web3-eth-iban@^4.0.7:
version "4.0.7"
@@ -5902,22 +5849,22 @@ web3-eth@^4.3.1:
web3-utils "^4.0.7"
web3-validator "^2.0.3"
-web3-eth@^4.4.0:
- version "4.4.0"
- resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.4.0.tgz#755c34a769109836d122a53b33814d63f9ec5a65"
- integrity sha512-HswKdzF44wUrciRAtEJaml9O7rDYDxElHmFs+27WcO3nel2zku+n0xs4e2ZAehfrCZ+05/y7TgnOZnaDU8Fb/A==
+web3-eth@^4.7.0:
+ version "4.7.0"
+ resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.7.0.tgz#7d18815c7a79c200552bd0df8d3127f7532b3cc2"
+ integrity sha512-gqlWq4Xjz+yKL2MdxQ+BgR3F4CRo4AXWDXzftb3LDzvauEfjk/yRyoxkMSK4S9RIG96ylRImS172cV6cYzcukw==
dependencies:
setimmediate "^1.0.5"
- web3-core "^4.3.2"
- web3-errors "^1.1.4"
- web3-eth-abi "^4.2.0"
- web3-eth-accounts "^4.1.1"
- web3-net "^4.0.7"
+ web3-core "^4.4.0"
+ web3-errors "^1.2.0"
+ web3-eth-abi "^4.2.2"
+ web3-eth-accounts "^4.1.2"
+ web3-net "^4.1.0"
web3-providers-ws "^4.0.7"
- web3-rpc-methods "^1.1.4"
- web3-types "^1.3.1"
- web3-utils "^4.1.1"
- web3-validator "^2.0.4"
+ web3-rpc-methods "^1.3.0"
+ web3-types "^1.6.0"
+ web3-utils "^4.3.0"
+ web3-validator "^2.0.6"
web3-net@^4.0.7:
version "4.0.7"
@@ -5929,6 +5876,16 @@ web3-net@^4.0.7:
web3-types "^1.3.0"
web3-utils "^4.0.7"
+web3-net@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-4.1.0.tgz#db7bde675e58b153339e4f149f29ec0410d6bab2"
+ integrity sha512-WWmfvHVIXWEoBDWdgKNYKN8rAy6SgluZ0abyRyXOL3ESr7ym7pKWbfP4fjApIHlYTh8tNqkrdPfM4Dyi6CA0SA==
+ dependencies:
+ web3-core "^4.4.0"
+ web3-rpc-methods "^1.3.0"
+ web3-types "^1.6.0"
+ web3-utils "^4.3.0"
+
web3-providers-http@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-4.1.0.tgz#8d7afda67d1d8542ca85b30f60a3d1fe1993b561"
@@ -5969,14 +5926,14 @@ web3-rpc-methods@^1.1.3:
web3-types "^1.3.0"
web3-validator "^2.0.3"
-web3-rpc-methods@^1.1.4:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/web3-rpc-methods/-/web3-rpc-methods-1.1.4.tgz#0b478e38231d3f3260ac504307c6dc4059af6fda"
- integrity sha512-LTFNg4LFaeU8K9ecuT8fHDp/LOXyxCneeZjCrRYIW1u82Ly52SrY55FIzMIISGoG/iT5Wh7UiHOB3CQsWLBmbQ==
+web3-rpc-methods@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/web3-rpc-methods/-/web3-rpc-methods-1.3.0.tgz#d5ee299a69389d63822d354ddee2c6a121a6f670"
+ integrity sha512-/CHmzGN+IYgdBOme7PdqzF+FNeMleefzqs0LVOduncSaqsppeOEoskLXb2anSpzmQAP3xZJPaTrkQPWSJMORig==
dependencies:
- web3-core "^4.3.2"
- web3-types "^1.3.1"
- web3-validator "^2.0.3"
+ web3-core "^4.4.0"
+ web3-types "^1.6.0"
+ web3-validator "^2.0.6"
web3-types@^1.3.0, web3-types@^1.3.1:
version "1.3.1"
@@ -5998,16 +5955,6 @@ web3-utils@^4.0.7:
web3-types "^1.3.0"
web3-validator "^2.0.3"
-web3-utils@^4.1.0, web3-utils@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-4.1.1.tgz#88c0fe404abc3f038b7318f3a31141676cefeb63"
- integrity sha512-5AOmLKH6QuwHunLCNdVFlPSDE+T88bJYRQP+HWYoKNbI4STALCYQiJvj7LXE+Ed6cPfqANaK/LwKNbMPLCPFwA==
- dependencies:
- ethereum-cryptography "^2.0.0"
- web3-errors "^1.1.4"
- web3-types "^1.3.1"
- web3-validator "^2.0.4"
-
web3-utils@^4.2.3, web3-utils@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-4.3.0.tgz#c18918f0d692f745d622d22172406f6102528860"
@@ -6030,17 +5977,6 @@ web3-validator@^2.0.3:
web3-types "^1.3.0"
zod "^3.21.4"
-web3-validator@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/web3-validator/-/web3-validator-2.0.4.tgz#66f34c94f21a3c94d0dc2a2d30deb8a379825d38"
- integrity sha512-qRxVePwdW+SByOmTpDZFWHIUAa7PswvxNszrOua6BoGqAhERo5oJZBN+EbWtK/+O+ApNxt5FR3nCPmiZldiOQA==
- dependencies:
- ethereum-cryptography "^2.0.0"
- util "^0.12.5"
- web3-errors "^1.1.4"
- web3-types "^1.3.1"
- zod "^3.21.4"
-
web3-validator@^2.0.5, web3-validator@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/web3-validator/-/web3-validator-2.0.6.tgz#a0cdaa39e1d1708ece5fae155b034e29d6a19248"
@@ -6052,27 +5988,27 @@ web3-validator@^2.0.5, web3-validator@^2.0.6:
web3-types "^1.6.0"
zod "^3.21.4"
-web3@^4.4.0:
- version "4.4.0"
- resolved "https://registry.yarnpkg.com/web3/-/web3-4.4.0.tgz#83e5906675608adf9a14841f257e441c9154a8c7"
- integrity sha512-WcFA3RgE+cyM96MHLPs5Ec155nkYQuwhjk3JIhxcy6J1QrZK2zvKGzTQCCYHe3JhraduNTMbWy+EeNNGGji9+Q==
+web3@^4.9.0:
+ version "4.9.0"
+ resolved "https://registry.yarnpkg.com/web3/-/web3-4.9.0.tgz#2d18f6a48f366601aef32308260542ed14bd2b1b"
+ integrity sha512-O0R90ijjyqUlG1Wk3SXqfYMU1ZGJvLCAF/WfSg/isDz/0Fkpqxoj893wauZ+ieRvTXITlbQHVXGfpp8qrhWZ1g==
dependencies:
- web3-core "^4.3.2"
- web3-errors "^1.1.4"
- web3-eth "^4.4.0"
- web3-eth-abi "^4.2.0"
- web3-eth-accounts "^4.1.1"
- web3-eth-contract "^4.2.0"
- web3-eth-ens "^4.0.8"
+ web3-core "^4.4.0"
+ web3-errors "^1.2.0"
+ web3-eth "^4.7.0"
+ web3-eth-abi "^4.2.2"
+ web3-eth-accounts "^4.1.2"
+ web3-eth-contract "^4.5.0"
+ web3-eth-ens "^4.3.0"
web3-eth-iban "^4.0.7"
web3-eth-personal "^4.0.8"
- web3-net "^4.0.7"
+ web3-net "^4.1.0"
web3-providers-http "^4.1.0"
web3-providers-ws "^4.0.7"
- web3-rpc-methods "^1.1.4"
- web3-types "^1.3.1"
- web3-utils "^4.1.1"
- web3-validator "^2.0.4"
+ web3-rpc-methods "^1.3.0"
+ web3-types "^1.6.0"
+ web3-utils "^4.3.0"
+ web3-validator "^2.0.6"
webidl-conversions@^3.0.0:
version "3.0.1"
From 9cf4a60ed02d8459d97c41a59a91755655ffcc72 Mon Sep 17 00:00:00 2001
From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Thu, 6 Jun 2024 16:49:56 +0200
Subject: [PATCH 14/30] tiny fix
---
src/plugin.ts | 84 ++++++++++++-----------------------------
test/unit/utils.test.ts | 2 +-
2 files changed, 26 insertions(+), 60 deletions(-)
diff --git a/src/plugin.ts b/src/plugin.ts
index dbe69a5..992fb73 100644
--- a/src/plugin.ts
+++ b/src/plugin.ts
@@ -26,7 +26,7 @@ export class ZkSyncPlugin extends Web3PluginBase {
public _rpc?: RpcMethods;
public _l2BridgeContracts: Record>;
public _erc20Contracts: Record>;
- Contracts: {
+ public Contracts: {
/**
* The web3.js Contract instance for the `ZkSync` interface.
*/
@@ -74,67 +74,33 @@ export class ZkSyncPlugin extends Web3PluginBase {
this.wethBridgeL2 = '';
this._l2BridgeContracts = {};
this._erc20Contracts = {};
-
- this.initializeContractsInstances();
- }
-
- private async initializeContractsInstances() {
- // TODO: optionally fetch and set the contract addresses from the Adapter once methods like getBridgehubContract and getDefaultBridgeAddresses are implemented
this.Contracts = {
- /**
- * The web3.js Contract instance for the `ZkSync` interface.
- * @constant
- */
- ZkSyncMainContract: new Contract(IZkSyncABI),
-
- /**
- * The ABI of the `Bridgehub` interface.
- * @constant
- */
- BridgehubContract: new Contract(IBridgehubABI),
-
- /**
- * The web3.js Contract instance for the `IContractDeployer` interface, which is utilized for deploying smart contracts.
- * @constant
- */
- ContractDeployerContract: new Contract(IContractDeployerABI),
-
- /**
- * The web3.js Contract instance for the `IL1Messenger` interface, which is utilized for sending messages from the L2 to L1.
- * @constant
- */
- L1MessengerContract: new Contract(IL1MessengerABI),
-
- /**
- * The web3.js Contract instance for the `IERC20` interface, which is utilized for interacting with ERC20 tokens.
- * @constant
- */
- IERC20Contract: new Contract(IERC20ABI),
-
- /**
- * The web3.js Contract instance for the `IERC1271` interface, which is utilized for signature validation by contracts.
- * @constant
- */
- IERC1271Contract: new Contract(IERC1271ABI),
-
- /**
- * The web3.js Contract instance for the `IL1Bridge` interface, which is utilized for transferring ERC20 tokens from L1 to L2.
- * @constant
- */
- L1BridgeContract: new Contract(IL1BridgeABI),
+ ZkSyncMainContract: new Contract(IZkSyncABI, ''),
+ BridgehubContract: new Contract(IBridgehubABI, ''),
+ ContractDeployerContract: new Contract(IContractDeployerABI, ''),
+ L1MessengerContract: new Contract(IL1MessengerABI, ''),
+ IERC20Contract: new Contract(IERC20ABI, ''),
+ IERC1271Contract: new Contract(IERC1271ABI, ''),
+ L1BridgeContract: new Contract(IL1BridgeABI, ''),
+ L2BridgeContract: new Contract(IL2BridgeABI, ''),
+ NonceHolderContract: new Contract(INonceHolderABI, ''),
+ };
- /**
- * The web3.js Contract instance for the `IL2Bridge` interface, which is utilized for transferring ERC20 tokens from L2 to L1.
- * @constant
- */
- L2BridgeContract: new Contract(IL2BridgeABI),
+ this.fillContractsAddresses();
+ }
- /**
- * The web3.js Contract instance for the `INonceHolder` interface, which is utilized for managing deployment nonces.
- * @constant
- */
- NonceHolderContract: new Contract(INonceHolderABI),
- };
+ private async fillContractsAddresses() {
+ // TODO: optionally fetch and set the contract addresses from the Adapter,
+ // nonce methods like getBridgehubContract and getDefaultBridgeAddresses are implemented.
+ // this.Contracts.ZkSyncMainContract.options.address =
+ // this.Contracts.BridgehubContract.options.address =
+ // this.Contracts.ContractDeployerContract.options.address =
+ // this.Contracts.L1MessengerContract.options.address =
+ // this.Contracts.IERC20Contract.options.address =
+ // this.Contracts.IERC1271Contract.options.address =
+ // this.Contracts.L1BridgeContract.options.address =
+ // this.Contracts.L2BridgeContract.options.address =
+ // this.Contracts.NonceHolderContract.options.address =
}
public link(parentContext: Web3Context): void {
diff --git a/test/unit/utils.test.ts b/test/unit/utils.test.ts
index a7fd6e8..d7ee1c0 100644
--- a/test/unit/utils.test.ts
+++ b/test/unit/utils.test.ts
@@ -281,7 +281,7 @@ describe('utils', () => {
// });
// });
describe('#isMessageSignatureCorrect()', () => {
- it.only('should return true if signature made by a private key was correct', async () => {
+ it('should return true if signature made by a private key was correct', async () => {
const wallet = web3Accounts.create();
const ADDRESS = wallet.address;
const message = 'Hello, world!';
From 4f34c20a9fce71336e8f17afa6adc465c354830e Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Thu, 6 Jun 2024 14:24:29 -0400
Subject: [PATCH 15/30] eip712 class
---
package.json | 5 +-
src/constants.ts | 30 +-
src/eip712/EIP712Transaction.ts | 572 ++++++++++++++++++++++++++++++++
src/eip712/constants.ts | 12 +
src/eip712/types.ts | 87 +++++
src/plugin.ts | 16 +-
src/rpc.methods.ts | 16 +-
src/types.ts | 22 +-
src/utils.ts | 118 ++-----
test/fixtures.ts | 76 +++--
test/integration/rpc.test.ts | 4 +-
test/unit/utils.test.ts | 146 ++++----
test/utils.ts | 8 +-
yarn.lock | 44 +++
14 files changed, 919 insertions(+), 237 deletions(-)
create mode 100644 src/eip712/EIP712Transaction.ts
create mode 100644 src/eip712/constants.ts
create mode 100644 src/eip712/types.ts
diff --git a/package.json b/package.json
index 9301bf2..9637d20 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
},
"scripts": {
"lint": "eslint '{src,test}/**/*.ts'",
+ "lint:fix": "eslint '{src,test}/**/*.ts' --fix",
"build": "tsc --project tsconfig.build.json",
"test": "jest --config=./test/jest.config.js"
},
@@ -22,12 +23,14 @@
"url": "git+ssh://git@github.com/web3/web3-plugin-zksync.git"
},
"dependencies": {
+ "ethereum-cryptography": "^2.1.3",
"hardhat": "^2.19.4",
"web3-core": "^4.4.0",
"web3-eth-abi": "^4.2.2",
"web3-eth-accounts": "^4.1.2",
"web3-types": "^1.6.0",
- "web3-utils": "^4.3.0"
+ "web3-utils": "^4.3.0",
+ "web3-validator": "^2.0.6"
},
"devDependencies": {
"@chainsafe/eslint-config": "^2.1.1",
diff --git a/src/constants.ts b/src/constants.ts
index 133c88f..08d403f 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -1,4 +1,4 @@
-import * as web3Types from 'web3-types';
+import type * as web3Types from 'web3-types';
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
@@ -155,18 +155,18 @@ export const REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT = 800;
*/
export const EIP712_TYPES = {
Transaction: [
- {name: 'txType', type: 'uint256'},
- {name: 'from', type: 'uint256'},
- {name: 'to', type: 'uint256'},
- {name: 'gasLimit', type: 'uint256'},
- {name: 'gasPerPubdataByteLimit', type: 'uint256'},
- {name: 'maxFeePerGas', type: 'uint256'},
- {name: 'maxPriorityFeePerGas', type: 'uint256'},
- {name: 'paymaster', type: 'uint256'},
- {name: 'nonce', type: 'uint256'},
- {name: 'value', type: 'uint256'},
- {name: 'data', type: 'bytes'},
- {name: 'factoryDeps', type: 'bytes32[]'},
- {name: 'paymasterInput', type: 'bytes'},
+ { name: 'txType', type: 'uint256' },
+ { name: 'from', type: 'uint256' },
+ { name: 'to', type: 'uint256' },
+ { name: 'gasLimit', type: 'uint256' },
+ { name: 'gasPerPubdataByteLimit', type: 'uint256' },
+ { name: 'maxFeePerGas', type: 'uint256' },
+ { name: 'maxPriorityFeePerGas', type: 'uint256' },
+ { name: 'paymaster', type: 'uint256' },
+ { name: 'nonce', type: 'uint256' },
+ { name: 'value', type: 'uint256' },
+ { name: 'data', type: 'bytes' },
+ { name: 'factoryDeps', type: 'bytes32[]' },
+ { name: 'paymasterInput', type: 'bytes' },
],
- };
\ No newline at end of file
+};
diff --git a/src/eip712/EIP712Transaction.ts b/src/eip712/EIP712Transaction.ts
new file mode 100644
index 0000000..a803d2c
--- /dev/null
+++ b/src/eip712/EIP712Transaction.ts
@@ -0,0 +1,572 @@
+/*
+This file is part of web3.js.
+
+web3.js is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+web3.js is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with web3.js. If not, see .
+*/
+import type { Bytes } from 'web3-types';
+import { RLP } from '@ethereumjs/rlp';
+import { keccak256 } from 'ethereum-cryptography/keccak.js';
+import {
+ bytesToHex,
+ bytesToUint8Array,
+ hexToBytes,
+ toBigInt,
+ toChecksumAddress,
+ toHex,
+ uint8ArrayConcat,
+} from 'web3-utils';
+import { validateNoLeadingZeroes } from 'web3-validator';
+import {
+ bigIntToHex,
+ bigIntToUnpaddedUint8Array,
+ ecrecover,
+ toUint8Array,
+ uint8ArrayToBigInt,
+ BaseTransaction,
+ Capability,
+} from 'web3-eth-accounts';
+import type { Common, JsonTx, TxOptions, TxValuesArray } from 'web3-eth-accounts';
+import { DEFAULT_GAS_PER_PUBDATA_LIMIT, ZERO_ADDRESS } from '../constants';
+import { concat, hashBytecode, toBytes } from '../utils';
+import type { Address } from '../types';
+import { MAX_INTEGER } from './constants';
+import type { Eip712TxData, TypedDataDomain, Eip712Meta } from './types';
+
+export const EIP712_TX_TYPE = 113; // 0x71
+const EIP712_TX_TYPE_UINT8ARRAY = hexToBytes(EIP712_TX_TYPE.toString(16).padStart(2, '0'));
+
+function meetsEIP155(_v: bigint, chainId: bigint) {
+ const v = Number(_v);
+ const chainIdDoubled = Number(chainId) * 2;
+ return v === chainIdDoubled + 35 || v === chainIdDoubled + 36;
+}
+
+/**
+ * Class to create the EIP-712 Transaction object.
+ * - TransactionType: 113
+ * - EIP: [EIP-712](https://eips.ethereum.org/EIPS/eip-712)
+ */
+
+export class EIP712Transaction extends BaseTransaction {
+ private txData: Eip712TxData;
+ public readonly chainId?: bigint;
+ public readonly gasPrice: bigint;
+ public eip712Domain: TypedDataDomain;
+
+ public readonly common: Common;
+
+ /**
+ * Instantiate a transaction from a data dictionary.
+ *
+ * Format: { nonce, gasPrice, gasLimit, to, value, data, v, r, s }
+ *
+ * Notes:
+ * - All parameters are optional and have some basic default values
+ */
+ public static fromTxData(txData: Eip712TxData, opts: TxOptions = {}) {
+ return new EIP712Transaction(txData, opts);
+ }
+
+ /**
+ * Instantiate a transaction from the serialized tx.
+ *
+ * Format: `rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s])`
+ */
+ public static fromSerializedTx(serialized: Uint8Array, opts: TxOptions = {}) {
+ const values = RLP.decode(serialized);
+
+ if (!Array.isArray(values)) {
+ throw new Error('Invalid serialized tx input. Must be array');
+ }
+
+ return this.fromValuesArray(values as Uint8Array[], opts);
+ }
+
+ /**
+ * Create a transaction from a values array.
+ *
+ * Format: `[nonce, gasPrice, gasLimit, to, value, data, v, r, s]`
+ */
+ public static fromValuesArray(values: TxValuesArray, opts: TxOptions = {}) {
+ // If length is not 6, it has length 9. If v/r/s are empty Uint8Array, it is still an unsigned transaction
+ // This happens if you get the RLP data from `raw()`
+ if (values.length !== 6 && values.length !== 9) {
+ throw new Error(
+ 'Invalid transaction. Only expecting 6 values (for unsigned tx) or 9 values (for signed tx).',
+ );
+ }
+
+ const [nonce, gasPrice, gasLimit, to, value, data, v, r, s] = values;
+
+ validateNoLeadingZeroes({ nonce, gasPrice, gasLimit, value, v, r, s });
+
+ return new EIP712Transaction(
+ {
+ nonce,
+ // @ts-ignore
+ gasPrice,
+ gasLimit,
+ to,
+ value,
+ data,
+ v,
+ r,
+ s,
+ },
+ opts,
+ );
+ }
+
+ /**
+ * This constructor takes the values, validates them, assigns them and freezes the object.
+ *
+ * It is not recommended to use this constructor directly. Instead use
+ * the static factory methods to assist in creating a Transaction object from
+ * varying data types.
+ */
+ public constructor(txData: Eip712TxData, opts: TxOptions = {}) {
+ super({ ...txData, type: EIP712_TX_TYPE }, opts);
+
+ this.txData = EIP712Transaction.getSignInput(txData);
+ this.eip712Domain = {
+ name: 'zkSync',
+ version: '2',
+ chainId: txData.chainId,
+ };
+ this.common = this._validateTxV(this.v, opts.common);
+ console.log('txData', txData);
+ this.chainId = txData.chainId ? toBigInt(txData.chainId) : undefined;
+ console.log('this.chainId', this.chainId);
+ this.gasPrice = uint8ArrayToBigInt(
+ // @ts-ignore
+ toUint8Array(txData.gasPrice === '' ? '0x' : txData.gasPrice),
+ );
+
+ if (this.gasPrice * this.gasLimit > MAX_INTEGER) {
+ const msg = this._errorMsg('gas limit * gasPrice cannot exceed MAX_INTEGER (2^256-1)');
+ throw new Error(msg);
+ }
+ this._validateCannotExceedMaxInteger({ gasPrice: this.gasPrice });
+ BaseTransaction._validateNotArray(txData);
+
+ if (this.common.gteHardfork('spuriousDragon')) {
+ if (!this.isSigned()) {
+ this.activeCapabilities.push(Capability.EIP155ReplayProtection);
+ } else {
+ // EIP155 spec:
+ // If block.number >= 2,675,000 and v = CHAIN_ID * 2 + 35 or v = CHAIN_ID * 2 + 36
+ // then when computing the hash of a transaction for purposes of signing or recovering
+ // instead of hashing only the first six elements (i.e. nonce, gasprice, startgas, to, value, data)
+ // hash nine elements, with v replaced by CHAIN_ID, r = 0 and s = 0.
+ // v and chain ID meet EIP-155 conditions
+
+ if (meetsEIP155(this.v!, this.common.chainId())) {
+ this.activeCapabilities.push(Capability.EIP155ReplayProtection);
+ }
+ }
+ }
+
+ const freeze = opts?.freeze ?? true;
+ if (freeze) {
+ Object.freeze(this);
+ }
+ }
+
+ /**
+ * Generates the EIP712 typed data from provided transaction. Optional fields are populated by zero values.
+ *
+ * @param transaction The transaction request that needs to be populated.
+ *
+ * @example
+ *
+ * const tx = EIP712Signer.getSignInput({
+ * type: utils.EIP712_TX_TYPE,
+ * to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618",
+ * value: BigInt(7_000_000),
+ * from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049",
+ * nonce: 0,
+ * chainId: BigInt(270),
+ * gasPrice: BigInt(250_000_000),
+ * gasLimit: BigInt(21_000),
+ * customData: {},
+ * });
+ */
+ static getSignInput(transaction: Eip712TxData) {
+ const maxFeePerGas = toBigInt(transaction.maxFeePerGas || transaction.gasPrice || 0n);
+ const maxPriorityFeePerGas = toBigInt(transaction.maxPriorityFeePerGas || maxFeePerGas);
+ const gasPerPubdataByteLimit = toBigInt(
+ transaction.customData?.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT,
+ );
+ return {
+ txType: transaction.type || EIP712_TX_TYPE,
+ from: transaction.from ? toHex(transaction.from) : undefined,
+ to: transaction.to ? toHex(transaction.to) : undefined,
+ gasLimit: transaction.gasLimit || 0n,
+ gasPerPubdataByteLimit: gasPerPubdataByteLimit,
+ customData: transaction.customData,
+ maxFeePerGas,
+ maxPriorityFeePerGas,
+ paymaster: transaction.customData?.paymasterParams?.paymaster || ZERO_ADDRESS,
+ nonce: transaction.nonce || 0,
+ value: transaction.value || BigInt(0),
+ data: transaction.data || '0x',
+ factoryDeps:
+ transaction.customData?.factoryDeps?.map((dep: Bytes) => hashBytecode(dep)) || [],
+ paymasterInput: transaction.customData?.paymasterParams?.paymasterInput || '0x',
+ };
+ }
+
+ /**
+ * Returns a Uint8Array Array of the raw Uint8Arrays of the legacy transaction, in order.
+ *
+ * Format: `[nonce, gasPrice, gasLimit, to, value, data, v, r, s]`
+ *
+ * For legacy txs this is also the correct format to add transactions
+ * to a block with {@link Block.fromValuesArray} (use the `serialize()` method
+ * for typed txs).
+ *
+ * For an unsigned tx this method returns the empty Uint8Array values
+ * for the signature parameters `v`, `r` and `s`. For an EIP-155 compliant
+ * representation have a look at {@link Transaction.getMessageToSign}.
+ */
+ public raw(): TxValuesArray {
+ const transaction = this.txData;
+ console.log('raw', this.chainId);
+ if (!this.chainId) {
+ throw Error("Transaction chainId isn't set!");
+ }
+
+ if (!transaction.from) {
+ throw new Error(
+ 'Explicitly providing `from` field is required for EIP712 transactions!',
+ );
+ }
+ const from = transaction.from;
+ const meta: Eip712Meta = transaction.customData ?? {};
+ // const maxFeePerGas = transaction.maxFeePerGas || transaction.gasPrice || 0;
+ // const maxPriorityFeePerGas = transaction.maxPriorityFeePerGas || maxFeePerGas;
+ console.log('transaction', transaction);
+ console.log('1');
+ console.log('toBytes(transaction.nonce || 0)', toBytes('0x0'));
+ console.log('2');
+
+ const fields: any[] = [
+ toBytes(transaction.nonce || 0),
+ // toBytes(maxPriorityFeePerGas),
+ // toBytes(maxFeePerGas),
+ toBytes(transaction.gasLimit || 0),
+ transaction.to ? toChecksumAddress(transaction.to as Address) : '0x',
+ toBytes(transaction.value || 0),
+ transaction.data || '0x',
+ ];
+ console.log('fields', fields);
+
+ // if (signature) {
+ // const sig = new SignatureObject(signature);
+ fields.push(
+ this.v !== undefined ? bigIntToUnpaddedUint8Array(this.v) : Uint8Array.from([]),
+ );
+ fields.push(
+ this.r !== undefined ? bigIntToUnpaddedUint8Array(this.r) : Uint8Array.from([]),
+ );
+ fields.push(
+ this.s !== undefined ? bigIntToUnpaddedUint8Array(this.s) : Uint8Array.from([]),
+ );
+ // } else {
+ // fields.push(toBytes(transaction.chainId));
+ // fields.push("0x");
+ // fields.push("0x");
+ // }
+ console.log('this.chainId', this.chainId);
+ fields.push(toBytes(this.chainId));
+ fields.push(toChecksumAddress(from));
+
+ // Add meta
+ console.log('meta', meta);
+ fields.push(toBytes(meta.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT));
+ fields.push((meta.factoryDeps ?? []).map(dep => toHex(dep)));
+ if (meta.customSignature && bytesToUint8Array(meta.customSignature).length === 0) {
+ throw new Error('Empty signatures are not supported!');
+ }
+ fields.push(meta.customSignature || '0x');
+
+ if (meta.paymasterParams) {
+ fields.push([
+ meta.paymasterParams.paymaster,
+ toHex(meta.paymasterParams.paymasterInput),
+ ]);
+ } else {
+ fields.push([]);
+ }
+ return fields;
+ }
+
+ /**
+ * Serializes an EIP712 transaction and includes a signature if provided.
+ *
+ * @param transaction The transaction that needs to be serialized.
+ * @param signature Ethers signature to be included in the transaction.
+ * @throws {Error} Throws an error if:
+ * - `transaction.customData.customSignature` is an empty string. The transaction should be signed, and the `transaction.customData.customSignature` field should be populated with the signature. It should not be specified if the transaction is not signed.
+ * - `transaction.chainId` is not provided.
+ * - `transaction.from` is not provided.
+ *
+ * @example Serialize EIP712 transaction without signature.
+ *
+ * const serializedTx = utils.serializeEip712({ chainId: 270, from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049" }, null);
+ *
+ * // serializedTx = "0x71ea8080808080808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0"
+ *
+ * @example Serialize EIP712 transaction with signature.
+ *
+ * const signature = ethers.Signature.from("0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a");
+ *
+ * const serializedTx = utils.serializeEip712(
+ * {
+ * chainId: 270,
+ * from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049",
+ * to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618",
+ * value: 1_000_000,
+ * },
+ * signature
+ * );
+ * // serializedTx = "0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0"
+ */
+ // @ts-ignore
+ serialize() {
+ return concat([new Uint8Array([EIP712_TX_TYPE]), RLP.encode(this.raw())]);
+ }
+
+ /**
+ * Returns the unsigned tx (hashed or raw), which can be used
+ * to sign the transaction (e.g. for sending to a hardware wallet).
+ *
+ * Note: the raw message message format for the legacy tx is not RLP encoded
+ * and you might need to do yourself with:
+ *
+ * ```javascript
+ * import { bufArrToArr } from '../util'
+ * import { RLP } from '../rlp'
+ * const message = tx.getMessageToSign(false)
+ * const serializedMessage = RLP.encode(message) // use this for the HW wallet input
+ * ```
+ *
+ * @param hashMessage - Return hashed message if set to true (default: true)
+ */
+ public getMessageToSign(hashMessage = true) {
+ const base = this.raw().slice(0, 9);
+ const message = uint8ArrayConcat(EIP712_TX_TYPE_UINT8ARRAY, RLP.encode(base));
+ if (hashMessage) {
+ return keccak256(message);
+ }
+ return message;
+ }
+
+ /**
+ * The amount of gas paid for the data in this tx
+ */
+ public getDataFee(): bigint {
+ // TODO: this is a temporary solution until we have a better way to calculate the data fee
+ if (this.cache.dataFee && this.cache.dataFee.hardfork === this.common.hardfork()) {
+ return this.cache.dataFee.value;
+ }
+
+ if (Object.isFrozen(this)) {
+ this.cache.dataFee = {
+ value: super.getDataFee(),
+ hardfork: this.common.hardfork(),
+ };
+ }
+
+ return super.getDataFee();
+ }
+
+ /**
+ * The up front amount that an account must have for this transaction to be valid
+ */
+ public getUpfrontCost(): bigint {
+ // TODO: this is a temporary solution until we have a better way to calculate the upfront cost
+ return this.gasLimit * this.gasPrice + this.value;
+ }
+
+ /**
+ * Computes a sha3-256 hash of the serialized tx.
+ *
+ * This method can only be used for signed txs (it throws otherwise).
+ * Use {@link Transaction.getMessageToSign} to get a tx hash for the purpose of signing.
+ */
+ public hash(): Uint8Array {
+ if (!this.isSigned()) {
+ const msg = this._errorMsg('Cannot call hash method if transaction is not signed');
+ throw new Error(msg);
+ }
+
+ if (Object.isFrozen(this)) {
+ if (!this.cache.hash) {
+ this.cache.hash = keccak256(RLP.encode(this.raw()));
+ }
+ return this.cache.hash;
+ }
+
+ return keccak256(RLP.encode(this.raw()));
+ }
+
+ /**
+ * Computes a sha3-256 hash which can be used to verify the signature
+ */
+
+ public getMessageToVerifySignature(): Uint8Array {
+ if (!this.isSigned()) {
+ const msg = this._errorMsg('This transaction is not signed');
+ throw new Error(msg);
+ }
+ return this.getMessageToSign();
+ }
+
+ /**
+ * Returns the public key of the sender
+ */
+ public getSenderPublicKey(): Uint8Array {
+ if (!this.isSigned()) {
+ const msg = this._errorMsg('Cannot call this method if transaction is not signed');
+ throw new Error(msg);
+ }
+
+ const msgHash = this.getMessageToVerifySignature();
+ const { v, r, s } = this;
+
+ this._validateHighS();
+
+ try {
+ return ecrecover(
+ msgHash,
+ v! + BigInt(27), // Recover the 27 which was stripped from ecsign
+ bigIntToUnpaddedUint8Array(r!),
+ bigIntToUnpaddedUint8Array(s!),
+ );
+ } catch (e: any) {
+ const msg = this._errorMsg('Invalid Signature');
+ throw new Error(msg);
+ }
+ }
+
+ /**
+ * Process the v, r, s values from the `sign` method of the base transaction.
+ */
+ protected _processSignature(_v: bigint, r: Uint8Array, s: Uint8Array) {
+ let v = _v;
+ if (this.supports(Capability.EIP155ReplayProtection)) {
+ v += this.common.chainId() * BigInt(2) + BigInt(8);
+ }
+
+ const opts = { ...this.txOptions, common: this.common };
+
+ return EIP712Transaction.fromTxData(
+ {
+ chainId: this.chainId,
+ nonce: this.nonce,
+ gasLimit: this.gasLimit,
+ to: this.to,
+ value: this.value,
+ data: this.data,
+ v: v - BigInt(27), // This looks extremely hacky: /util actually adds 27 to the value, the recovery bit is either 0 or 1.
+ r: uint8ArrayToBigInt(r),
+ s: uint8ArrayToBigInt(s),
+ },
+ opts,
+ );
+ }
+
+ /**
+ * Returns an object with the JSON representation of the transaction.
+ */
+ public toJSON(): JsonTx {
+ return {
+ nonce: bigIntToHex(this.nonce),
+ gasPrice: bigIntToHex(this.gasPrice),
+ gasLimit: bigIntToHex(this.gasLimit),
+ to: this.to !== undefined ? this.to.toString() : undefined,
+ value: bigIntToHex(this.value),
+ data: bytesToHex(this.data),
+ v: this.v !== undefined ? bigIntToHex(this.v) : undefined,
+ r: this.r !== undefined ? bigIntToHex(this.r) : undefined,
+ s: this.s !== undefined ? bigIntToHex(this.s) : undefined,
+ };
+ }
+
+ /**
+ * Validates tx's `v` value
+ */
+ private _validateTxV(_v?: bigint, common?: Common): Common {
+ let chainIdBigInt;
+ const v = _v !== undefined ? Number(_v) : undefined;
+ // Check for valid v values in the scope of a signed legacy tx
+ if (v !== undefined) {
+ // v is 1. not matching the EIP-155 chainId included case and...
+ // v is 2. not matching the classic v=27 or v=28 case
+ if (v < 37 && v !== 27 && v !== 28) {
+ throw new Error(
+ `Legacy txs need either v = 27/28 or v >= 37 (EIP-155 replay protection), got v = ${v}`,
+ );
+ }
+ }
+
+ // No unsigned tx and EIP-155 activated and chain ID included
+ if (
+ v !== undefined &&
+ v !== 0 &&
+ (!common || common.gteHardfork('spuriousDragon')) &&
+ v !== 27 &&
+ v !== 28
+ ) {
+ if (common) {
+ if (!meetsEIP155(BigInt(v), common.chainId())) {
+ throw new Error(
+ `Incompatible EIP155-based V ${v} and chain id ${common.chainId()}. See the Common parameter of the Transaction constructor to set the chain id.`,
+ );
+ }
+ } else {
+ // Derive the original chain ID
+ let numSub;
+ if ((v - 35) % 2 === 0) {
+ numSub = 35;
+ } else {
+ numSub = 36;
+ }
+ // Use derived chain ID to create a proper Common
+ chainIdBigInt = BigInt(v - numSub) / BigInt(2);
+ }
+ }
+ return this._getCommon(common, chainIdBigInt);
+ }
+
+ /**
+ * Return a compact error string representation of the object
+ */
+ public errorStr() {
+ let errorStr = this._getSharedErrorPostfix();
+ errorStr += ` gasPrice=${this.gasPrice}`;
+ return errorStr;
+ }
+
+ /**
+ * Internal helper function to create an annotated error message
+ *
+ * @param msg Base error message
+ * @hidden
+ */
+ protected _errorMsg(msg: string) {
+ return `${msg} (${this.errorStr()})`;
+ }
+}
diff --git a/src/eip712/constants.ts b/src/eip712/constants.ts
new file mode 100644
index 0000000..e669696
--- /dev/null
+++ b/src/eip712/constants.ts
@@ -0,0 +1,12 @@
+/**
+ * Default gas per pubdata byte for L2 transactions.
+ * This value is utilized when inserting a default value for type 2
+ * and EIP712 type transactions.
+ *
+ * @constant
+ */
+// It is a realistic value, but it is large enough to fill into any batch regardless of the pubdata price.
+export const DEFAULT_GAS_PER_PUBDATA_LIMIT = 50_000;
+export const MAX_INTEGER = BigInt(
+ '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
+);
diff --git a/src/eip712/types.ts b/src/eip712/types.ts
new file mode 100644
index 0000000..1f091df
--- /dev/null
+++ b/src/eip712/types.ts
@@ -0,0 +1,87 @@
+import type { Bytes, Numbers } from 'web3-types';
+import type { Address } from 'web3';
+import type { FeeMarketEIP1559TxData } from 'web3-eth-accounts';
+
+export interface TypedDataDomain {
+ /**
+ * The human-readable name of the signing domain.
+ */
+ name?: null | string;
+
+ /**
+ * The major version of the signing domain.
+ */
+ version?: null | string;
+
+ /**
+ * The chain ID of the signing domain.
+ */
+ chainId?: null | Numbers;
+
+ /**
+ * The the address of the contract that will verify the signature.
+ */
+ verifyingContract?: null | string;
+
+ /**
+ * A salt used for purposes decided by the specific domain.
+ */
+ salt?: null | Bytes;
+}
+
+/**
+ * A specific field of a structured [[link-eip-712]] type.
+ */
+export interface TypedDataField {
+ /**
+ * The field name.
+ */
+ name: string;
+
+ /**
+ * The type of the field.
+ */
+ type: string;
+}
+
+export type PaymasterParams = {
+ /** The address of the paymaster. */
+ paymaster: Address;
+ /** The bytestream input for the paymaster. */
+ paymasterInput: Bytes;
+};
+
+export type Eip712Meta = {
+ /** The maximum amount of gas the user is willing to pay for a single byte of pubdata. */
+ gasPerPubdata?: Numbers;
+ /** An array of bytes containing the bytecode of the contract being deployed and any related contracts it can deploy. */
+ factoryDeps?: Bytes[];
+ /** Custom signature used for cases where the signer's account is not an EOA. */
+ customSignature?: Bytes;
+ /** Parameters for configuring the custom paymaster for the transaction. */
+ paymasterParams?: PaymasterParams;
+};
+
+export type Eip712TxData = FeeMarketEIP1559TxData & {
+ /** The custom data for EIP712 transaction metadata. */
+ customData?: null | Eip712Meta;
+ from?: Address;
+};
+
+export const EIP712_TYPES = {
+ Transaction: [
+ { name: 'txType', type: 'uint256' },
+ { name: 'from', type: 'uint256' },
+ { name: 'to', type: 'uint256' },
+ { name: 'gasLimit', type: 'uint256' },
+ { name: 'gasPerPubdataByteLimit', type: 'uint256' },
+ { name: 'maxFeePerGas', type: 'uint256' },
+ { name: 'maxPriorityFeePerGas', type: 'uint256' },
+ { name: 'paymaster', type: 'uint256' },
+ { name: 'nonce', type: 'uint256' },
+ { name: 'value', type: 'uint256' },
+ { name: 'data', type: 'bytes' },
+ { name: 'factoryDeps', type: 'bytes32[]' },
+ { name: 'paymasterInput', type: 'bytes' },
+ ],
+};
diff --git a/src/plugin.ts b/src/plugin.ts
index fc922e8..ba5f2bd 100644
--- a/src/plugin.ts
+++ b/src/plugin.ts
@@ -1,10 +1,12 @@
import type { Address } from 'web3';
import { Web3PluginBase, Contract } from 'web3';
import type { Web3RequestManager } from 'web3-core';
+import { TransactionFactory } from 'web3-eth-accounts';
import { IERC20ABI } from './contracts/IERC20';
import { RpcMethods } from './rpc.methods';
import { ETH_ADDRESS, ZERO_ADDRESS } from './constants';
import { IL2BridgeABI } from './contracts/IL2Bridge';
+import { EIP712Transaction, EIP712_TX_TYPE } from './eip712/EIP712Transaction';
export class ZkSyncPlugin extends Web3PluginBase {
public pluginNamespace = 'zkSync';
@@ -25,6 +27,8 @@ export class ZkSyncPlugin extends Web3PluginBase {
this.wethBridgeL2 = '';
this._l2BridgeContracts = {};
this._erc20Contracts = {};
+ // @ts-ignore
+ TransactionFactory.registerTransactionType(EIP712_TX_TYPE, EIP712Transaction);
}
/**
@@ -32,7 +36,9 @@ export class ZkSyncPlugin extends Web3PluginBase {
*/
get rpc(): RpcMethods {
if (!this._rpc) {
- this._rpc = new RpcMethods(this.requestManager as unknown as Web3RequestManager);
+ this._rpc = new RpcMethods(
+ this.requestManager as unknown as Web3RequestManager,
+ );
}
return this._rpc;
}
@@ -102,7 +108,9 @@ export class ZkSyncPlugin extends Web3PluginBase {
return l1Token;
}
} catch (e) {
- throw new Error(`Error getting L1 address for token ${token}. ${JSON.stringify(e)}`);
+ throw new Error(
+ `Error getting L1 address for token ${token}. ${JSON.stringify(e)}`,
+ );
}
}
@@ -128,7 +136,9 @@ export class ZkSyncPlugin extends Web3PluginBase {
return l2WethToken;
}
} catch (e) {
- throw new Error(`Error getting L2 address for token ${token}. ${JSON.stringify(e)}`);
+ throw new Error(
+ `Error getting L2 address for token ${token}. ${JSON.stringify(e)}`,
+ );
}
}
diff --git a/src/rpc.methods.ts b/src/rpc.methods.ts
index 7f98606..a98b6f9 100644
--- a/src/rpc.methods.ts
+++ b/src/rpc.methods.ts
@@ -1,6 +1,6 @@
import type { Web3RequestManager } from 'web3-core';
import * as web3Utils from 'web3-utils';
-import * as web3Types from 'web3-types';
+import type * as web3Types from 'web3-types';
import * as web3Accounts from 'web3-eth-accounts';
import {
DEFAULT_RETURN_FORMAT,
@@ -105,7 +105,9 @@ export class RpcMethods {
*
* @param returnFormat - The format of the return value.
*/
- public async getL1BatchNumber(returnFormat: DataFormat = DEFAULT_RETURN_FORMAT): Promise {
+ public async getL1BatchNumber(
+ returnFormat: DataFormat = DEFAULT_RETURN_FORMAT,
+ ): Promise {
return web3Utils.format(
IntSchema,
await this._send('zks_L1BatchNumber', []),
@@ -204,7 +206,11 @@ export class RpcMethods {
]);
if (Array.isArray(result)) {
return result.map(tx => {
- return web3Utils.format(RawBlockTransactionSchema, tx, returnFormat) as RawBlockTransaction;
+ return web3Utils.format(
+ RawBlockTransactionSchema,
+ tx,
+ returnFormat,
+ ) as RawBlockTransaction;
});
}
return [];
@@ -404,7 +410,9 @@ export class RpcMethods {
const res = (await this._send('zks_getProof', [
address,
keys,
- typeof l1BatchNumber === 'number' ? l1BatchNumber : Number(web3Utils.toNumber(l1BatchNumber)),
+ typeof l1BatchNumber === 'number'
+ ? l1BatchNumber
+ : Number(web3Utils.toNumber(l1BatchNumber)),
])) as StorageProof;
const result = web3Utils.format(ProofSchema, res, returnFormat) as StorageProof;
result.storageProof = [];
diff --git a/src/types.ts b/src/types.ts
index ec12339..a1dfbb3 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,11 +1,9 @@
-export type { Bytes, HexString, Numbers } from 'web3-types';
-
// import { FMT_BYTES, FMT_NUMBER, TransactionReceipt, Web3Eth } from 'web3';
// // TODO: // is it needed to be re-exported from web3
// import { watchTransactionForConfirmations } from 'web3-eth/lib/types/utils/watch_transaction_for_confirmations.js';
-import {
+import type {
Bytes,
HexString,
Numbers,
@@ -15,7 +13,7 @@ import {
TransactionReceipt,
} from 'web3-types';
-import {
+import type {
// FeeMarketEIP1559Transaction,
FeeMarketEIP1559TxData,
// TxOptions
@@ -30,7 +28,9 @@ import {
// isAddressEq,
// } from './utils';
-import { RpcMethods } from './rpc.methods';
+import type { RpcMethods } from './rpc.methods';
+
+export type { Bytes, HexString, Numbers } from 'web3-types';
export interface TransactionOverrides extends Omit {}
export const ZeroAddress: Address = '0x0000000000000000000000000000000000000000';
@@ -701,17 +701,17 @@ export interface TransactionDetails {
/** Represents the full deposit fee containing fees for both L1 and L2 transactions. */
export interface FullDepositFee {
/** The maximum fee per gas for L1 transaction. */
- maxFeePerGas?: BigInt;
+ maxFeePerGas?: bigint;
/** The maximum priority fee per gas for L1 transaction. */
- maxPriorityFeePerGas?: BigInt;
+ maxPriorityFeePerGas?: bigint;
/** The gas price for L2 transaction. */
- gasPrice?: BigInt;
+ gasPrice?: bigint;
/** The base cost of the deposit transaction on L2. */
- baseCost: BigInt;
+ baseCost: bigint;
/** The gas limit for L1 transaction. */
- l1GasLimit: BigInt;
+ l1GasLimit: bigint;
/** The gas limit for L2 transaction. */
- l2GasLimit: BigInt;
+ l2GasLimit: bigint;
}
/** Represents a raw block transaction. */
diff --git a/src/utils.ts b/src/utils.ts
index 6602a89..14e11d7 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -13,10 +13,12 @@ import * as web3Types from 'web3-types';
import * as web3Abi from 'web3-eth-abi';
import * as web3Contract from 'web3-eth-contract';
-import {
+import type {
DeploymentInfo,
// Eip712Meta,
EthereumSignature,
+} from './types';
+import {
// PaymasterParams,
PriorityOpTree,
PriorityQueueType,
@@ -50,7 +52,7 @@ import {
// DEFAULT_GAS_PER_PUBDATA_LIMIT,
} from './constants';
-import { RpcMethods } from './rpc.methods'; // to be used instead of the one at zksync-ethers: Provider from ./provider
+import type { RpcMethods } from './rpc.methods'; // to be used instead of the one at zksync-ethers: Provider from ./provider
// export * from './paymaster-utils';
// export * from './smart-account-utils';
@@ -171,7 +173,7 @@ export class SignatureObject {
throw new Error('Invalid signature length');
}
// Initialize with a single string parameter
- const signature = rOrSignature as string;
+ const signature = rOrSignature;
this.r = web3Accounts.toUint8Array(signature.slice(0, 66));
this.s = web3Accounts.toUint8Array(`0x${signature.slice(66, 130)}`);
this.v = BigInt(web3Utils.hexToNumber(`0x${signature.slice(130, 132)}`));
@@ -319,7 +321,8 @@ export function getDeployedContracts(receipt: web3Types.TransactionReceipt): Dep
.filter(
log =>
log.topics &&
- log.topics[0] === contractFunctionId('ContractDeployed(address,bytes32,address)') &&
+ log.topics[0] ===
+ contractFunctionId('ContractDeployed(address,bytes32,address)') &&
log.address &&
isAddressEq(log.address, CONTRACT_DEPLOYER_ADDRESS),
)
@@ -362,7 +365,9 @@ export function create2Address(
const prefix = web3Utils.keccak256(web3Utils.utf8ToBytes('zksyncCreate2'));
const inputHash = web3Utils.keccak256(input);
const addressBytes = web3Utils
- .keccak256(concat([prefix, web3Utils.padLeft(sender, 32 * 2), salt, bytecodeHash, inputHash]))
+ .keccak256(
+ concat([prefix, web3Utils.padLeft(sender, 32 * 2), salt, bytecodeHash, inputHash]),
+ )
.slice(26);
return web3Utils.toChecksumAddress(addressBytes);
}
@@ -422,94 +427,6 @@ export async function checkBaseCost(
}
}
-// /**
-// * Serializes an EIP712 transaction and includes a signature if provided.
-// *
-// * @param transaction The transaction that needs to be serialized.
-// * @param signature Ethers signature to be included in the transaction.
-// * @throws {Error} Throws an error if:
-// * - `transaction.customData.customSignature` is an empty string. The transaction should be signed, and the `transaction.customData.customSignature` field should be populated with the signature. It should not be specified if the transaction is not signed.
-// * - `transaction.chainId` is not provided.
-// * - `transaction.from` is not provided.
-// *
-// * @example Serialize EIP712 transaction without signature.
-// *
-// * const serializedTx = utils.serializeEip712({ chainId: 270, from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049" }, null);
-// *
-// * // serializedTx = "0x71ea8080808080808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0"
-// *
-// * @example Serialize EIP712 transaction with signature.
-// *
-// * const signature = ethers.Signature.from("0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a");
-// *
-// * const serializedTx = utils.serializeEip712(
-// * {
-// * chainId: 270,
-// * from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049",
-// * to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618",
-// * value: 1_000_000,
-// * },
-// * signature
-// * );
-// * // serializedTx = "0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0"
-// */
-// export function serializeEip712(transaction: TransactionLike, signature?: SignatureLike): string {
-// if (!transaction.chainId) {
-// throw Error("Transaction chainId isn't set!");
-// }
-
-// if (!transaction.from) {
-// throw new Error('Explicitly providing `from` field is required for EIP712 transactions!');
-// }
-// const from = transaction.from;
-// const meta: Eip712Meta = transaction.customData ?? {};
-// const maxFeePerGas = transaction.maxFeePerGas || transaction.gasPrice || 0;
-// const maxPriorityFeePerGas = transaction.maxPriorityFeePerGas || maxFeePerGas;
-
-// const fields: any[] = [
-// toBytes(transaction.nonce || 0),
-// toBytes(maxPriorityFeePerGas),
-// toBytes(maxFeePerGas),
-// toBytes(transaction.gasLimit || 0),
-// transaction.to ? web3Utils.toChecksumAddress(transaction.to) : '0x',
-// toBytes(transaction.value || 0),
-// transaction.data || '0x',
-// ];
-
-// if (signature) {
-// const sig = new SignatureObject(signature);
-// fields.push(toBytes(sig.yParity));
-// fields.push(toBytes(sig.r));
-// fields.push(toBytes(sig.s));
-// } else {
-// fields.push(toBytes(transaction.chainId));
-// fields.push('0x');
-// fields.push('0x');
-// }
-// fields.push(toBytes(transaction.chainId));
-// fields.push(web3Utils.toChecksumAddress(from));
-
-// // Add meta
-// fields.push(toBytes(meta.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT));
-// fields.push((meta.factoryDeps ?? []).map(dep => web3Utils.toHex(dep)));
-
-// if (meta.customSignature && web3Utils.bytesToUint8Array(meta.customSignature).length === 0) {
-// throw new Error('Empty signatures are not supported!');
-// }
-// fields.push(meta.customSignature || '0x');
-
-// if (meta.paymasterParams) {
-// fields.push([
-// meta.paymasterParams.paymaster,
-// web3Utils.toHex(meta.paymasterParams.paymasterInput),
-// ]);
-// } else {
-// fields.push([]);
-// }
-
-// return concat([new Uint8Array([EIP712_TX_TYPE]), RLP.encode(fields)]);
-// }
-
/**
* Returns the hash of the given bytecode.
*
@@ -687,8 +604,12 @@ export function getSignature(transaction: any, ethSignature?: EthereumSignature)
throw new Error('No signature provided!');
}
- const r = web3Utils.bytesToUint8Array(web3Utils.padLeft(web3Utils.toHex(ethSignature.r), 32 * 2));
- const s = web3Utils.bytesToUint8Array(web3Utils.padLeft(web3Utils.toHex(ethSignature.s), 32 * 2));
+ const r = web3Utils.bytesToUint8Array(
+ web3Utils.padLeft(web3Utils.toHex(ethSignature.r), 32 * 2),
+ );
+ const s = web3Utils.bytesToUint8Array(
+ web3Utils.padLeft(web3Utils.toHex(ethSignature.s), 32 * 2),
+ );
const v = ethSignature.v;
return new Uint8Array([...r, ...s, v]);
@@ -947,7 +868,12 @@ async function isSignatureCorrect(
msgHash: string,
signature: SignatureLike,
): Promise {
- const code = await web3.eth.getCode(context, address, undefined, web3Types.DEFAULT_RETURN_FORMAT);
+ const code = await web3.eth.getCode(
+ context,
+ address,
+ undefined,
+ web3Types.DEFAULT_RETURN_FORMAT,
+ );
const isContractAccount = web3Utils.bytesToUint8Array(code).length !== 0;
if (!isContractAccount) {
diff --git a/test/fixtures.ts b/test/fixtures.ts
index 265d79a..942aa8d 100644
--- a/test/fixtures.ts
+++ b/test/fixtures.ts
@@ -16,46 +16,52 @@ export const getRawBlockTransactionsData = {
},
initiatorAddress: '0x202bd724d72fd5a169c8930203e1b60870e4df95',
signature: [
- 137, 115, 161, 220, 2, 48, 185, 157, 125, 236, 198, 85, 99, 212, 128, 24, 126, 171, 22,
- 34, 146, 36, 193, 208, 83, 3, 134, 11, 74, 38, 89, 252, 37, 222, 4, 59, 169, 237, 144,
- 64, 12, 82, 61, 251, 40, 85, 42, 89, 21, 199, 71, 128, 151, 231, 166, 230, 60, 14, 17,
- 59, 67, 118, 175, 216, 28,
+ 137, 115, 161, 220, 2, 48, 185, 157, 125, 236, 198, 85, 99, 212, 128, 24,
+ 126, 171, 22, 34, 146, 36, 193, 208, 83, 3, 134, 11, 74, 38, 89, 252, 37,
+ 222, 4, 59, 169, 237, 144, 64, 12, 82, 61, 251, 40, 85, 42, 89, 21, 199, 71,
+ 128, 151, 231, 166, 230, 60, 14, 17, 59, 67, 118, 175, 216, 28,
],
transactionType: 'LegacyTransaction',
input: {
hash: '0x16d5e37b848eed8b33d927b7c3b9d974cc48e164af5f7a6d172334a1330a5e76',
data: [
- 249, 2, 206, 130, 104, 112, 132, 59, 154, 202, 0, 131, 61, 9, 0, 148, 187, 92, 48,
- 154, 58, 147, 71, 192, 19, 91, 147, 203, 213, 61, 57, 74, 168, 67, 69, 229, 128, 185,
- 2, 100, 201, 128, 117, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 253, 105, 228, 93, 111, 81, 228, 130, 172, 79, 143, 46, 20,
- 242, 21, 82, 0, 0, 93, 139, 6, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 197, 134, 51, 192, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 197,
- 134, 51, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 56, 197, 134, 51, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 197, 134, 51, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 221, 91, 114, 141, 15,
- 115, 53, 225, 115, 233, 223, 14, 194, 138, 27, 152, 228, 159, 84, 144, 67, 246, 163,
- 74, 17, 27, 161, 247, 5, 126, 30, 1, 243, 98, 134, 218, 191, 139, 161, 27, 167, 121,
- 27, 171, 231, 94, 248, 206, 73, 195, 156, 142, 246, 164, 198, 205, 78, 67, 188, 193,
- 147, 210, 209, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 56, 204, 92, 213, 109, 169, 231, 44, 75, 130, 19, 30,
- 79, 248, 50, 78, 119, 48, 161, 206, 119, 24, 74, 4, 190, 125, 90, 23, 101, 13, 83, 24,
- 231, 138, 70, 132, 57, 11, 124, 83, 101, 7, 232, 64, 136, 14, 187, 52, 224, 220, 104,
- 8, 215, 48, 44, 207, 203, 144, 38, 231, 116, 87, 208, 130, 2, 124, 160, 137, 115, 161,
- 220, 2, 48, 185, 157, 125, 236, 198, 85, 99, 212, 128, 24, 126, 171, 22, 34, 146, 36,
- 193, 208, 83, 3, 134, 11, 74, 38, 89, 252, 160, 37, 222, 4, 59, 169, 237, 144, 64, 12,
- 82, 61, 251, 40, 85, 42, 89, 21, 199, 71, 128, 151, 231, 166, 230, 60, 14, 17, 59, 67,
- 118, 175, 216,
+ 249, 2, 206, 130, 104, 112, 132, 59, 154, 202, 0, 131, 61, 9, 0, 148,
+ 187, 92, 48, 154, 58, 147, 71, 192, 19, 91, 147, 203, 213, 61, 57, 74,
+ 168, 67, 69, 229, 128, 185, 2, 100, 201, 128, 117, 57, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 253, 105, 228, 93, 111, 81, 228, 130, 172, 79, 143, 46, 20, 242, 21, 82,
+ 0, 0, 93, 139, 6, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 197, 134, 51, 192, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 197,
+ 134, 51, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 56, 197, 134, 51, 192, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 197, 134,
+ 51, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 221, 91, 114, 141, 15, 115, 53, 225,
+ 115, 233, 223, 14, 194, 138, 27, 152, 228, 159, 84, 144, 67, 246, 163,
+ 74, 17, 27, 161, 247, 5, 126, 30, 1, 243, 98, 134, 218, 191, 139, 161,
+ 27, 167, 121, 27, 171, 231, 94, 248, 206, 73, 195, 156, 142, 246, 164,
+ 198, 205, 78, 67, 188, 193, 147, 210, 209, 46, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
+ 1, 56, 204, 92, 213, 109, 169, 231, 44, 75, 130, 19, 30, 79, 248, 50,
+ 78, 119, 48, 161, 206, 119, 24, 74, 4, 190, 125, 90, 23, 101, 13, 83,
+ 24, 231, 138, 70, 132, 57, 11, 124, 83, 101, 7, 232, 64, 136, 14, 187,
+ 52, 224, 220, 104, 8, 215, 48, 44, 207, 203, 144, 38, 231, 116, 87, 208,
+ 130, 2, 124, 160, 137, 115, 161, 220, 2, 48, 185, 157, 125, 236, 198,
+ 85, 99, 212, 128, 24, 126, 171, 22, 34, 146, 36, 193, 208, 83, 3, 134,
+ 11, 74, 38, 89, 252, 160, 37, 222, 4, 59, 169, 237, 144, 64, 12, 82, 61,
+ 251, 40, 85, 42, 89, 21, 199, 71, 128, 151, 231, 166, 230, 60, 14, 17,
+ 59, 67, 118, 175, 216,
],
},
paymasterParams: {
diff --git a/test/integration/rpc.test.ts b/test/integration/rpc.test.ts
index f07553e..38685fa 100644
--- a/test/integration/rpc.test.ts
+++ b/test/integration/rpc.test.ts
@@ -38,7 +38,9 @@ describe('ZkSyncPlugin rpc tests', () => {
expect(res).toBeDefined();
});
it('getRawBlockTransactions', async () => {
- const res = await web3.zkSync.rpc.getRawBlockTransactions(getRawBlockTransactionsData.input);
+ const res = await web3.zkSync.rpc.getRawBlockTransactions(
+ getRawBlockTransactionsData.input,
+ );
expect(res).toEqual(getRawBlockTransactionsData.output);
});
it('getMainContract', async () => {
diff --git a/test/unit/utils.test.ts b/test/unit/utils.test.ts
index 686865b..8d5d1f3 100644
--- a/test/unit/utils.test.ts
+++ b/test/unit/utils.test.ts
@@ -2,11 +2,9 @@ import {
// types,
utils,
} from '../../src';
-import {
- ADDRESS1,
- // ADDRESS2
-} from '../utils';
+import { ADDRESS1, ADDRESS2 } from '../utils';
import * as constants from '../../src/constants';
+import { EIP712Transaction } from '../../src/eip712/EIP712Transaction';
describe('utils', () => {
describe('#getHashedL2ToL1Msg()', () => {
@@ -115,67 +113,81 @@ describe('utils', () => {
});
});
- // describe('#serializeEip712()', () => {
- // it('should throw an error when `tx.chainId` is not specified', async () => {
- // try {
- // utils.serializeEip712({});
- // } catch (e) {
- // expect((e as Error).message).toBe("Transaction chainId isn't set!");
- // }
- // });
+ describe('#serializeEip712()', () => {
+ it('should throw an error when `tx.chainId` is not specified', async () => {
+ try {
+ const txInstance = new EIP712Transaction({});
+ txInstance.serialize();
+ } catch (e) {
+ expect((e as Error).message).toBe("Transaction chainId isn't set!");
+ }
+ });
- // it('should throw an error when `tx.from` is not specified', async () => {
- // try {
- // utils.serializeEip712({ chainId: 270 });
- // } catch (e) {
- // expect((e as Error).message).toBe(
- // 'Explicitly providing `from` field is required for EIP712 transactions!',
- // );
- // }
- // });
+ it('should throw an error when `tx.from` is not specified', async () => {
+ try {
+ const txInstance = new EIP712Transaction({ chainId: 270 });
+ txInstance.serialize();
+ } catch (e) {
+ expect((e as Error).message).toBe(
+ 'Explicitly providing `from` field is required for EIP712 transactions!',
+ );
+ }
+ });
- // it('should throw an error when `tx.customData.customSignature` is empty string', async () => {
- // try {
- // utils.serializeEip712({
- // chainId: 270,
- // from: ADDRESS1,
- // customData: {
- // customSignature: '',
- // },
- // });
- // } catch (e) {
- // expect((e as Error).message).toBe('Empty signatures are not supported');
- // }
- // });
+ it.only('should throw an error when `tx.customData.customSignature` is empty string', async () => {
+ try {
+ const txInstance = new EIP712Transaction({
+ chainId: 270,
+ from: ADDRESS1,
+ customData: {
+ customSignature: '',
+ },
+ });
+ txInstance.serialize();
+ } catch (e) {
+ expect((e as Error).message).toBe('Empty signatures are not supported');
+ }
+ });
- // it('should return a serialized transaction with populated default values', async () => {
- // const tx =
- // '0x71ea8080808080808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
- // const result = utils.serializeEip712({
- // chainId: 270,
- // from: ADDRESS1,
- // });
- // expect(result).toBe(tx);
- // });
+ it('should return a serialized transaction with populated default values', async () => {
+ const txInstance = new EIP712Transaction({
+ chainId: 270,
+ from: ADDRESS1,
+ });
+ const result = txInstance.serialize();
- // it('should return a serialized transaction with provided signature', async () => {
- // const tx =
- // '0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
- // const signature = ethers.Signature.from(
- // '0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a',
- // );
- // const result = utils.serializeEip712(
- // {
- // chainId: 270,
- // from: ADDRESS1,
- // to: ADDRESS2,
- // value: 1_000_000,
- // },
- // signature,
- // );
- // expect(result).toBe(tx);
- // });
- // });
+ const tx =
+ '0x71ea8080808080808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
+ expect(result).toBe(tx);
+ });
+
+ it('should return a serialized transaction with provided signature', async () => {
+ const txInstance = new EIP712Transaction({
+ chainId: 270,
+ from: ADDRESS1,
+ to: ADDRESS2,
+ value: 1_000_000,
+ });
+ const result = txInstance.serialize();
+
+ // const tx =
+ // '0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
+ // const signature = ethers.Signature.from(
+ // '0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a',
+ // );
+ // const result = utils.serializeEip712(
+ // {
+ // chainId: 270,
+ // from: ADDRESS1,
+ // to: ADDRESS2,
+ // value: 1_000_000,
+ // },
+ // signature,
+ // );
+ expect(result).toBeDefined();
+ // expect(result).toBe(tx);
+ });
+ });
describe('#hashBytecode()', () => {
it('should return the hash of bytecode which length is not 2 bytes so padding needs to be performed', async () => {
@@ -184,8 +196,8 @@ describe('utils', () => {
const hashedBytecode = utils.hashBytecode(bytecode);
expect(hashedBytecode).toEqual(
new Uint8Array([
- 1, 0, 0, 27, 57, 231, 154, 55, 0, 164, 201, 96, 244, 120, 23, 112, 54, 34, 224, 133, 160,
- 122, 88, 164, 112, 80, 0, 134, 48, 138, 74, 16,
+ 1, 0, 0, 27, 57, 231, 154, 55, 0, 164, 201, 96, 244, 120, 23, 112, 54, 34, 224,
+ 133, 160, 122, 88, 164, 112, 80, 0, 134, 48, 138, 74, 16,
]),
);
});
@@ -196,8 +208,8 @@ describe('utils', () => {
const hashedBytecode = utils.hashBytecode(bytecode);
expect(hashedBytecode).toEqual(
new Uint8Array([
- 1, 0, 1, 203, 106, 110, 141, 95, 104, 41, 82, 47, 25, 250, 149, 104, 102, 14, 10, 156,
- 213, 59, 46, 139, 228, 222, 176, 166, 121, 69, 46, 65,
+ 1, 0, 1, 203, 106, 110, 141, 95, 104, 41, 82, 47, 25, 250, 149, 104, 102, 14,
+ 10, 156, 213, 59, 46, 139, 228, 222, 176, 166, 121, 69, 46, 65,
]),
);
});
@@ -206,7 +218,9 @@ describe('utils', () => {
try {
utils.hashBytecode('0x0002');
} catch (e) {
- expect((e as Error).message).toBe('The bytecode length in bytes must be divisible by 32!');
+ expect((e as Error).message).toBe(
+ 'The bytecode length in bytes must be divisible by 32!',
+ );
}
});
diff --git a/test/utils.ts b/test/utils.ts
index 0a72a7d..525b832 100644
--- a/test/utils.ts
+++ b/test/utils.ts
@@ -1,11 +1,9 @@
export const ADDRESS1 = '0x36615Cf349d7F6344891B1e7CA7C72883F5dc049';
-export const PRIVATE_KEY1 =
- '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110';
+export const PRIVATE_KEY1 = '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110';
export const MNEMONIC1 =
- 'stuff slice staff easily soup parent arm payment cotton trade scatter struggle';
+ 'stuff slice staff easily soup parent arm payment cotton trade scatter struggle';
export const ADDRESS2 = '0xa61464658AfeAf65CccaaFD3a512b69A83B77618';
-export const PRIVATE_KEY2 =
- '0xac1e735be8536c6534bb4f17f06f6afc73b2b5ba84ac2cfb12f7461b20c0bbe3';
+export const PRIVATE_KEY2 = '0xac1e735be8536c6534bb4f17f06f6afc73b2b5ba84ac2cfb12f7461b20c0bbe3';
export const DAI_L1 = '0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55';
export const APPROVAL_TOKEN = '0x841c43Fa5d8fFfdB9efE3358906f7578d8700Dd4'; // Crown token
export const PAYMASTER = '0xa222f0c183AFA73a8Bc1AFb48D34C88c9Bf7A174'; // Crown token paymaster
diff --git a/yarn.lock b/yarn.lock
index 57f63e3..f0b5754 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1140,6 +1140,13 @@
dependencies:
"@noble/hashes" "1.3.1"
+"@noble/curves@1.3.0", "@noble/curves@~1.3.0":
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.3.0.tgz#01be46da4fd195822dab821e72f71bf4aeec635e"
+ integrity sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==
+ dependencies:
+ "@noble/hashes" "1.3.3"
+
"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12"
@@ -1150,6 +1157,11 @@
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9"
integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==
+"@noble/hashes@1.3.3", "@noble/hashes@~1.3.2":
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699"
+ integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==
+
"@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c"
@@ -1386,6 +1398,11 @@
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938"
integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==
+"@scure/base@~1.1.4":
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.6.tgz#8ce5d304b436e4c84f896e0550c83e4d88cb917d"
+ integrity sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==
+
"@scure/bip32@1.1.5":
version "1.1.5"
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.5.tgz#d2ccae16dcc2e75bc1d75f5ef3c66a338d1ba300"
@@ -1404,6 +1421,15 @@
"@noble/hashes" "~1.3.1"
"@scure/base" "~1.1.0"
+"@scure/bip32@1.3.3":
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.3.tgz#a9624991dc8767087c57999a5d79488f48eae6c8"
+ integrity sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==
+ dependencies:
+ "@noble/curves" "~1.3.0"
+ "@noble/hashes" "~1.3.2"
+ "@scure/base" "~1.1.4"
+
"@scure/bip39@1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5"
@@ -1420,6 +1446,14 @@
"@noble/hashes" "~1.3.0"
"@scure/base" "~1.1.0"
+"@scure/bip39@1.2.2":
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.2.tgz#f3426813f4ced11a47489cbcf7294aa963966527"
+ integrity sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==
+ dependencies:
+ "@noble/hashes" "~1.3.2"
+ "@scure/base" "~1.1.4"
+
"@sentry/core@5.30.0":
version "5.30.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3"
@@ -2923,6 +2957,16 @@ ethereum-cryptography@^2.0.0:
"@scure/bip32" "1.3.1"
"@scure/bip39" "1.2.1"
+ethereum-cryptography@^2.1.3:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz#1352270ed3b339fe25af5ceeadcf1b9c8e30768a"
+ integrity sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==
+ dependencies:
+ "@noble/curves" "1.3.0"
+ "@noble/hashes" "1.3.3"
+ "@scure/bip32" "1.3.3"
+ "@scure/bip39" "1.2.2"
+
ethereumjs-abi@^0.6.8:
version "0.6.8"
resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae"
From b0d67dce659fc0ced406b622467f551087cec55e Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Mon, 10 Jun 2024 21:52:06 -0400
Subject: [PATCH 16/30] eip712
---
src/eip712/types.ts | 2 +
src/plugin.ts | 12 +-
src/types.ts | 9 -
src/utils.ts | 500 +++++++++++++++++++++++++---------------
test/unit/utils.test.ts | 273 +++++++++++-----------
5 files changed, 452 insertions(+), 344 deletions(-)
diff --git a/src/eip712/types.ts b/src/eip712/types.ts
index 1f091df..007b37e 100644
--- a/src/eip712/types.ts
+++ b/src/eip712/types.ts
@@ -66,6 +66,8 @@ export type Eip712TxData = FeeMarketEIP1559TxData & {
/** The custom data for EIP712 transaction metadata. */
customData?: null | Eip712Meta;
from?: Address;
+ hash?: string;
+ signature?: string;
};
export const EIP712_TYPES = {
diff --git a/src/plugin.ts b/src/plugin.ts
index ad3ae4c..1331669 100644
--- a/src/plugin.ts
+++ b/src/plugin.ts
@@ -125,7 +125,9 @@ export class ZkSyncPlugin extends Web3PluginBase {
*/
get rpc(): RpcMethods {
if (!this._rpc) {
- this._rpc = new RpcMethods(this.requestManager as unknown as Web3RequestManager);
+ this._rpc = new RpcMethods(
+ this.requestManager as unknown as Web3RequestManager,
+ );
}
return this._rpc;
}
@@ -195,7 +197,9 @@ export class ZkSyncPlugin extends Web3PluginBase {
return l1Token;
}
} catch (e) {
- throw new Error(`Error getting L1 address for token ${token}. ${JSON.stringify(e)}`);
+ throw new Error(
+ `Error getting L1 address for token ${token}. ${JSON.stringify(e)}`,
+ );
}
}
@@ -221,7 +225,9 @@ export class ZkSyncPlugin extends Web3PluginBase {
return l2WethToken;
}
} catch (e) {
- throw new Error(`Error getting L2 address for token ${token}. ${JSON.stringify(e)}`);
+ throw new Error(
+ `Error getting L2 address for token ${token}. ${JSON.stringify(e)}`,
+ );
}
}
diff --git a/src/types.ts b/src/types.ts
index a1dfbb3..4fd810d 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -392,15 +392,6 @@ export interface zkSyncTxData extends FeeMarketEIP1559TxData {
// }
// }
-// /**
-// * A `TransactionLike` is an extension of {@link ethers.TransactionLike} with additional features for interacting
-// * with zkSync Era.
-// */
-// export interface TransactionLike extends ethers.TransactionLike {
-// /** The custom data for EIP712 transaction metadata. */
-// customData?: null | Eip712Meta;
-// }
-
// /**
// * A `Transaction` is an extension of {@link ethers.Transaction} with additional features for interacting
// * with zkSync Era.
diff --git a/src/utils.ts b/src/utils.ts
index 5e69377..447fc51 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1,7 +1,6 @@
// import { AbiCoder, BigNumberish, Bytes, ethers, SignatureLike } from 'ethers';
import { sha256 } from 'ethereum-cryptography/sha256.js';
-// import { RLP } from '@ethereumjs/rlp';
// import { secp256k1 } from '@noble/curves/secp256k1';
// import { keccak256 } from '@ethersproject/keccak256';
@@ -13,17 +12,16 @@ import * as web3Types from 'web3-types';
import * as web3Abi from 'web3-eth-abi';
import * as web3Contract from 'web3-eth-contract';
-import type {
- DeploymentInfo,
- // Eip712Meta,
- EthereumSignature,
-} from './types';
+import { RLP } from '@ethereumjs/rlp'; // to be used instead of the one at zksync-ethers: Provider from ./provider
+import { bytesToHex, toBigInt, toHex } from 'web3-utils';
+import type { Address } from 'web3';
+import type { Bytes, Eip712TypedData } from 'web3-types';
+import type { DeploymentInfo, Eip712Meta, EthereumSignature, PaymasterParams } from './types';
import {
// PaymasterParams,
PriorityOpTree,
PriorityQueueType,
// Transaction,
- // TransactionLike,
// TransactionRequest,
} from './types';
// import { EIP712Signer } from './signer';
@@ -48,11 +46,16 @@ import {
EIP1271_MAGIC_VALUE,
L1_FEE_ESTIMATION_COEF_NUMERATOR,
L1_FEE_ESTIMATION_COEF_DENOMINATOR,
+ EIP712_TX_TYPE,
+ EIP712_TYPES,
+ DEFAULT_GAS_PER_PUBDATA_LIMIT,
+ ZERO_ADDRESS,
// EIP712_TX_TYPE,
// DEFAULT_GAS_PER_PUBDATA_LIMIT,
} from './constants';
-import type { RpcMethods } from './rpc.methods'; // to be used instead of the one at zksync-ethers: Provider from ./provider
+import type { RpcMethods } from './rpc.methods';
+import type { Eip712TxData } from './eip712/types';
// export * from './paymaster-utils';
// export * from './smart-account-utils';
@@ -152,8 +155,7 @@ function recoverSignerAddress(
const s = web3Utils.toHex(signature.s);
const v = web3Utils.toHex(signature.v);
- const recoveredAddress = web3Accounts.recover(message, v, r, s);
- return recoveredAddress;
+ return web3Accounts.recover(message, v, r, s);
}
export class SignatureObject {
@@ -321,7 +323,8 @@ export function getDeployedContracts(receipt: web3Types.TransactionReceipt): Dep
.filter(
log =>
log.topics &&
- log.topics[0] === contractFunctionId('ContractDeployed(address,bytes32,address)') &&
+ log.topics[0] ===
+ contractFunctionId('ContractDeployed(address,bytes32,address)') &&
log.address &&
isAddressEq(log.address, CONTRACT_DEPLOYER_ADDRESS),
)
@@ -364,7 +367,9 @@ export function create2Address(
const prefix = web3Utils.keccak256(web3Utils.utf8ToBytes('zksyncCreate2'));
const inputHash = web3Utils.keccak256(input);
const addressBytes = web3Utils
- .keccak256(concat([prefix, web3Utils.padLeft(sender, 32 * 2), salt, bytecodeHash, inputHash]))
+ .keccak256(
+ concat([prefix, web3Utils.padLeft(sender, 32 * 2), salt, bytecodeHash, inputHash]),
+ )
.slice(26);
return web3Utils.toChecksumAddress(addressBytes);
}
@@ -477,122 +482,187 @@ export function hashBytecode(bytecode: web3Types.Bytes): Uint8Array {
return hash;
}
-// /**
-// * Parses an EIP712 transaction from a payload.
-// *
-// * @param payload The payload to parse.
-// *
-// * @example
-// *
-// * import { types } from "zksync-ethers";
-// *
-// * const serializedTx =
-// * "0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0";
-// * const tx: types.TransactionLike = utils.parseEip712(serializedTx);
-// * /*
-// * tx: types.TransactionLike = {
-// * type: 113,
-// * nonce: 0,
-// * maxPriorityFeePerGas: BigInt(0),
-// * maxFeePerGas: BigInt(0),
-// * gasLimit: BigInt(0),
-// * to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618",
-// * value: BigInt(1000000),
-// * data: "0x",
-// * chainId: BigInt(270),
-// * from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049",
-// * customData: {
-// * gasPerPubdata: BigInt(50000),
-// * factoryDeps: [],
-// * customSignature: "0x",
-// * paymasterParams: null,
-// * },
-// * hash: "0x9ed410ce33179ac1ff6b721060605afc72d64febfe0c08cacab5a246602131ee",
-// * };
-// * *\/
-// */
-// // TODO: extend ethers.Transaction and add custom fields
-// export function parseEip712(payload: web3Types.Bytes): TransactionLike {
-// function handleAddress(value: string): string | null {
-// if (value === '0x') {
-// return null;
-// }
-// return web3Utils.toChecksumAddress(value);
-// }
-
-// function handleNumber(value: string): bigint {
-// if (!value || value === '0x') {
-// return 0n;
-// }
-// return BigInt(value);
-// }
-
-// function arrayToPaymasterParams(arr: string[]): PaymasterParams | undefined {
-// if (arr.length === 0) {
-// return undefined;
-// }
-// if (arr.length !== 2) {
-// throw new Error(
-// `Invalid paymaster parameters, expected to have length of 2, found ${arr.length}!`,
-// );
-// }
-
-// return {
-// paymaster: web3Utils.toChecksumAddress(arr[0]),
-// paymasterInput: web3Utils.bytesToUint8Array(arr[1]),
-// };
-// }
-
-// const bytes = web3Utils.bytesToUint8Array(payload);
-
-// // try using: RLP.decode
-// const raw = ethers.decodeRlp(bytes.slice(1)) as string[];
-// const transaction: TransactionLike = {
-// type: EIP712_TX_TYPE,
-// nonce: Number(handleNumber(raw[0])),
-// maxPriorityFeePerGas: handleNumber(raw[1]),
-// maxFeePerGas: handleNumber(raw[2]),
-// gasLimit: handleNumber(raw[3]),
-// to: handleAddress(raw[4]),
-// value: handleNumber(raw[5]),
-// data: raw[6],
-// chainId: handleNumber(raw[10]),
-// from: handleAddress(raw[11]),
-// customData: {
-// gasPerPubdata: handleNumber(raw[12]),
-// factoryDeps: raw[13] as unknown as string[],
-// customSignature: raw[14],
-// paymasterParams: arrayToPaymasterParams(raw[15] as unknown as string[]),
-// },
-// };
-
-// const ethSignature = {
-// v: Number(handleNumber(raw[7])),
-// r: raw[8],
-// s: raw[9],
-// };
-
-// if (
-// (web3Utils.toHex(ethSignature.r) === '0x' || web3Utils.toHex(ethSignature.s) === '0x') &&
-// !transaction.customData?.customSignature
-// ) {
-// return transaction;
-// }
-
-// if (ethSignature.v !== 0 && ethSignature.v !== 1 && !transaction.customData?.customSignature) {
-// throw new Error('Failed to parse signature!');
-// }
-
-// if (!transaction.customData?.customSignature) {
-// transaction.signature = new SignatureObject(ethSignature).toString();
-// }
-
-// transaction.hash = eip712TxHash(transaction, ethSignature);
-
-// return transaction;
-// }
-
-export function getSignature(transaction: any, ethSignature?: EthereumSignature): Uint8Array {
+function handleAddress(value?: Uint8Array): string | null {
+ if (!value) {
+ return null;
+ }
+ const hexValue = bytesToHex(value);
+ if (hexValue === '0x') {
+ return null;
+ }
+
+ return web3Utils.toChecksumAddress(hexValue);
+}
+
+function handleNumber(value?: Uint8Array): bigint {
+ if (!value) {
+ return 0n;
+ }
+ const hexValue = bytesToHex(value);
+ if (hexValue === '0x') {
+ return 0n;
+ }
+ return toBigInt(hexValue);
+}
+function arrayToPaymasterParams(arr: Uint8Array): PaymasterParams | undefined {
+ if (arr.length === 0) {
+ return undefined;
+ }
+ if (arr.length !== 2) {
+ throw new Error(
+ `Invalid paymaster parameters, expected to have length of 2, found ${arr.length}!`,
+ );
+ }
+
+ return {
+ paymaster: web3Utils.toChecksumAddress(toHex(arr[0])),
+ paymasterInput: web3Utils.bytesToUint8Array(toHex(arr[1])),
+ };
+}
+
+export const getSignInput = (transaction: Eip712TxData) => {
+ const maxFeePerGas = toHex(transaction.maxFeePerGas || transaction.gasPrice || 0n);
+ const maxPriorityFeePerGas = toHex(transaction.maxPriorityFeePerGas || maxFeePerGas);
+ const gasPerPubdataByteLimit = toHex(
+ transaction.customData?.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT,
+ );
+ return {
+ txType: transaction.type || EIP712_TX_TYPE,
+ from: transaction.from ? toHex(transaction.from) : undefined,
+ to: transaction.to ? toHex(transaction.to) : undefined,
+ gasLimit: transaction.gasLimit || 0,
+ gasPerPubdataByteLimit: gasPerPubdataByteLimit,
+ customData: transaction.customData,
+ maxFeePerGas,
+ maxPriorityFeePerGas,
+ paymaster: transaction.customData?.paymasterParams?.paymaster || ZERO_ADDRESS,
+ nonce: transaction.nonce || 0,
+ value: transaction.value || toHex(0),
+ data: transaction.data || '0x',
+ factoryDeps:
+ transaction.customData?.factoryDeps?.map((dep: Bytes) => hashBytecode(dep)) || [],
+ paymasterInput: transaction.customData?.paymasterParams?.paymasterInput || '0x',
+ };
+};
+
+export function eip712TxTypedData(transaction: Eip712TxData): Eip712TypedData {
+ return {
+ types: EIP712_TYPES,
+ primaryType: 'Transaction',
+ domain: {
+ name: 'zkSync',
+ version: '2',
+ chainId: Number(transaction.chainId),
+ },
+ message: getSignInput(transaction),
+ };
+}
+/**
+ * Returns the hash of an EIP712 transaction.
+ *
+ * @param transaction The EIP-712 transaction.
+ * @param ethSignature The ECDSA signature of the transaction.
+ *
+ * @example
+ *
+ *
+ */
+export function eip712TxHash(transaction: Eip712TxData, ethSignature?: EthereumSignature): string {
+ const bytes: string[] = [];
+
+ const typedDataStruct = eip712TxTypedData(transaction);
+
+ bytes.push(web3Abi.getEncodedEip712Data(typedDataStruct, true));
+ bytes.push(web3Utils.keccak256(getSignature(typedDataStruct.message, ethSignature)));
+ return web3Utils.keccak256(concat(bytes));
+}
+/**
+ * Parses an EIP712 transaction from a payload.
+ *
+ * @param payload The payload to parse.
+ *
+ * @example
+ *
+ *
+ * const serializedTx =
+ * "0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0";
+ * const tx: types.TransactionLike = utils.parseEip712(serializedTx);
+ * /*
+ * tx: types.Eip712TxData = {
+ * type: 113,
+ * nonce: 0,
+ * maxPriorityFeePerGas: BigInt(0),
+ * maxFeePerGas: BigInt(0),
+ * gasLimit: BigInt(0),
+ * to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618",
+ * value: BigInt(1000000),
+ * data: "0x",
+ * chainId: BigInt(270),
+ * from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049",
+ * customData: {
+ * gasPerPubdata: BigInt(50000),
+ * factoryDeps: [],
+ * customSignature: "0x",
+ * paymasterParams: null,
+ * },
+ * hash: "0x9ed410ce33179ac1ff6b721060605afc72d64febfe0c08cacab5a246602131ee",
+ * };
+ * *\/
+ */
+
+export function parseEip712(payload: web3Types.Bytes): Eip712TxData {
+ const bytes = web3Utils.bytesToUint8Array(payload);
+
+ // try using: RLP.decode
+ const raw = RLP.decode(bytes.slice(1)) as Array;
+ const transaction: Eip712TxData = {
+ type: EIP712_TX_TYPE,
+ nonce: handleNumber(raw[0]),
+ maxPriorityFeePerGas: handleNumber(raw[1]),
+ maxFeePerGas: handleNumber(raw[2]),
+ gasLimit: handleNumber(raw[3]),
+ to: handleAddress(raw[4]) as Address,
+ value: handleNumber(raw[5]),
+ data: bytesToHex(raw[6]),
+ chainId: handleNumber(raw[10]),
+ from: handleAddress(raw[11]) as Address,
+ customData: {
+ gasPerPubdata: handleNumber(raw[12]),
+ factoryDeps: raw[13] as unknown as string[],
+ customSignature: bytesToHex(raw[14]),
+ paymasterParams: arrayToPaymasterParams(raw[15]),
+ },
+ };
+ const ethSignature = {
+ v: Number(handleNumber(raw[7])),
+ r: raw[8],
+ s: raw[9],
+ };
+
+ if (
+ (web3Utils.toHex(ethSignature.r) === '0x' || web3Utils.toHex(ethSignature.s) === '0x') &&
+ !transaction.customData?.customSignature
+ ) {
+ return transaction;
+ }
+
+ if (ethSignature.v !== 0 && ethSignature.v !== 1 && !transaction.customData?.customSignature) {
+ throw new Error('Failed to parse signature!');
+ }
+
+ if (!transaction.customData?.customSignature) {
+ transaction.signature = new SignatureObject(ethSignature).toString();
+ }
+
+ transaction.hash = eip712TxHash(transaction, ethSignature);
+
+ return transaction;
+}
+
+export function getSignature(
+ transaction: Eip712TxData,
+ ethSignature?: EthereumSignature,
+): Uint8Array {
if (transaction?.customData?.customSignature && transaction.customData.customSignature.length) {
return web3Utils.bytesToUint8Array(transaction.customData.customSignature);
}
@@ -601,70 +671,116 @@ export function getSignature(transaction: any, ethSignature?: EthereumSignature)
throw new Error('No signature provided!');
}
- const r = web3Utils.bytesToUint8Array(web3Utils.padLeft(web3Utils.toHex(ethSignature.r), 32 * 2));
- const s = web3Utils.bytesToUint8Array(web3Utils.padLeft(web3Utils.toHex(ethSignature.s), 32 * 2));
+ const r = web3Utils.bytesToUint8Array(
+ web3Utils.padLeft(web3Utils.toHex(ethSignature.r), 32 * 2),
+ );
+ const s = web3Utils.bytesToUint8Array(
+ web3Utils.padLeft(web3Utils.toHex(ethSignature.s), 32 * 2),
+ );
const v = ethSignature.v;
return new Uint8Array([...r, ...s, v]);
}
-// /**
-// * Returns the hash of an EIP712 transaction.
-// *
-// * @param transaction The EIP-712 transaction.
-// * @param ethSignature The ECDSA signature of the transaction.
-// *
-// * @example
-// *
-// *
-// */
-// export function eip712TxHash(
-// transaction: Transaction | TransactionRequest,
-// ethSignature?: EthereumSignature,
-// ): string {
-// const signedDigest = EIP712Signer.getSignedDigest(transaction);
-// const hashedSignature = web3Utils.keccak256(getSignature(transaction, ethSignature));
-
-// return web3Utils.keccak256(concat([signedDigest, hashedSignature]));
-// }
-
-// /**
-// * Returns the hash of the L2 priority operation from a given transaction receipt and L2 address.
-// *
-// * @param txReceipt The receipt of the L1 transaction.
-// * @param zkSyncAddress The address of the zkSync Era main contract.
-// *
-// * @example
-// */
-// export function getL2HashFromPriorityOp(
-// txReceipt: web3Types.TransactionReceipt,
-// zkSyncAddress: web3.Address,
-// ): string {
-// let txHash: string | null = null;
-// for (const log of txReceipt.logs) {
-// if (!isAddressEq(log.address as string, zkSyncAddress)) {
-// continue;
-// }
-
-// try {
-// // TODO: implement at web3.js Contract the parsing of the logs similar to new ethers.Interface(ABI).parseLog(...)
-// const priorityQueueLog = ZkSyncMainContract.parseLog({
-// topics: log.topics as string[],
-// data: log.data,
-// });
-// if (priorityQueueLog && priorityQueueLog.args.txHash !== null) {
-// txHash = priorityQueueLog.args.txHash;
-// }
-// } catch {
-// // skip
-// }
-// }
-// if (!txHash) {
-// throw new Error('Failed to parse tx logs!');
-// }
-
-// return txHash;
-// }
+export function serializeEip712(transaction: Eip712TxData, signature?: SignatureLike): string {
+ if (!transaction.chainId) {
+ throw Error("Transaction chainId isn't set!");
+ }
+
+ if (!transaction.from) {
+ throw new Error('Explicitly providing `from` field is required for EIP712 transactions!');
+ }
+ const from = transaction.from;
+ const meta: Eip712Meta = transaction.customData ?? {};
+ const maxFeePerGas = toHex(transaction.maxFeePerGas || transaction.gasPrice || 0);
+ const maxPriorityFeePerGas = toHex(transaction.maxPriorityFeePerGas || maxFeePerGas);
+
+ const nonce = toHex(transaction.nonce || 0);
+ const fields: Array = [
+ nonce === '0x0' ? new Uint8Array() : toBytes(nonce),
+ maxPriorityFeePerGas === '0x0' ? new Uint8Array() : toBytes(maxPriorityFeePerGas),
+ maxFeePerGas === '0x0' ? new Uint8Array() : toBytes(maxFeePerGas),
+ toHex(transaction.gasLimit || 0) === '0x0'
+ ? new Uint8Array()
+ : toBytes(transaction.gasLimit!),
+ transaction.to ? web3Utils.toChecksumAddress(toHex(transaction.to)) : '0x',
+ toHex(transaction.value || 0) === '0x0' ? new Uint8Array() : toBytes(nonce),
+ toHex(transaction.data || '0x'),
+ ];
+
+ if (signature) {
+ const sig = new SignatureObject(signature);
+ // fields.push(toBytes(sig.yParity));
+ fields.push(toBytes(Number(sig.v) === 27 ? 0 : 1));
+ fields.push(toBytes(sig.r));
+ fields.push(toBytes(sig.s));
+ } else {
+ fields.push(toHex(transaction.chainId));
+ fields.push('0x');
+ fields.push('0x');
+ }
+ fields.push(toHex(transaction.chainId));
+ fields.push(web3Utils.toChecksumAddress(from));
+
+ // Add meta
+ fields.push(toHex(meta.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT));
+ fields.push((meta.factoryDeps ?? []).map(dep => web3Utils.toHex(dep)));
+
+ if (meta.customSignature && web3Utils.bytesToUint8Array(meta.customSignature).length === 0) {
+ throw new Error('Empty signatures are not supported!');
+ }
+ fields.push(meta.customSignature || '0x');
+
+ if (meta.paymasterParams) {
+ fields.push([
+ meta.paymasterParams.paymaster,
+ web3Utils.toHex(meta.paymasterParams.paymasterInput),
+ ]);
+ } else {
+ fields.push([]);
+ }
+
+ return concat([new Uint8Array([EIP712_TX_TYPE]), RLP.encode(fields)]);
+}
+
+/**
+ * Returns the hash of the L2 priority operation from a given transaction receipt and L2 address.
+ *
+ * @param txReceipt The receipt of the L1 transaction.
+ * @param zkSyncAddress The address of the zkSync Era main contract.
+ *
+ * @example
+ */
+export function getL2HashFromPriorityOp(
+ txReceipt: web3Types.TransactionReceipt,
+ zkSyncAddress: web3.Address,
+): string {
+ let txHash: string | null = null;
+ for (const log of txReceipt.logs) {
+ if (!isAddressEq(log.address as string, zkSyncAddress)) {
+ continue;
+ }
+
+ try {
+ // TODO: implement at web3.js Contract the parsing of the logs similar to new ethers.Interface(ABI).parseLog(...)
+ // @ts-ignore
+ const priorityQueueLog = ZkSyncMainContract.parseLog({
+ topics: log.topics as string[],
+ data: log.data,
+ });
+ if (priorityQueueLog && priorityQueueLog.args.txHash !== null) {
+ txHash = priorityQueueLog.args.txHash;
+ }
+ } catch {
+ // skip
+ }
+ }
+ if (!txHash) {
+ throw new Error('Failed to parse tx logs!');
+ }
+
+ return txHash;
+}
const ADDRESS_MODULO = 2n ** 160n;
@@ -875,7 +991,9 @@ async function isSignatureCorrect(
return isECDSASignatureCorrect(address, message, signature);
} else {
const msgHash = web3Accounts.hashMessage(
- typeof message === 'string' ? message : web3Utils.bytesToHex(message as unknown as string),
+ typeof message === 'string'
+ ? message
+ : web3Utils.bytesToHex(message as unknown as string),
);
return await isEIP1271SignatureCorrect(context, address, msgHash, signature);
}
@@ -970,10 +1088,10 @@ export async function isTypedDataSignatureCorrect(
message: value,
};
// could be also:
- // const message = web3Abi.getEncodedEip712Data(data);
- // return await isSignatureCorrect(context, address, message, signature);
+ const message = web3Abi.getEncodedEip712Data(data);
+ return isSignatureCorrect(context, address, message, signature);
- return await isSignatureCorrect(context, address, data, signature);
+ // return isSignatureCorrect(context, address, data, signature);
}
/**
diff --git a/test/unit/utils.test.ts b/test/unit/utils.test.ts
index e75077f..9b94eea 100644
--- a/test/unit/utils.test.ts
+++ b/test/unit/utils.test.ts
@@ -7,11 +7,12 @@ import {
} from '../../src';
import { ADDRESS1, ADDRESS2 } from '../utils';
import * as constants from '../../src/constants';
-import { EIP712Transaction } from '../../src/eip712/EIP712Transaction';
+import type { Eip712TxData } from '../../src/eip712/types';
+import { eip712TxTypedData, getSignInput } from '../../src/utils';
describe('utils', () => {
describe('#getHashedL2ToL1Msg()', () => {
- it('should return a hashed L2 to L1 message', async () => {
+ it('should return a hashed L2 to L1 message', () => {
const withdrawETHMessage =
'0x6c0960f936615cf349d7f6344891b1e7ca7c72883f5dc04900000000000000000000000000000000000000000000000000000001a13b8600';
const withdrawETHMessageHash =
@@ -22,31 +23,31 @@ describe('utils', () => {
});
describe('#isETH()', () => {
- it('should return true for legacy L1 ETH address', async () => {
+ it('should return true for legacy L1 ETH address', () => {
const result = utils.isETH(constants.LEGACY_ETH_ADDRESS);
expect(result).toBeTruthy();
});
- it('should return true for L1 ETH address', async () => {
+ it('should return true for L1 ETH address', () => {
const result = utils.isETH(constants.ETH_ADDRESS_IN_CONTRACTS);
expect(result).toBeTruthy();
});
- it('should return true for L2 ETH address', async () => {
+ it('should return true for L2 ETH address', () => {
const result = utils.isETH(constants.L2_BASE_TOKEN_ADDRESS);
expect(result).toBeTruthy();
});
});
describe('#createAddress()', () => {
- it('should return a correct address', async () => {
+ it('should return a correct address', () => {
const address = utils.createAddress(ADDRESS1, 1);
expect(address).toBe('0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021');
});
});
describe('#create2Address()', () => {
- it('should return a correct address', async () => {
+ it('should return a correct address', () => {
const address = utils.create2Address(
ADDRESS1,
'0x010001cb6a6e8d5f6829522f19fa9568660e0a9cd53b2e8be4deb0a679452e41',
@@ -58,7 +59,7 @@ describe('utils', () => {
});
describe('#applyL1ToL2Alias()', () => {
- it('should return the L2 contract address based on provided L1 contract address', async () => {
+ it('should return the L2 contract address based on provided L1 contract address', () => {
const l1ContractAddress = '0x702942B8205E5dEdCD3374E5f4419843adA76Eeb';
const l2ContractAddress = utils.applyL1ToL2Alias(l1ContractAddress);
expect(l2ContractAddress.toLowerCase()).toBe(
@@ -66,7 +67,7 @@ describe('utils', () => {
);
});
- it('should return the L2 contract address by padding zero to left', async () => {
+ it('should return the L2 contract address by padding zero to left', () => {
const l1ContractAddress = '0xeeeeffffffffffffffffffffffffffffffffeeef';
const l2ContractAddress = utils.applyL1ToL2Alias(l1ContractAddress);
expect(l2ContractAddress.toLowerCase()).toBe(
@@ -76,7 +77,7 @@ describe('utils', () => {
});
describe('#undoL1ToL2Alias()', () => {
- it('should return the L1 contract address based on provided L2 contract address', async () => {
+ it('should return the L1 contract address based on provided L2 contract address', () => {
const l2ContractAddress = '0x813A42B8205E5DedCd3374e5f4419843ADa77FFC';
const l1ContractAddress = utils.undoL1ToL2Alias(l2ContractAddress);
expect(l1ContractAddress.toLowerCase()).toBe(
@@ -84,7 +85,7 @@ describe('utils', () => {
);
});
- it('should return the L1 contract address by padding zero to left', async () => {
+ it('should return the L1 contract address by padding zero to left', () => {
const l2ContractAddress = '0x1111000000000000000000000000000000001111';
const l1ContractAddress = utils.undoL1ToL2Alias(l2ContractAddress);
expect(l1ContractAddress.toLowerCase()).toBe(
@@ -103,7 +104,7 @@ describe('utils', () => {
});
describe('#checkBaseCost()', () => {
- it('should throw an error if the base cost bigger than value', async () => {
+ it('should throw an error if the base cost bigger than value', () => {
const baseCost = 100;
const value = 99;
try {
@@ -117,19 +118,17 @@ describe('utils', () => {
});
describe('#serializeEip712()', () => {
- it('should throw an error when `tx.chainId` is not specified', async () => {
+ it('should throw an error when `tx.chainId` is not specified', () => {
try {
- const txInstance = new EIP712Transaction({});
- txInstance.serialize();
+ utils.serializeEip712({});
} catch (e) {
expect((e as Error).message).toBe("Transaction chainId isn't set!");
}
});
- it('should throw an error when `tx.from` is not specified', async () => {
+ it('should throw an error when `tx.from` is not specified', () => {
try {
- const txInstance = new EIP712Transaction({ chainId: 270 });
- txInstance.serialize();
+ utils.serializeEip712({ chainId: 270 });
} catch (e) {
expect((e as Error).message).toBe(
'Explicitly providing `from` field is required for EIP712 transactions!',
@@ -137,63 +136,51 @@ describe('utils', () => {
}
});
- it.only('should throw an error when `tx.customData.customSignature` is empty string', async () => {
+ it('should throw an error when `tx.customData.customSignature` is empty string', () => {
try {
- const txInstance = new EIP712Transaction({
+ utils.serializeEip712({
chainId: 270,
from: ADDRESS1,
customData: {
customSignature: '',
},
});
- txInstance.serialize();
} catch (e) {
expect((e as Error).message).toBe('Empty signatures are not supported');
}
});
- it('should return a serialized transaction with populated default values', async () => {
- const txInstance = new EIP712Transaction({
- chainId: 270,
- from: ADDRESS1,
- });
- const result = txInstance.serialize();
-
+ it('should return a serialized transaction with populated default values', () => {
const tx =
'0x71ea8080808080808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
- expect(result).toBe(tx);
- });
-
- it('should return a serialized transaction with provided signature', async () => {
- const txInstance = new EIP712Transaction({
+ const result = utils.serializeEip712({
chainId: 270,
from: ADDRESS1,
- to: ADDRESS2,
- value: 1_000_000,
});
- const result = txInstance.serialize();
-
- // const tx =
- // '0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
- // const signature = ethers.Signature.from(
- // '0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a',
- // );
- // const result = utils.serializeEip712(
- // {
- // chainId: 270,
- // from: ADDRESS1,
- // to: ADDRESS2,
- // value: 1_000_000,
- // },
- // signature,
- // );
- expect(result).toBeDefined();
- // expect(result).toBe(tx);
+ expect(result).toBe(tx);
});
+
+ // it('should return a serialized transaction with provided signature', async () => {
+ // const tx =
+ // '0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
+ // const signature = ethers.Signature.from(
+ // '0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a',
+ // );
+ // const result = utils.serializeEip712(
+ // {
+ // chainId: 270,
+ // from: ADDRESS1,
+ // to: ADDRESS2,
+ // value: 1_000_000,
+ // },
+ // signature,
+ // );
+ // expect(result).toBe(tx);
+ // });
});
describe('#hashBytecode()', () => {
- it('should return the hash of bytecode which length is not 2 bytes so padding needs to be performed', async () => {
+ it('should return the hash of bytecode which length is not 2 bytes so padding needs to be performed', () => {
const bytecode =
'0x000200000000000200010000000103550000006001100270000000130010019d0000008001000039000000400010043f0000000101200190000000290000c13d0000000001000031000000040110008c000000420000413d0000000101000367000000000101043b000000e001100270000000150210009c000000310000613d000000160110009c000000420000c13d0000000001000416000000000110004c000000420000c13d000000040100008a00000000011000310000001702000041000000200310008c000000000300001900000000030240190000001701100197000000000410004c000000000200a019000000170110009c00000000010300190000000001026019000000000110004c000000420000c13d00000004010000390000000101100367000000000101043b000000000010041b0000000001000019000000490001042e0000000001000416000000000110004c000000420000c13d0000002001000039000001000010044300000120000004430000001401000041000000490001042e0000000001000416000000000110004c000000420000c13d000000040100008a00000000011000310000001702000041000000000310004c000000000300001900000000030240190000001701100197000000000410004c000000000200a019000000170110009c00000000010300190000000001026019000000000110004c000000440000613d00000000010000190000004a00010430000000000100041a000000800010043f0000001801000041000000490001042e0000004800000432000000490001042e0000004a00010430000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0000000200000000000000000000000000000040000001000000000000000000000000000000000000000000000000000000000000000000000000006d4ce63c0000000000000000000000000000000000000000000000000000000060fe47b18000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000080000000000000000000000000000000000000000000000000000000000000000000000000000000009c8c8fa789967eb514f3ec9def748480945cc9b10fcbd1a19597d924eb201083';
const hashedBytecode = utils.hashBytecode(bytecode);
@@ -205,7 +192,7 @@ describe('utils', () => {
);
});
- it('should return the hash of bytecode which length is 2 bytes so padding does not need to be performed', async () => {
+ it('should return the hash of bytecode which length is 2 bytes so padding does not need to be performed', () => {
const bytecode =
'0x0002000000000002000900000000000200010000000103550000006001100270000001980010019d0000008001000039000000400010043f0000000101200190000000340000c13d0000000001000031000000040110008c000003670000413d0000000101000367000000000101043b000000e0011002700000019d0210009c000001420000213d000001a50210009c000001720000213d000001a90210009c000001fd0000613d000001aa0210009c000002210000613d000001ab0110009c000003670000c13d0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000000310004c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d0000000201000039000000000101041a000000400200043d00000000001204350000019801000041000001980320009c00000000010240190000004001100210000001ad011001c70000065c0001042e0000000001000416000000000110004c000003670000c13d00000000020000310000001f01200039000000200a00008a0000000004a1016f000000400100043d0000000003140019000000000443004b00000000040000190000000104004039000001990530009c000003c90000213d0000000104400190000003c90000c13d000000400030043f0000001f0320018f00000001040003670000000505200272000000520000613d000000000600001900000005076002100000000008710019000000000774034f000000000707043b00000000007804350000000106600039000000000756004b0000004a0000413d000000000630004c000000610000613d0000000505500210000000000454034f00000000055100190000000303300210000000000605043300000000063601cf000000000636022f000000000404043b0000010003300089000000000434022f00000000033401cf000000000363019f00000000003504350000019a03000041000000600420008c000000000400001900000000040340190000019a05200197000000000650004c000000000300a0190000019a0550009c000000000304c019000000000330004c000003670000c13d0000000034010434000001990540009c000003670000213d000000000221001900000000041400190000001f054000390000019a06000041000000000725004b000000000700001900000000070680190000019a055001970000019a08200197000000000985004b0000000006008019000000000585013f0000019a0550009c00000000050700190000000005066019000000000550004c000003670000c13d0000000005040433000001990650009c000003c90000213d0000003f065000390000000006a6016f000000400b00043d00000000066b00190000000007b6004b00000000070000190000000107004039000001990860009c000003c90000213d0000000107700190000003c90000c13d000000400060043f000000000c5b043600000020065000390000000007460019000000000727004b000003670000213d000000000750004c0000009e0000613d000000000700001900000020077000390000000008b70019000000000947001900000000090904330000000000980435000000000857004b000000970000413d00000000046b001900000000000404350000000003030433000001990430009c000003670000213d00000000031300190000001f043000390000019a05000041000000000624004b000000000600001900000000060580190000019a044001970000019a07200197000000000874004b0000000005008019000000000474013f0000019a0440009c00000000040600190000000004056019000000000440004c000003670000c13d0000000004030433000001990540009c000003c90000213d0000003f054000390000000005a5016f000000400800043d0000000005580019000000000685004b00000000060000190000000106004039000001990750009c000003c90000213d0000000106600190000003c90000c13d000000400050043f0000000005480436000800000005001d00000020054000390000000006350019000000000226004b000003670000213d00060000000c001d00090000000b001d00070000000a001d000000000240004c000000d50000613d000000000200001900000020022000390000000006820019000000000732001900000000070704330000000000760435000000000642004b000000ce0000413d0000000002580019000000000002043500000040011000390000000001010433000500000001001d000000ff0110008c0000000901000029000003670000213d0000000001010433000400000001001d000001990110009c000003c90000213d000100000008001d0000000301000039000300000001001d000000000101041a000000010210019000000001011002700000007f0310018f0000000001036019000200000001001d0000001f0110008c00000000010000190000000101002039000000010110018f000000000112004b0000021b0000c13d0000000201000029000000200110008c000001100000413d0000000301000029000000000010043500000198010000410000000002000414000001980320009c0000000001024019000000c0011002100000019b011001c70000801002000039065b06560000040f0000000102200190000003670000613d00000004030000290000001f023000390000000502200270000000200330008c0000000002004019000000000301043b00000002010000290000001f01100039000000050110027000000000011300190000000002230019000000000312004b000001100000813d000000000002041b0000000102200039000000000312004b0000010c0000413d00000004010000290000001f0110008c000004990000a13d0000000301000029000000000010043500000198010000410000000002000414000001980320009c0000000001024019000000c0011002100000019b011001c70000801002000039065b06560000040f000000010220019000000007020000290000000906000029000003670000613d000000040300002900000000032301700000002002000039000000000101043b000001300000613d0000002002000039000000000400001900000000056200190000000005050433000000000051041b000000200220003900000001011000390000002004400039000000000534004b000001280000413d0000000404000029000000000343004b0000013e0000813d00000004030000290000000303300210000000f80330018f000000010400008a000000000334022f000000000343013f000000090400002900000000024200190000000002020433000000000232016f000000000021041b0000000401000029000000010110021000000001011001bf000004a70000013d0000019e0210009c000001c60000213d000001a20210009c000002440000613d000001a30210009c000002700000613d000001a40110009c000003670000c13d0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000000310004c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d0000000405000039000000000405041a000000010640019000000001014002700000007f0210018f00000000010260190000001f0210008c00000000020000190000000102002039000000000224013f00000001022001900000021b0000c13d000000400200043d0000000003120436000000000660004c000003800000c13d000001000500008a000000000454016f0000000000430435000000000110004c000000200400003900000000040060190000038d0000013d000001a60210009c000002940000613d000001a70210009c000002e30000613d000001a80110009c000003670000c13d0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000400310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000004010000390000000101100367000000000101043b000900000001001d000001ac0110009c000003670000213d0000000001000411000700000001001d00000000001004350000000101000039000800000001001d000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000003670000613d000000000101043b00000009020000290000000000200435000000200010043f00000024010000390000000101100367000000000101043b000600000001001d00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000003670000613d000000000101043b000000000101041a00000006020000290000000003210019000000000113004b000000000100001900000001010040390000000101100190000003ae0000c13d00000007010000290000000902000029065b05ea0000040f000000400100043d000000080200002900000000002104350000019802000041000001980310009c00000000010280190000004001100210000001ad011001c70000065c0001042e0000019f0210009c000002ff0000613d000001a00210009c000003510000613d000001a10110009c000003670000c13d0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000400310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000001020003670000000401200370000000000101043b000001ac0310009c000003670000213d0000002402200370000000000302043b000001ac0230009c000003670000213d00000000001004350000000101000039000000200010043f0000004002000039000900000002001d0000000001000019000800000003001d065b052b0000040f00000008020000290000000000200435000000200010043f00000000010000190000000902000029065b052b0000040f000000000101041a000000400200043d00000000001204350000019801000041000001980320009c00000000010240190000004001100210000001ad011001c70000065c0001042e0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000000310004c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d0000000303000039000000000203041a000000010420019000000001012002700000007f0510018f000000000601001900000000060560190000001f0560008c00000000050000190000000105002039000000000552013f0000000105500190000003690000613d000001b70100004100000000001004350000002201000039000000040010043f000001b8010000410000065d000104300000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000400310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000001010003670000000402100370000000000202043b000001ac0320009c000003670000213d0000002401100370000000000301043b0000000001000411065b05ea0000040f0000000101000039000000400200043d00000000001204350000019801000041000001980320009c00000000010240190000004001100210000001ad011001c70000065c0001042e0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000400310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000001010003670000000402100370000000000402043b000001ac0240009c000003670000213d0000002401100370000000000501043b000000000140004c000003a60000c13d000000400100043d0000004402100039000001b503000041000000000032043500000024021000390000001f030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b6011001c70000065d000104300000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000200310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000004010000390000000101100367000000000101043b000001ac0210009c000003670000213d0000000000100435000000200000043f00000040020000390000000001000019065b052b0000040f000000000101041a000000400200043d00000000001204350000019801000041000001980320009c00000000010240190000004001100210000001ad011001c70000065c0001042e0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000600310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000001010003670000000402100370000000000402043b000001ac0240009c000003670000213d0000002402100370000000000202043b000900000002001d000001ac0220009c000003670000213d0000004401100370000000000101043b000700000001001d00000000004004350000000101000039000600000001001d000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039000800000004001d065b06560000040f0000000102200190000003670000613d000000000101043b0000000002000411000500000002001d0000000000200435000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f00000008030000290000000102200190000003670000613d000000000101043b000000000201041a000000010100008a000000000112004b0000041c0000c13d000000000103001900000009020000290000000703000029065b05570000040f000000400100043d000000060200002900000000002104350000019802000041000001980310009c00000000010280190000004001100210000001ad011001c70000065c0001042e0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000000310004c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d0000000501000039000000000101041a000000ff0110018f000000400200043d00000000001204350000019801000041000001980320009c00000000010240190000004001100210000001ad011001c70000065c0001042e0000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000400310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000001010003670000000402100370000000000202043b000900000002001d000001ac0220009c000003670000213d0000002401100370000000000101043b000800000001001d0000000001000411000600000001001d00000000001004350000000101000039000700000001001d000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000003670000613d000000000101043b00000009020000290000000000200435000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000003670000613d000000000101043b000000000101041a0000000803000029000000000231004b0000040f0000813d000000400100043d0000006402100039000001af0300004100000000003204350000004402100039000001b0030000410000000000320435000000240210003900000025030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d000104300000000001000416000000000110004c000003670000c13d000000040100008a00000000011000310000019a02000041000000400310008c000000000300001900000000030240190000019a01100197000000000410004c000000000200a0190000019a0110009c00000000010300190000000001026019000000000110004c000003670000c13d00000001010003670000000402100370000000000202043b000001ac0320009c000003730000a13d00000000010000190000065d00010430000000800060043f000000000440004c000003b40000c13d000001000300008a000000000232016f000000a00020043f000000000160004c000000c001000039000000a001006039000003c30000013d0000002401100370000000000301043b0000000001000411065b05570000040f0000000101000039000000400200043d00000000001204350000019801000041000001980320009c00000000010240190000004001100210000001ad011001c70000065c0001042e0000000000500435000000000410004c00000000040000190000038d0000613d000001b30500004100000000040000190000000006430019000000000705041a000000000076043500000001055000390000002004400039000000000614004b000003860000413d0000003f01400039000000200300008a000000000331016f0000000001230019000000000331004b00000000040000190000000104004039000001990310009c000003c90000213d0000000103400190000003c90000c13d000000400010043f000900000001001d065b05410000040f000000090400002900000000014100490000019802000041000001980310009c0000000001028019000001980340009c000000000204401900000040022002100000006001100210000000000121019f0000065c0001042e0000000201000039000000000301041a0000000002530019000000000332004b000000000300001900000001030040390000000103300190000003de0000613d000001b70100004100000000001004350000001101000039000000040010043f000001b8010000410000065d000104300000000000300435000000a001000039000000000260004c000003cf0000613d000001bf0200004100000000040000190000000003040019000000000402041a000000a005300039000000000045043500000001022000390000002004300039000000000564004b000003ba0000413d000000c0013000390000001f01100039000000200200008a000000000121016f000001c002100041000001c10220009c000003cf0000813d000001b70100004100000000001004350000004101000039000000040010043f000001b8010000410000065d00010430000900000001001d000000400010043f0000008002000039065b05410000040f000000090400002900000000014100490000019802000041000001980310009c0000000001028019000001980340009c000000000204401900000040022002100000006001100210000000000121019f0000065c0001042e000800000005001d000000000021041b0000000000400435000000200000043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039000900000004001d065b06560000040f00000009060000290000000102200190000003670000613d000000000101043b000000000201041a00000008030000290000000002320019000000000021041b000000400100043d000000000031043500000198020000410000000003000414000001980430009c0000000003028019000001980410009c00000000010280190000004001100210000000c002300210000000000112019f0000019b011001c70000800d020000390000000303000039000001b4040000410000000005000019065b06510000040f0000000101200190000003670000613d000000400100043d000000010200003900000000002104350000019802000041000001980310009c00000000010280190000004001100210000001ad011001c70000065c0001042e000000000331004900000006010000290000000902000029065b05ea0000040f000000400100043d000000070200002900000000002104350000019802000041000001980310009c00000000010280190000004001100210000001ad011001c70000065c0001042e0000000701000029000000000112004b000004310000813d000000400100043d0000004402100039000001be03000041000000000032043500000024021000390000001d030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b6011001c70000065d00010430000400000002001d000000000130004c000004490000c13d000000400100043d0000006402100039000001bc0300004100000000003204350000004402100039000001bd030000410000000000320435000000240210003900000024030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d000104300000000501000029000001ac01100198000500000001001d000004620000c13d000000400100043d0000006402100039000001ba0300004100000000003204350000004402100039000001bb030000410000000000320435000000240210003900000022030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d00010430000000080100002900000000001004350000000601000029000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000003670000613d000000000101043b00000005020000290000000000200435000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f00000004030000290000000102200190000003670000613d00000007020000290000000002230049000000000101043b000000000021041b000000400100043d000000000021043500000198020000410000000003000414000001980430009c0000000003028019000001980410009c00000000010280190000004001100210000000c002300210000000000112019f0000019b011001c70000800d020000390000000303000039000001b90400004100000008050000290000000506000029065b06510000040f00000008030000290000000101200190000002d60000c13d000003670000013d0000000401000029000000000110004c00000000010000190000049f0000613d0000000601000029000000000101043300000004040000290000000302400210000000010300008a000000000223022f000000000232013f000000000121016f0000000102400210000000000121019f0000000302000029000000000012041b00000001010000290000000001010433000900000001001d000001990110009c000003c90000213d0000000401000039000600000001001d000000000101041a000000010210019000000001021002700000007f0320018f0000000002036019000400000002001d0000001f0220008c00000000020000190000000102002039000000000121013f00000001011001900000021b0000c13d0000000401000029000000200110008c000004dc0000413d0000000601000029000000000010043500000198010000410000000002000414000001980320009c0000000001024019000000c0011002100000019b011001c70000801002000039065b06560000040f0000000102200190000003670000613d00000009030000290000001f023000390000000502200270000000200330008c0000000002004019000000000301043b00000004010000290000001f01100039000000050110027000000000011300190000000002230019000000000312004b000004dc0000813d000000000002041b0000000102200039000000000312004b000004d80000413d00000009010000290000001f0110008c0000050e0000a13d0000000601000029000000000010043500000198010000410000000002000414000001980320009c0000000001024019000000c0011002100000019b011001c70000801002000039065b06560000040f000000010220019000000007020000290000000106000029000003670000613d000000090300002900000000032301700000002002000039000000000101043b000004fc0000613d0000002002000039000000000400001900000000056200190000000005050433000000000051041b000000200220003900000001011000390000002004400039000000000534004b000004f40000413d0000000904000029000000000343004b0000050a0000813d00000009030000290000000303300210000000f80330018f000000010400008a000000000334022f000000000343013f000000010400002900000000024200190000000002020433000000000232016f000000000021041b0000000101000039000000090200002900000001022002100000051b0000013d0000000901000029000000000110004c0000000001000019000005140000613d0000000801000029000000000101043300000009040000290000000302400210000000010300008a000000000223022f000000000232013f000000000221016f0000000101400210000000000112019f0000000602000029000000000012041b0000000501000039000000000201041a000001000300008a000000000232016f0000000503000029000000ff0330018f000000000232019f000000000021041b0000002001000039000001000010044300000120000004430000019c010000410000065c0001042e0000019803000041000001980410009c00000000010380190000004001100210000001980420009c00000000020380190000006002200210000000000112019f0000000002000414000001980420009c0000000002038019000000c002200210000000000112019f000001c2011001c70000801002000039065b06560000040f00000001022001900000053f0000613d000000000101043b000000000001042d00000000010000190000065d0001043000000020030000390000000004310436000000000302043300000000003404350000004001100039000000000430004c000005500000613d000000000400001900000000054100190000002004400039000000000624001900000000060604330000000000650435000000000534004b000005490000413d000000000231001900000000000204350000001f02300039000000200300008a000000000232016f0000000001210019000000000001042d0004000000000002000400000003001d000001ac01100198000005ab0000613d000001ac02200198000200000002001d000005c00000613d000300000001001d0000000000100435000000200000043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000005a90000613d000000000101043b000000000201041a0000000401000029000100000002001d000000000112004b000005d50000413d00000003010000290000000000100435000000200000043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000005a90000613d000000040200002900000001030000290000000002230049000000000101043b000000000021041b0000000201000029000000000010043500000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f0000000102200190000005a90000613d000000000101043b000000000201041a00000004030000290000000002320019000000000021041b000000400100043d000000000031043500000198020000410000000003000414000001980430009c0000000003028019000001980410009c00000000010280190000004001100210000000c002300210000000000112019f0000019b011001c70000800d020000390000000303000039000001b40400004100000003050000290000000206000029065b06510000040f0000000101200190000005a90000613d000000000001042d00000000010000190000065d00010430000000400100043d0000006402100039000001c70300004100000000003204350000004402100039000001c8030000410000000000320435000000240210003900000025030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d00010430000000400100043d0000006402100039000001c50300004100000000003204350000004402100039000001c6030000410000000000320435000000240210003900000023030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d00010430000000400100043d0000006402100039000001c30300004100000000003204350000004402100039000001c4030000410000000000320435000000240210003900000026030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d000104300003000000000002000001ac01100198000006270000613d000200000003001d000001ac02200198000300000002001d0000063c0000613d000100000001001d00000000001004350000000101000039000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f00000001022001900000000304000029000006250000613d000000000101043b0000000000400435000000200010043f00000198010000410000000002000414000001980320009c0000000001024019000000c001100210000001ae011001c70000801002000039065b06560000040f00000003060000290000000102200190000006250000613d000000000101043b0000000202000029000000000021041b000000400100043d000000000021043500000198020000410000000003000414000001980430009c0000000003028019000001980410009c00000000010280190000004001100210000000c002300210000000000112019f0000019b011001c70000800d020000390000000303000039000001b9040000410000000105000029065b06510000040f0000000101200190000006250000613d000000000001042d00000000010000190000065d00010430000000400100043d0000006402100039000001bc0300004100000000003204350000004402100039000001bd030000410000000000320435000000240210003900000024030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d00010430000000400100043d0000006402100039000001ba0300004100000000003204350000004402100039000001bb030000410000000000320435000000240210003900000022030000390000000000320435000001b10200004100000000002104350000000402100039000000200300003900000000003204350000019802000041000001980310009c00000000010280190000004001100210000001b2011001c70000065d0001043000000654002104210000000102000039000000000001042d0000000002000019000000000001042d00000659002104230000000102000039000000000001042d0000000002000019000000000001042d0000065b000004320000065c0001042e0000065d000104300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff000000000000000000000000000000000000000000000000ffffffffffffffff8000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000002000000000000000000000000000000002000000000000000000000000000000400000010000000000000000000000000000000000000000000000000000000000000000000000000040c10f1800000000000000000000000000000000000000000000000000000000a457c2d600000000000000000000000000000000000000000000000000000000a457c2d700000000000000000000000000000000000000000000000000000000a9059cbb00000000000000000000000000000000000000000000000000000000dd62ed3e0000000000000000000000000000000000000000000000000000000040c10f190000000000000000000000000000000000000000000000000000000070a082310000000000000000000000000000000000000000000000000000000095d89b410000000000000000000000000000000000000000000000000000000023b872dc0000000000000000000000000000000000000000000000000000000023b872dd00000000000000000000000000000000000000000000000000000000313ce56700000000000000000000000000000000000000000000000000000000395093510000000000000000000000000000000000000000000000000000000006fdde0300000000000000000000000000000000000000000000000000000000095ea7b30000000000000000000000000000000000000000000000000000000018160ddd000000000000000000000000ffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000200000000000000000000000000200000000000000000000000000000000000040000000000000000000000000207a65726f00000000000000000000000000000000000000000000000000000045524332303a2064656372656173656420616c6c6f77616e63652062656c6f7708c379a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000840000000000000000000000008a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19bddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef45524332303a206d696e7420746f20746865207a65726f20616464726573730000000000000000000000000000000000000000640000000000000000000000004e487b710000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000240000000000000000000000008c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925737300000000000000000000000000000000000000000000000000000000000045524332303a20617070726f766520746f20746865207a65726f206164647265726573730000000000000000000000000000000000000000000000000000000045524332303a20617070726f76652066726f6d20746865207a65726f2061646445524332303a20696e73756666696369656e7420616c6c6f77616e6365000000c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85bffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff00000000000000800200000000000000000000000000000000000000000000000000000000000000616c616e6365000000000000000000000000000000000000000000000000000045524332303a207472616e7366657220616d6f756e7420657863656564732062657373000000000000000000000000000000000000000000000000000000000045524332303a207472616e7366657220746f20746865207a65726f2061646472647265737300000000000000000000000000000000000000000000000000000045524332303a207472616e736665722066726f6d20746865207a65726f206164000000000000000000000000000000000000000000000000000000000000000018469939d00da7016fd24775544e09a6a1ad29697146a060aa4a0baa144c2ede';
const hashedBytecode = utils.hashBytecode(bytecode);
@@ -217,7 +204,7 @@ describe('utils', () => {
);
});
- it('should throw an error when bytecode is not divisible by 32', async () => {
+ it('should throw an error when bytecode is not divisible by 32', () => {
try {
utils.hashBytecode('0x0002');
} catch (e) {
@@ -227,7 +214,7 @@ describe('utils', () => {
}
});
- it('should throw an error when bytecode is has even number of 32-byte words', async () => {
+ it('should throw an error when bytecode is has even number of 32-byte words', () => {
try {
utils.hashBytecode(`0x${'00020000000000020009000000000002'.repeat(2)}`);
} catch (e) {
@@ -238,62 +225,62 @@ describe('utils', () => {
});
});
- // describe('#parseEip712()', () => {
- // it('should parse a transaction with a signature', async () => {
- // const tx: types.TransactionLike = {
- // type: 113,
- // nonce: 0,
- // maxPriorityFeePerGas: 0n,
- // maxFeePerGas: 0n,
- // gasLimit: 0n,
- // to: ADDRESS2,
- // value: 1_000_000n,
- // data: '0x',
- // chainId: 270n,
- // from: ADDRESS1,
- // customData: {
- // gasPerPubdata: 50_000n,
- // factoryDeps: [],
- // customSignature: '0x',
- // paymasterParams: undefined,
- // },
- // hash: '0x9ed410ce33179ac1ff6b721060605afc72d64febfe0c08cacab5a246602131ee',
- // };
-
- // const serializedTx =
- // '0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
- // const result = utils.parseEip712(serializedTx);
- // expect(result).toEqual(tx);
- // });
-
- // it('should parse a transaction without a signature', async () => {
- // const tx: types.TransactionLike = {
- // type: 113,
- // nonce: 0,
- // maxPriorityFeePerGas: 0n,
- // maxFeePerGas: 0n,
- // gasLimit: 0n,
- // to: ADDRESS2,
- // value: 0n,
- // data: '0x',
- // chainId: 270n,
- // from: ADDRESS1,
- // customData: {
- // gasPerPubdata: 50_000n,
- // factoryDeps: [],
- // customSignature: '0x',
- // paymasterParams: undefined,
- // },
- // hash: '0x7d3aab3e3d06d6a702228d911c2a9afaccddd52514fb89dc9d0ff81a67bfff04',
- // };
-
- // const serializedTx =
- // '0x71f83e8080808094a61464658afeaf65cccaafd3a512b69a83b77618808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
-
- // const result = utils.parseEip712(serializedTx);
- // expect(result).toEqual(tx);
- // });
- // });
+ describe('#parseEip712()', () => {
+ it('should parse a transaction with a signature', () => {
+ const tx: Eip712TxData = {
+ type: 113,
+ nonce: 0n,
+ maxPriorityFeePerGas: 0n,
+ maxFeePerGas: 0n,
+ gasLimit: 0n,
+ to: ADDRESS2,
+ value: 1_000_000n,
+ data: '0x',
+ chainId: 270n,
+ from: ADDRESS1,
+ customData: {
+ gasPerPubdata: 50_000n,
+ factoryDeps: [],
+ customSignature: '0x',
+ paymasterParams: undefined,
+ },
+ hash: '0x9ed410ce33179ac1ff6b721060605afc72d64febfe0c08cacab5a246602131ee',
+ };
+
+ const serializedTx =
+ '0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
+ const result = utils.parseEip712(serializedTx);
+ expect(result).toEqual(tx);
+ });
+
+ it('should parse a transaction without a signature', () => {
+ const tx: Eip712TxData = {
+ type: 113,
+ nonce: 0n,
+ maxPriorityFeePerGas: 0n,
+ maxFeePerGas: 0n,
+ gasLimit: 0n,
+ to: ADDRESS2,
+ value: 0n,
+ data: '0x',
+ chainId: 270n,
+ from: ADDRESS1,
+ customData: {
+ gasPerPubdata: 50_000n,
+ factoryDeps: [],
+ customSignature: '0x',
+ paymasterParams: undefined,
+ },
+ hash: '0x7d3aab3e3d06d6a702228d911c2a9afaccddd52514fb89dc9d0ff81a67bfff04',
+ };
+
+ const serializedTx =
+ '0x71f83e8080808094a61464658afeaf65cccaafd3a512b69a83b77618808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
+
+ const result = utils.parseEip712(serializedTx);
+ expect(result).toEqual(tx);
+ });
+ });
describe('#isMessageSignatureCorrect()', () => {
it('should return true if signature made by a private key was correct', async () => {
const wallet = web3Accounts.create();
@@ -314,55 +301,60 @@ describe('utils', () => {
describe('#isTypedDataSignatureCorrect()', () => {
// TODO: Needs investigation
it.skip('should return true if correct', async () => {
- // const wallet = web3Accounts.create();
- // const ADDRESS = wallet.address;
- // const PRIVATE_KEY = wallet.privateKey;
+ const account = web3Accounts.create();
+ const ADDRESS = account.address;
+ const PRIVATE_KEY = account.privateKey;
- const ADDRESS = '0x99F3629e38c617cb619682f721Aaf9F61a3DE3d3';
- const PRIVATE_KEY = '0x5b032dc95add073bbacb2a2cbe0d667855cca807abe1461c72257b7ee0d7d334';
+ // const ADDRESS = '0x99F3629e38c617cb619682f721Aaf9F61a3DE3d3';
+ // const PRIVATE_KEY =
+ // '0x5b032dc95add073bbacb2a2cbe0d667855cca807abe1461c72257b7ee0d7d334';
console.log('ADDRESS', ADDRESS);
console.log('PRIVATE_KEY', PRIVATE_KEY);
- const web3 = new Web3();
+ const web3 = new Web3('https://mainnet.era.zksync.io');
const tx = {
type: 113,
chainId: 300,
from: ADDRESS,
to: '0xa61464658AfeAf65CccaaFD3a512b69A83B77618',
- value: BigInt(7_000_000),
+ value: 7_000_000,
};
// const eip712Signer = new EIP712Signer(new Wallet(PRIVATE_KEY), web3.config.chainId);
- // const signInput = EIP712Signer.getSignInput(tx);
-
- const signInput = {
- txType: 113,
- from: '0x99F3629e38c617cb619682f721Aaf9F61a3DE3d3',
- to: '0xa61464658AfeAf65CccaaFD3a512b69A83B77618',
- gasLimit: 0n,
- gasPerPubdataByteLimit: 50000,
- maxFeePerGas: 0n,
- maxPriorityFeePerGas: 0n,
- paymaster: '0x0000000000000000000000000000000000000000',
- nonce: 0,
- value: 7000000n,
- data: '0x',
- factoryDeps: [],
- paymasterInput: '0x',
- };
+ const signInput = getSignInput(tx);
+
+ // const signInput = {
+ // txType: 113,
+ // from: '0x99F3629e38c617cb619682f721Aaf9F61a3DE3d3',
+ // to: '0xa61464658AfeAf65CccaaFD3a512b69A83B77618',
+ // gasLimit: 0n,
+ // gasPerPubdataByteLimit: 50000,
+ // maxFeePerGas: 0n,
+ // maxPriorityFeePerGas: 0n,
+ // paymaster: '0x0000000000000000000000000000000000000000',
+ // nonce: 0,
+ // value: 7000000n,
+ // data: '0x',
+ // factoryDeps: [],
+ // paymasterInput: '0x',
+ // };
const domain = {
name: 'zkSync',
version: '2',
chainId: tx.chainId,
};
console.log('domain', domain);
+ web3.eth.accounts.wallet.add(account);
- // const signature = await eip712Signer.sign(tx);
+ console.log('account', account);
+ const typedData = eip712TxTypedData(tx);
+ console.log('typedData', typedData);
+ const signature = await web3.eth.signTypedData(account.address, typedData);
- const signature =
- '0x14920a5306c8b739fd2fbdd6bb933c54c05391ab9454741b5fa1132c31c6f35d4f8a716939e43df6eb660b5298d8354d08710a0d94e8d0c14dc88032cac5deda1b';
+ // const signature =
+ // '0x14920a5306c8b739fd2fbdd6bb933c54c05391ab9454741b5fa1132c31c6f35d4f8a716939e43df6eb660b5298d8354d08710a0d94e8d0c14dc88032cac5deda1b';
console.log('signature\n\t', signature);
const isValidSignature = await utils.isTypedDataSignatureCorrect(
@@ -377,5 +369,4 @@ describe('utils', () => {
expect(isValidSignature).toBe(true);
});
});
-
});
From 93b28a51a123c04f26c5d7c4f27421c3e67a89d4 Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Tue, 11 Jun 2024 19:10:16 -0400
Subject: [PATCH 17/30] remove tx type
---
src/constants.ts | 4 +
src/eip712/EIP712Transaction.ts | 572 --------------------------------
src/eip712/constants.ts | 12 -
src/eip712/types.ts | 89 -----
src/plugin.ts | 5 -
src/types.ts | 79 ++++-
src/utils.ts | 16 +-
test/unit/utils.test.ts | 90 ++---
8 files changed, 138 insertions(+), 729 deletions(-)
delete mode 100644 src/eip712/EIP712Transaction.ts
delete mode 100644 src/eip712/constants.ts
delete mode 100644 src/eip712/types.ts
diff --git a/src/constants.ts b/src/constants.ts
index 518b15a..bdbc5c6 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -175,3 +175,7 @@ export const EIP712_TYPES = {
{ name: 'paymasterInput', type: 'bytes' },
],
};
+
+export const MAX_INTEGER = BigInt(
+ '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
+);
diff --git a/src/eip712/EIP712Transaction.ts b/src/eip712/EIP712Transaction.ts
deleted file mode 100644
index a803d2c..0000000
--- a/src/eip712/EIP712Transaction.ts
+++ /dev/null
@@ -1,572 +0,0 @@
-/*
-This file is part of web3.js.
-
-web3.js is free software: you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-web3.js is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with web3.js. If not, see .
-*/
-import type { Bytes } from 'web3-types';
-import { RLP } from '@ethereumjs/rlp';
-import { keccak256 } from 'ethereum-cryptography/keccak.js';
-import {
- bytesToHex,
- bytesToUint8Array,
- hexToBytes,
- toBigInt,
- toChecksumAddress,
- toHex,
- uint8ArrayConcat,
-} from 'web3-utils';
-import { validateNoLeadingZeroes } from 'web3-validator';
-import {
- bigIntToHex,
- bigIntToUnpaddedUint8Array,
- ecrecover,
- toUint8Array,
- uint8ArrayToBigInt,
- BaseTransaction,
- Capability,
-} from 'web3-eth-accounts';
-import type { Common, JsonTx, TxOptions, TxValuesArray } from 'web3-eth-accounts';
-import { DEFAULT_GAS_PER_PUBDATA_LIMIT, ZERO_ADDRESS } from '../constants';
-import { concat, hashBytecode, toBytes } from '../utils';
-import type { Address } from '../types';
-import { MAX_INTEGER } from './constants';
-import type { Eip712TxData, TypedDataDomain, Eip712Meta } from './types';
-
-export const EIP712_TX_TYPE = 113; // 0x71
-const EIP712_TX_TYPE_UINT8ARRAY = hexToBytes(EIP712_TX_TYPE.toString(16).padStart(2, '0'));
-
-function meetsEIP155(_v: bigint, chainId: bigint) {
- const v = Number(_v);
- const chainIdDoubled = Number(chainId) * 2;
- return v === chainIdDoubled + 35 || v === chainIdDoubled + 36;
-}
-
-/**
- * Class to create the EIP-712 Transaction object.
- * - TransactionType: 113
- * - EIP: [EIP-712](https://eips.ethereum.org/EIPS/eip-712)
- */
-
-export class EIP712Transaction extends BaseTransaction {
- private txData: Eip712TxData;
- public readonly chainId?: bigint;
- public readonly gasPrice: bigint;
- public eip712Domain: TypedDataDomain;
-
- public readonly common: Common;
-
- /**
- * Instantiate a transaction from a data dictionary.
- *
- * Format: { nonce, gasPrice, gasLimit, to, value, data, v, r, s }
- *
- * Notes:
- * - All parameters are optional and have some basic default values
- */
- public static fromTxData(txData: Eip712TxData, opts: TxOptions = {}) {
- return new EIP712Transaction(txData, opts);
- }
-
- /**
- * Instantiate a transaction from the serialized tx.
- *
- * Format: `rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s])`
- */
- public static fromSerializedTx(serialized: Uint8Array, opts: TxOptions = {}) {
- const values = RLP.decode(serialized);
-
- if (!Array.isArray(values)) {
- throw new Error('Invalid serialized tx input. Must be array');
- }
-
- return this.fromValuesArray(values as Uint8Array[], opts);
- }
-
- /**
- * Create a transaction from a values array.
- *
- * Format: `[nonce, gasPrice, gasLimit, to, value, data, v, r, s]`
- */
- public static fromValuesArray(values: TxValuesArray, opts: TxOptions = {}) {
- // If length is not 6, it has length 9. If v/r/s are empty Uint8Array, it is still an unsigned transaction
- // This happens if you get the RLP data from `raw()`
- if (values.length !== 6 && values.length !== 9) {
- throw new Error(
- 'Invalid transaction. Only expecting 6 values (for unsigned tx) or 9 values (for signed tx).',
- );
- }
-
- const [nonce, gasPrice, gasLimit, to, value, data, v, r, s] = values;
-
- validateNoLeadingZeroes({ nonce, gasPrice, gasLimit, value, v, r, s });
-
- return new EIP712Transaction(
- {
- nonce,
- // @ts-ignore
- gasPrice,
- gasLimit,
- to,
- value,
- data,
- v,
- r,
- s,
- },
- opts,
- );
- }
-
- /**
- * This constructor takes the values, validates them, assigns them and freezes the object.
- *
- * It is not recommended to use this constructor directly. Instead use
- * the static factory methods to assist in creating a Transaction object from
- * varying data types.
- */
- public constructor(txData: Eip712TxData, opts: TxOptions = {}) {
- super({ ...txData, type: EIP712_TX_TYPE }, opts);
-
- this.txData = EIP712Transaction.getSignInput(txData);
- this.eip712Domain = {
- name: 'zkSync',
- version: '2',
- chainId: txData.chainId,
- };
- this.common = this._validateTxV(this.v, opts.common);
- console.log('txData', txData);
- this.chainId = txData.chainId ? toBigInt(txData.chainId) : undefined;
- console.log('this.chainId', this.chainId);
- this.gasPrice = uint8ArrayToBigInt(
- // @ts-ignore
- toUint8Array(txData.gasPrice === '' ? '0x' : txData.gasPrice),
- );
-
- if (this.gasPrice * this.gasLimit > MAX_INTEGER) {
- const msg = this._errorMsg('gas limit * gasPrice cannot exceed MAX_INTEGER (2^256-1)');
- throw new Error(msg);
- }
- this._validateCannotExceedMaxInteger({ gasPrice: this.gasPrice });
- BaseTransaction._validateNotArray(txData);
-
- if (this.common.gteHardfork('spuriousDragon')) {
- if (!this.isSigned()) {
- this.activeCapabilities.push(Capability.EIP155ReplayProtection);
- } else {
- // EIP155 spec:
- // If block.number >= 2,675,000 and v = CHAIN_ID * 2 + 35 or v = CHAIN_ID * 2 + 36
- // then when computing the hash of a transaction for purposes of signing or recovering
- // instead of hashing only the first six elements (i.e. nonce, gasprice, startgas, to, value, data)
- // hash nine elements, with v replaced by CHAIN_ID, r = 0 and s = 0.
- // v and chain ID meet EIP-155 conditions
-
- if (meetsEIP155(this.v!, this.common.chainId())) {
- this.activeCapabilities.push(Capability.EIP155ReplayProtection);
- }
- }
- }
-
- const freeze = opts?.freeze ?? true;
- if (freeze) {
- Object.freeze(this);
- }
- }
-
- /**
- * Generates the EIP712 typed data from provided transaction. Optional fields are populated by zero values.
- *
- * @param transaction The transaction request that needs to be populated.
- *
- * @example
- *
- * const tx = EIP712Signer.getSignInput({
- * type: utils.EIP712_TX_TYPE,
- * to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618",
- * value: BigInt(7_000_000),
- * from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049",
- * nonce: 0,
- * chainId: BigInt(270),
- * gasPrice: BigInt(250_000_000),
- * gasLimit: BigInt(21_000),
- * customData: {},
- * });
- */
- static getSignInput(transaction: Eip712TxData) {
- const maxFeePerGas = toBigInt(transaction.maxFeePerGas || transaction.gasPrice || 0n);
- const maxPriorityFeePerGas = toBigInt(transaction.maxPriorityFeePerGas || maxFeePerGas);
- const gasPerPubdataByteLimit = toBigInt(
- transaction.customData?.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT,
- );
- return {
- txType: transaction.type || EIP712_TX_TYPE,
- from: transaction.from ? toHex(transaction.from) : undefined,
- to: transaction.to ? toHex(transaction.to) : undefined,
- gasLimit: transaction.gasLimit || 0n,
- gasPerPubdataByteLimit: gasPerPubdataByteLimit,
- customData: transaction.customData,
- maxFeePerGas,
- maxPriorityFeePerGas,
- paymaster: transaction.customData?.paymasterParams?.paymaster || ZERO_ADDRESS,
- nonce: transaction.nonce || 0,
- value: transaction.value || BigInt(0),
- data: transaction.data || '0x',
- factoryDeps:
- transaction.customData?.factoryDeps?.map((dep: Bytes) => hashBytecode(dep)) || [],
- paymasterInput: transaction.customData?.paymasterParams?.paymasterInput || '0x',
- };
- }
-
- /**
- * Returns a Uint8Array Array of the raw Uint8Arrays of the legacy transaction, in order.
- *
- * Format: `[nonce, gasPrice, gasLimit, to, value, data, v, r, s]`
- *
- * For legacy txs this is also the correct format to add transactions
- * to a block with {@link Block.fromValuesArray} (use the `serialize()` method
- * for typed txs).
- *
- * For an unsigned tx this method returns the empty Uint8Array values
- * for the signature parameters `v`, `r` and `s`. For an EIP-155 compliant
- * representation have a look at {@link Transaction.getMessageToSign}.
- */
- public raw(): TxValuesArray {
- const transaction = this.txData;
- console.log('raw', this.chainId);
- if (!this.chainId) {
- throw Error("Transaction chainId isn't set!");
- }
-
- if (!transaction.from) {
- throw new Error(
- 'Explicitly providing `from` field is required for EIP712 transactions!',
- );
- }
- const from = transaction.from;
- const meta: Eip712Meta = transaction.customData ?? {};
- // const maxFeePerGas = transaction.maxFeePerGas || transaction.gasPrice || 0;
- // const maxPriorityFeePerGas = transaction.maxPriorityFeePerGas || maxFeePerGas;
- console.log('transaction', transaction);
- console.log('1');
- console.log('toBytes(transaction.nonce || 0)', toBytes('0x0'));
- console.log('2');
-
- const fields: any[] = [
- toBytes(transaction.nonce || 0),
- // toBytes(maxPriorityFeePerGas),
- // toBytes(maxFeePerGas),
- toBytes(transaction.gasLimit || 0),
- transaction.to ? toChecksumAddress(transaction.to as Address) : '0x',
- toBytes(transaction.value || 0),
- transaction.data || '0x',
- ];
- console.log('fields', fields);
-
- // if (signature) {
- // const sig = new SignatureObject(signature);
- fields.push(
- this.v !== undefined ? bigIntToUnpaddedUint8Array(this.v) : Uint8Array.from([]),
- );
- fields.push(
- this.r !== undefined ? bigIntToUnpaddedUint8Array(this.r) : Uint8Array.from([]),
- );
- fields.push(
- this.s !== undefined ? bigIntToUnpaddedUint8Array(this.s) : Uint8Array.from([]),
- );
- // } else {
- // fields.push(toBytes(transaction.chainId));
- // fields.push("0x");
- // fields.push("0x");
- // }
- console.log('this.chainId', this.chainId);
- fields.push(toBytes(this.chainId));
- fields.push(toChecksumAddress(from));
-
- // Add meta
- console.log('meta', meta);
- fields.push(toBytes(meta.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT));
- fields.push((meta.factoryDeps ?? []).map(dep => toHex(dep)));
- if (meta.customSignature && bytesToUint8Array(meta.customSignature).length === 0) {
- throw new Error('Empty signatures are not supported!');
- }
- fields.push(meta.customSignature || '0x');
-
- if (meta.paymasterParams) {
- fields.push([
- meta.paymasterParams.paymaster,
- toHex(meta.paymasterParams.paymasterInput),
- ]);
- } else {
- fields.push([]);
- }
- return fields;
- }
-
- /**
- * Serializes an EIP712 transaction and includes a signature if provided.
- *
- * @param transaction The transaction that needs to be serialized.
- * @param signature Ethers signature to be included in the transaction.
- * @throws {Error} Throws an error if:
- * - `transaction.customData.customSignature` is an empty string. The transaction should be signed, and the `transaction.customData.customSignature` field should be populated with the signature. It should not be specified if the transaction is not signed.
- * - `transaction.chainId` is not provided.
- * - `transaction.from` is not provided.
- *
- * @example Serialize EIP712 transaction without signature.
- *
- * const serializedTx = utils.serializeEip712({ chainId: 270, from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049" }, null);
- *
- * // serializedTx = "0x71ea8080808080808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0"
- *
- * @example Serialize EIP712 transaction with signature.
- *
- * const signature = ethers.Signature.from("0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a");
- *
- * const serializedTx = utils.serializeEip712(
- * {
- * chainId: 270,
- * from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049",
- * to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618",
- * value: 1_000_000,
- * },
- * signature
- * );
- * // serializedTx = "0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0"
- */
- // @ts-ignore
- serialize() {
- return concat([new Uint8Array([EIP712_TX_TYPE]), RLP.encode(this.raw())]);
- }
-
- /**
- * Returns the unsigned tx (hashed or raw), which can be used
- * to sign the transaction (e.g. for sending to a hardware wallet).
- *
- * Note: the raw message message format for the legacy tx is not RLP encoded
- * and you might need to do yourself with:
- *
- * ```javascript
- * import { bufArrToArr } from '../util'
- * import { RLP } from '../rlp'
- * const message = tx.getMessageToSign(false)
- * const serializedMessage = RLP.encode(message) // use this for the HW wallet input
- * ```
- *
- * @param hashMessage - Return hashed message if set to true (default: true)
- */
- public getMessageToSign(hashMessage = true) {
- const base = this.raw().slice(0, 9);
- const message = uint8ArrayConcat(EIP712_TX_TYPE_UINT8ARRAY, RLP.encode(base));
- if (hashMessage) {
- return keccak256(message);
- }
- return message;
- }
-
- /**
- * The amount of gas paid for the data in this tx
- */
- public getDataFee(): bigint {
- // TODO: this is a temporary solution until we have a better way to calculate the data fee
- if (this.cache.dataFee && this.cache.dataFee.hardfork === this.common.hardfork()) {
- return this.cache.dataFee.value;
- }
-
- if (Object.isFrozen(this)) {
- this.cache.dataFee = {
- value: super.getDataFee(),
- hardfork: this.common.hardfork(),
- };
- }
-
- return super.getDataFee();
- }
-
- /**
- * The up front amount that an account must have for this transaction to be valid
- */
- public getUpfrontCost(): bigint {
- // TODO: this is a temporary solution until we have a better way to calculate the upfront cost
- return this.gasLimit * this.gasPrice + this.value;
- }
-
- /**
- * Computes a sha3-256 hash of the serialized tx.
- *
- * This method can only be used for signed txs (it throws otherwise).
- * Use {@link Transaction.getMessageToSign} to get a tx hash for the purpose of signing.
- */
- public hash(): Uint8Array {
- if (!this.isSigned()) {
- const msg = this._errorMsg('Cannot call hash method if transaction is not signed');
- throw new Error(msg);
- }
-
- if (Object.isFrozen(this)) {
- if (!this.cache.hash) {
- this.cache.hash = keccak256(RLP.encode(this.raw()));
- }
- return this.cache.hash;
- }
-
- return keccak256(RLP.encode(this.raw()));
- }
-
- /**
- * Computes a sha3-256 hash which can be used to verify the signature
- */
-
- public getMessageToVerifySignature(): Uint8Array {
- if (!this.isSigned()) {
- const msg = this._errorMsg('This transaction is not signed');
- throw new Error(msg);
- }
- return this.getMessageToSign();
- }
-
- /**
- * Returns the public key of the sender
- */
- public getSenderPublicKey(): Uint8Array {
- if (!this.isSigned()) {
- const msg = this._errorMsg('Cannot call this method if transaction is not signed');
- throw new Error(msg);
- }
-
- const msgHash = this.getMessageToVerifySignature();
- const { v, r, s } = this;
-
- this._validateHighS();
-
- try {
- return ecrecover(
- msgHash,
- v! + BigInt(27), // Recover the 27 which was stripped from ecsign
- bigIntToUnpaddedUint8Array(r!),
- bigIntToUnpaddedUint8Array(s!),
- );
- } catch (e: any) {
- const msg = this._errorMsg('Invalid Signature');
- throw new Error(msg);
- }
- }
-
- /**
- * Process the v, r, s values from the `sign` method of the base transaction.
- */
- protected _processSignature(_v: bigint, r: Uint8Array, s: Uint8Array) {
- let v = _v;
- if (this.supports(Capability.EIP155ReplayProtection)) {
- v += this.common.chainId() * BigInt(2) + BigInt(8);
- }
-
- const opts = { ...this.txOptions, common: this.common };
-
- return EIP712Transaction.fromTxData(
- {
- chainId: this.chainId,
- nonce: this.nonce,
- gasLimit: this.gasLimit,
- to: this.to,
- value: this.value,
- data: this.data,
- v: v - BigInt(27), // This looks extremely hacky: /util actually adds 27 to the value, the recovery bit is either 0 or 1.
- r: uint8ArrayToBigInt(r),
- s: uint8ArrayToBigInt(s),
- },
- opts,
- );
- }
-
- /**
- * Returns an object with the JSON representation of the transaction.
- */
- public toJSON(): JsonTx {
- return {
- nonce: bigIntToHex(this.nonce),
- gasPrice: bigIntToHex(this.gasPrice),
- gasLimit: bigIntToHex(this.gasLimit),
- to: this.to !== undefined ? this.to.toString() : undefined,
- value: bigIntToHex(this.value),
- data: bytesToHex(this.data),
- v: this.v !== undefined ? bigIntToHex(this.v) : undefined,
- r: this.r !== undefined ? bigIntToHex(this.r) : undefined,
- s: this.s !== undefined ? bigIntToHex(this.s) : undefined,
- };
- }
-
- /**
- * Validates tx's `v` value
- */
- private _validateTxV(_v?: bigint, common?: Common): Common {
- let chainIdBigInt;
- const v = _v !== undefined ? Number(_v) : undefined;
- // Check for valid v values in the scope of a signed legacy tx
- if (v !== undefined) {
- // v is 1. not matching the EIP-155 chainId included case and...
- // v is 2. not matching the classic v=27 or v=28 case
- if (v < 37 && v !== 27 && v !== 28) {
- throw new Error(
- `Legacy txs need either v = 27/28 or v >= 37 (EIP-155 replay protection), got v = ${v}`,
- );
- }
- }
-
- // No unsigned tx and EIP-155 activated and chain ID included
- if (
- v !== undefined &&
- v !== 0 &&
- (!common || common.gteHardfork('spuriousDragon')) &&
- v !== 27 &&
- v !== 28
- ) {
- if (common) {
- if (!meetsEIP155(BigInt(v), common.chainId())) {
- throw new Error(
- `Incompatible EIP155-based V ${v} and chain id ${common.chainId()}. See the Common parameter of the Transaction constructor to set the chain id.`,
- );
- }
- } else {
- // Derive the original chain ID
- let numSub;
- if ((v - 35) % 2 === 0) {
- numSub = 35;
- } else {
- numSub = 36;
- }
- // Use derived chain ID to create a proper Common
- chainIdBigInt = BigInt(v - numSub) / BigInt(2);
- }
- }
- return this._getCommon(common, chainIdBigInt);
- }
-
- /**
- * Return a compact error string representation of the object
- */
- public errorStr() {
- let errorStr = this._getSharedErrorPostfix();
- errorStr += ` gasPrice=${this.gasPrice}`;
- return errorStr;
- }
-
- /**
- * Internal helper function to create an annotated error message
- *
- * @param msg Base error message
- * @hidden
- */
- protected _errorMsg(msg: string) {
- return `${msg} (${this.errorStr()})`;
- }
-}
diff --git a/src/eip712/constants.ts b/src/eip712/constants.ts
deleted file mode 100644
index e669696..0000000
--- a/src/eip712/constants.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
- * Default gas per pubdata byte for L2 transactions.
- * This value is utilized when inserting a default value for type 2
- * and EIP712 type transactions.
- *
- * @constant
- */
-// It is a realistic value, but it is large enough to fill into any batch regardless of the pubdata price.
-export const DEFAULT_GAS_PER_PUBDATA_LIMIT = 50_000;
-export const MAX_INTEGER = BigInt(
- '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
-);
diff --git a/src/eip712/types.ts b/src/eip712/types.ts
deleted file mode 100644
index 007b37e..0000000
--- a/src/eip712/types.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import type { Bytes, Numbers } from 'web3-types';
-import type { Address } from 'web3';
-import type { FeeMarketEIP1559TxData } from 'web3-eth-accounts';
-
-export interface TypedDataDomain {
- /**
- * The human-readable name of the signing domain.
- */
- name?: null | string;
-
- /**
- * The major version of the signing domain.
- */
- version?: null | string;
-
- /**
- * The chain ID of the signing domain.
- */
- chainId?: null | Numbers;
-
- /**
- * The the address of the contract that will verify the signature.
- */
- verifyingContract?: null | string;
-
- /**
- * A salt used for purposes decided by the specific domain.
- */
- salt?: null | Bytes;
-}
-
-/**
- * A specific field of a structured [[link-eip-712]] type.
- */
-export interface TypedDataField {
- /**
- * The field name.
- */
- name: string;
-
- /**
- * The type of the field.
- */
- type: string;
-}
-
-export type PaymasterParams = {
- /** The address of the paymaster. */
- paymaster: Address;
- /** The bytestream input for the paymaster. */
- paymasterInput: Bytes;
-};
-
-export type Eip712Meta = {
- /** The maximum amount of gas the user is willing to pay for a single byte of pubdata. */
- gasPerPubdata?: Numbers;
- /** An array of bytes containing the bytecode of the contract being deployed and any related contracts it can deploy. */
- factoryDeps?: Bytes[];
- /** Custom signature used for cases where the signer's account is not an EOA. */
- customSignature?: Bytes;
- /** Parameters for configuring the custom paymaster for the transaction. */
- paymasterParams?: PaymasterParams;
-};
-
-export type Eip712TxData = FeeMarketEIP1559TxData & {
- /** The custom data for EIP712 transaction metadata. */
- customData?: null | Eip712Meta;
- from?: Address;
- hash?: string;
- signature?: string;
-};
-
-export const EIP712_TYPES = {
- Transaction: [
- { name: 'txType', type: 'uint256' },
- { name: 'from', type: 'uint256' },
- { name: 'to', type: 'uint256' },
- { name: 'gasLimit', type: 'uint256' },
- { name: 'gasPerPubdataByteLimit', type: 'uint256' },
- { name: 'maxFeePerGas', type: 'uint256' },
- { name: 'maxPriorityFeePerGas', type: 'uint256' },
- { name: 'paymaster', type: 'uint256' },
- { name: 'nonce', type: 'uint256' },
- { name: 'value', type: 'uint256' },
- { name: 'data', type: 'bytes' },
- { name: 'factoryDeps', type: 'bytes32[]' },
- { name: 'paymasterInput', type: 'bytes' },
- ],
-};
diff --git a/src/plugin.ts b/src/plugin.ts
index 1331669..36d67c1 100644
--- a/src/plugin.ts
+++ b/src/plugin.ts
@@ -1,5 +1,4 @@
import type { Web3Context, Web3RequestManager } from 'web3-core';
-import { TransactionFactory } from 'web3-eth-accounts';
import type { Address } from 'web3-types';
import { Contract } from 'web3-eth-contract';
import { Web3PluginBase } from 'web3-core';
@@ -7,9 +6,7 @@ import { Web3PluginBase } from 'web3-core';
import { IERC20ABI } from './contracts/IERC20';
import { RpcMethods } from './rpc.methods';
import { ETH_ADDRESS, ZERO_ADDRESS } from './constants';
-
import { IL2BridgeABI } from './contracts/IL2Bridge';
-import { EIP712Transaction, EIP712_TX_TYPE } from './eip712/EIP712Transaction';
import { IZkSyncABI } from './contracts/IZkSyncStateTransition';
import { IBridgehubABI } from './contracts/IBridgehub';
import { IContractDeployerABI } from './contracts/IContractDeployer';
@@ -75,8 +72,6 @@ export class ZkSyncPlugin extends Web3PluginBase {
this.wethBridgeL2 = '';
this._l2BridgeContracts = {};
this._erc20Contracts = {};
- // @ts-ignore
- TransactionFactory.registerTransactionType(EIP712_TX_TYPE, EIP712Transaction);
this.Contracts = {
ZkSyncMainContract: new Contract(IZkSyncABI, ''),
BridgehubContract: new Contract(IBridgehubABI, ''),
diff --git a/src/types.ts b/src/types.ts
index 4fd810d..c7a2f4b 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,5 +1,5 @@
// import { FMT_BYTES, FMT_NUMBER, TransactionReceipt, Web3Eth } from 'web3';
-
+import type { FeeMarketEIP1559TxData } from 'web3-eth-accounts';
// // TODO: // is it needed to be re-exported from web3
// import { watchTransactionForConfirmations } from 'web3-eth/lib/types/utils/watch_transaction_for_confirmations.js';
@@ -13,11 +13,10 @@ import type {
TransactionReceipt,
} from 'web3-types';
-import type {
- // FeeMarketEIP1559Transaction,
- FeeMarketEIP1559TxData,
- // TxOptions
-} from 'web3-eth-accounts';
+// import type {
+// FeeMarketEIP1559Transaction,
+// TxOptions
+// } from 'web3-eth-accounts';
// import {
// EIP712_TX_TYPE,
@@ -832,3 +831,71 @@ export interface EstimateFee {
max_fee_per_gas: Numbers;
max_priority_fee_per_gas: Numbers;
}
+
+export interface TypedDataDomain {
+ /**
+ * The human-readable name of the signing domain.
+ */
+ name?: null | string;
+
+ /**
+ * The major version of the signing domain.
+ */
+ version?: null | string;
+
+ /**
+ * The chain ID of the signing domain.
+ */
+ chainId?: null | Numbers;
+
+ /**
+ * The the address of the contract that will verify the signature.
+ */
+ verifyingContract?: null | string;
+
+ /**
+ * A salt used for purposes decided by the specific domain.
+ */
+ salt?: null | Bytes;
+}
+
+/**
+ * A specific field of a structured [[link-eip-712]] type.
+ */
+export interface TypedDataField {
+ /**
+ * The field name.
+ */
+ name: string;
+
+ /**
+ * The type of the field.
+ */
+ type: string;
+}
+
+export type Eip712TxData = FeeMarketEIP1559TxData & {
+ /** The custom data for EIP712 transaction metadata. */
+ customData?: null | Eip712Meta;
+ from?: Address;
+ hash?: string;
+ signature?: string;
+};
+
+export const EIP712_TYPES = {
+ Transaction: [
+ { name: 'txType', type: 'uint256' },
+ { name: 'from', type: 'uint256' },
+ { name: 'to', type: 'uint256' },
+ { name: 'gasLimit', type: 'uint256' },
+ { name: 'gasPerPubdataByteLimit', type: 'uint256' },
+ { name: 'maxFeePerGas', type: 'uint256' },
+ { name: 'maxPriorityFeePerGas', type: 'uint256' },
+ { name: 'paymaster', type: 'uint256' },
+ { name: 'nonce', type: 'uint256' },
+ { name: 'value', type: 'uint256' },
+ { name: 'data', type: 'bytes' },
+ { name: 'factoryDeps', type: 'bytes32[]' },
+ { name: 'paymasterInput', type: 'bytes' },
+ ],
+};
diff --git a/src/utils.ts b/src/utils.ts
index 447fc51..8ecab74 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -16,7 +16,13 @@ import { RLP } from '@ethereumjs/rlp'; // to be used instead of the one at zksyn
import { bytesToHex, toBigInt, toHex } from 'web3-utils';
import type { Address } from 'web3';
import type { Bytes, Eip712TypedData } from 'web3-types';
-import type { DeploymentInfo, Eip712Meta, EthereumSignature, PaymasterParams } from './types';
+import type {
+ DeploymentInfo,
+ Eip712Meta,
+ Eip712TxData,
+ EthereumSignature,
+ PaymasterParams,
+} from './types';
import {
// PaymasterParams,
PriorityOpTree,
@@ -55,7 +61,6 @@ import {
} from './constants';
import type { RpcMethods } from './rpc.methods';
-import type { Eip712TxData } from './eip712/types';
// export * from './paymaster-utils';
// export * from './smart-account-utils';
@@ -527,6 +532,7 @@ export const getSignInput = (transaction: Eip712TxData) => {
transaction.customData?.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT,
);
return {
+ chainId: transaction.chainId ? toHex(transaction.chainId) : undefined,
txType: transaction.type || EIP712_TX_TYPE,
from: transaction.from ? toHex(transaction.from) : undefined,
to: transaction.to ? toHex(transaction.to) : undefined,
@@ -704,7 +710,9 @@ export function serializeEip712(transaction: Eip712TxData, signature?: Signature
? new Uint8Array()
: toBytes(transaction.gasLimit!),
transaction.to ? web3Utils.toChecksumAddress(toHex(transaction.to)) : '0x',
- toHex(transaction.value || 0) === '0x0' ? new Uint8Array() : toBytes(nonce),
+ toHex(transaction.value || 0) === '0x0'
+ ? new Uint8Array()
+ : toBytes(toHex(transaction.value || 0)),
toHex(transaction.data || '0x'),
];
@@ -740,6 +748,8 @@ export function serializeEip712(transaction: Eip712TxData, signature?: Signature
fields.push([]);
}
+ console.log(fields);
+
return concat([new Uint8Array([EIP712_TX_TYPE]), RLP.encode(fields)]);
}
diff --git a/test/unit/utils.test.ts b/test/unit/utils.test.ts
index 9b94eea..fd0b417 100644
--- a/test/unit/utils.test.ts
+++ b/test/unit/utils.test.ts
@@ -1,14 +1,11 @@
import { Web3 } from 'web3';
import * as web3Accounts from 'web3-eth-accounts';
-import {
- // types,
- utils,
-} from '../../src';
+import type { types } from '../../src';
+import { utils } from '../../src';
import { ADDRESS1, ADDRESS2 } from '../utils';
import * as constants from '../../src/constants';
-import type { Eip712TxData } from '../../src/eip712/types';
-import { eip712TxTypedData, getSignInput } from '../../src/utils';
+import { getSignInput, serializeEip712 } from '../../src/utils';
describe('utils', () => {
describe('#getHashedL2ToL1Msg()', () => {
@@ -104,7 +101,7 @@ describe('utils', () => {
});
describe('#checkBaseCost()', () => {
- it('should throw an error if the base cost bigger than value', () => {
+ it('should throw an error if the base cost bigger than value', async () => {
const baseCost = 100;
const value = 99;
try {
@@ -160,23 +157,26 @@ describe('utils', () => {
expect(result).toBe(tx);
});
- // it('should return a serialized transaction with provided signature', async () => {
- // const tx =
- // '0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
- // const signature = ethers.Signature.from(
- // '0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a',
- // );
- // const result = utils.serializeEip712(
- // {
- // chainId: 270,
- // from: ADDRESS1,
- // to: ADDRESS2,
- // value: 1_000_000,
- // },
- // signature,
- // );
- // expect(result).toBe(tx);
- // });
+ it.only('should return a serialized transaction with provided signature', async () => {
+ const tx =
+ '0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
+
+ const signature =
+ '0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a';
+
+ // wallet.sign(message).signature;
+ // const signature = s.toString();
+ const result = utils.serializeEip712(
+ {
+ chainId: 270,
+ from: ADDRESS1,
+ to: ADDRESS2,
+ value: 1_000_000,
+ },
+ signature,
+ );
+ expect(result).toBe(tx);
+ });
});
describe('#hashBytecode()', () => {
@@ -227,7 +227,7 @@ describe('utils', () => {
describe('#parseEip712()', () => {
it('should parse a transaction with a signature', () => {
- const tx: Eip712TxData = {
+ const tx: types.Eip712TxData = {
type: 113,
nonce: 0n,
maxPriorityFeePerGas: 0n,
@@ -254,7 +254,7 @@ describe('utils', () => {
});
it('should parse a transaction without a signature', () => {
- const tx: Eip712TxData = {
+ const tx: types.Eip712TxData = {
type: 113,
nonce: 0n,
maxPriorityFeePerGas: 0n,
@@ -301,24 +301,23 @@ describe('utils', () => {
describe('#isTypedDataSignatureCorrect()', () => {
// TODO: Needs investigation
it.skip('should return true if correct', async () => {
- const account = web3Accounts.create();
- const ADDRESS = account.address;
- const PRIVATE_KEY = account.privateKey;
+ // const account = web3Accounts.create();
+ // const ADDRESS = account.address;
+ // const PRIVATE_KEY = account.privateKey;
- // const ADDRESS = '0x99F3629e38c617cb619682f721Aaf9F61a3DE3d3';
- // const PRIVATE_KEY =
- // '0x5b032dc95add073bbacb2a2cbe0d667855cca807abe1461c72257b7ee0d7d334';
- console.log('ADDRESS', ADDRESS);
- console.log('PRIVATE_KEY', PRIVATE_KEY);
+ const PRIVATE_KEY =
+ '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110';
+
+ const account = web3Accounts.privateKeyToAccount(PRIVATE_KEY);
const web3 = new Web3('https://mainnet.era.zksync.io');
const tx = {
type: 113,
- chainId: 300,
- from: ADDRESS,
+ chainId: 270,
+ from: account.address,
to: '0xa61464658AfeAf65CccaaFD3a512b69A83B77618',
- value: 7_000_000,
+ value: 7_000_000n,
};
// const eip712Signer = new EIP712Signer(new Wallet(PRIVATE_KEY), web3.config.chainId);
@@ -349,17 +348,24 @@ describe('utils', () => {
web3.eth.accounts.wallet.add(account);
console.log('account', account);
- const typedData = eip712TxTypedData(tx);
- console.log('typedData', typedData);
- const signature = await web3.eth.signTypedData(account.address, typedData);
+ console.log('signInput', signInput);
+ // const hash = eip712TxHash(tx);
+ // console.log('typedData', hash);
+
+ const signature = serializeEip712(signInput);
+ console.log('signature', signature);
+ // const signature = await web3.eth.signTypedData(account.address, typedData);
// const signature =
// '0x14920a5306c8b739fd2fbdd6bb933c54c05391ab9454741b5fa1132c31c6f35d4f8a716939e43df6eb660b5298d8354d08710a0d94e8d0c14dc88032cac5deda1b';
+ // '0x71f8418080808094a61464658afeaf65cccaafd3a512b69a83b77618836acfc08082012c808082012c9434a535b103a4641ae33bb774a5922765a9ac2a6282c350c080c0'
console.log('signature\n\t', signature);
-
+ expect(signature).toBe(
+ '0x14920a5306c8b739fd2fbdd6bb933c54c05391ab9454741b5fa1132c31c6f35d4f8a716939e43df6eb660b5298d8354d08710a0d94e8d0c14dc88032cac5deda1b',
+ );
const isValidSignature = await utils.isTypedDataSignatureCorrect(
web3,
- ADDRESS,
+ account.address,
domain,
constants.EIP712_TYPES,
signInput,
From 7c00ed8202d2dc0d0eca393b7d72426cffed841f Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Wed, 12 Jun 2024 16:00:40 -0400
Subject: [PATCH 18/30] fix tests
---
src/types.ts | 18 ------------------
src/utils.ts | 16 +++++++---------
test/unit/utils.test.ts | 16 +++++++---------
3 files changed, 14 insertions(+), 36 deletions(-)
diff --git a/src/types.ts b/src/types.ts
index c7a2f4b..3bd3c7b 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -881,21 +881,3 @@ export type Eip712TxData = FeeMarketEIP1559TxData & {
hash?: string;
signature?: string;
};
-
-export const EIP712_TYPES = {
- Transaction: [
- { name: 'txType', type: 'uint256' },
- { name: 'from', type: 'uint256' },
- { name: 'to', type: 'uint256' },
- { name: 'gasLimit', type: 'uint256' },
- { name: 'gasPerPubdataByteLimit', type: 'uint256' },
- { name: 'maxFeePerGas', type: 'uint256' },
- { name: 'maxPriorityFeePerGas', type: 'uint256' },
- { name: 'paymaster', type: 'uint256' },
- { name: 'nonce', type: 'uint256' },
- { name: 'value', type: 'uint256' },
- { name: 'data', type: 'bytes' },
- { name: 'factoryDeps', type: 'bytes32[]' },
- { name: 'paymasterInput', type: 'bytes' },
- ],
-};
diff --git a/src/utils.ts b/src/utils.ts
index 8ecab74..c068d74 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -176,9 +176,9 @@ export class SignatureObject {
v?: web3Types.Numbers,
) {
if (typeof rOrSignature === 'string') {
- if (rOrSignature.length !== 132) {
- throw new Error('Invalid signature length');
- }
+ // if (rOrSignature.length !== 132) {
+ // throw new Error('Invalid signature length');
+ // }
// Initialize with a single string parameter
const signature = rOrSignature;
this.r = web3Accounts.toUint8Array(signature.slice(0, 66));
@@ -710,18 +710,16 @@ export function serializeEip712(transaction: Eip712TxData, signature?: Signature
? new Uint8Array()
: toBytes(transaction.gasLimit!),
transaction.to ? web3Utils.toChecksumAddress(toHex(transaction.to)) : '0x',
- toHex(transaction.value || 0) === '0x0'
- ? new Uint8Array()
- : toBytes(toHex(transaction.value || 0)),
+ toHex(transaction.value || 0) === '0x0' ? new Uint8Array() : toHex(transaction.value || 0),
toHex(transaction.data || '0x'),
];
if (signature) {
const sig = new SignatureObject(signature);
// fields.push(toBytes(sig.yParity));
- fields.push(toBytes(Number(sig.v) === 27 ? 0 : 1));
- fields.push(toBytes(sig.r));
- fields.push(toBytes(sig.s));
+ fields.push(toHex(Number(sig.v) === 27 ? 0 : 1));
+ fields.push(toHex(sig.r));
+ fields.push(toHex(sig.s));
} else {
fields.push(toHex(transaction.chainId));
fields.push('0x');
diff --git a/test/unit/utils.test.ts b/test/unit/utils.test.ts
index fd0b417..570a96a 100644
--- a/test/unit/utils.test.ts
+++ b/test/unit/utils.test.ts
@@ -1,11 +1,9 @@
import { Web3 } from 'web3';
import * as web3Accounts from 'web3-eth-accounts';
-
import type { types } from '../../src';
import { utils } from '../../src';
import { ADDRESS1, ADDRESS2 } from '../utils';
import * as constants from '../../src/constants';
-import { getSignInput, serializeEip712 } from '../../src/utils';
describe('utils', () => {
describe('#getHashedL2ToL1Msg()', () => {
@@ -160,11 +158,11 @@ describe('utils', () => {
it.only('should return a serialized transaction with provided signature', async () => {
const tx =
'0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
-
- const signature =
- '0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a';
-
- // wallet.sign(message).signature;
+ const signature = new utils.SignatureObject(
+ '0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a',
+ );
+ const recoveredTx = utils.parseEip712(tx);
+ console.log('recoveredTx', recoveredTx);
// const signature = s.toString();
const result = utils.serializeEip712(
{
@@ -322,7 +320,7 @@ describe('utils', () => {
// const eip712Signer = new EIP712Signer(new Wallet(PRIVATE_KEY), web3.config.chainId);
- const signInput = getSignInput(tx);
+ const signInput = utils.getSignInput(tx);
// const signInput = {
// txType: 113,
@@ -352,7 +350,7 @@ describe('utils', () => {
// const hash = eip712TxHash(tx);
// console.log('typedData', hash);
- const signature = serializeEip712(signInput);
+ const signature = utils.serializeEip712(signInput);
console.log('signature', signature);
// const signature = await web3.eth.signTypedData(account.address, typedData);
From a874af906960bd8ce10e6b80f900b9386403653a Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Thu, 13 Jun 2024 01:09:30 -0400
Subject: [PATCH 19/30] all tests covered
---
src/Eip712.ts | 318 +++++++++++++++++++++++++++++++++++
src/utils.ts | 358 +++++++---------------------------------
test/unit/utils.test.ts | 109 ++++--------
3 files changed, 412 insertions(+), 373 deletions(-)
create mode 100644 src/Eip712.ts
diff --git a/src/Eip712.ts b/src/Eip712.ts
new file mode 100644
index 0000000..73fc1ce
--- /dev/null
+++ b/src/Eip712.ts
@@ -0,0 +1,318 @@
+import { bytesToHex, toBigInt, toHex } from 'web3-utils';
+import type { Bytes, Eip712TypedData } from 'web3-types';
+import * as web3Abi from 'web3-eth-abi';
+import * as web3Utils from 'web3-utils';
+import * as web3Acccounts from 'web3-eth-accounts';
+import { RLP } from '@ethereumjs/rlp';
+import type { Address } from 'web3';
+import * as ethereumCryptography from 'ethereum-cryptography/secp256k1';
+import {
+ DEFAULT_GAS_PER_PUBDATA_LIMIT,
+ EIP712_TX_TYPE,
+ EIP712_TYPES,
+ ZERO_ADDRESS,
+} from './constants';
+import type { Eip712Meta, Eip712TxData, EthereumSignature, PaymasterParams } from './types';
+import type { SignatureLike } from './utils';
+import { concat, hashBytecode, SignatureObject, toBytes } from './utils';
+
+function handleAddress(value?: Uint8Array): string | null {
+ if (!value) {
+ return null;
+ }
+ const hexValue = bytesToHex(value);
+ if (hexValue === '0x') {
+ return null;
+ }
+
+ return web3Utils.toChecksumAddress(hexValue);
+}
+
+function handleNumber(value?: Uint8Array): bigint {
+ if (!value) {
+ return 0n;
+ }
+ const hexValue = bytesToHex(value);
+ if (hexValue === '0x') {
+ return 0n;
+ }
+ return toBigInt(hexValue);
+}
+function arrayToPaymasterParams(arr: Uint8Array): PaymasterParams | undefined {
+ if (arr.length === 0) {
+ return undefined;
+ }
+ if (arr.length !== 2) {
+ throw new Error(
+ `Invalid paymaster parameters, expected to have length of 2, found ${arr.length}!`,
+ );
+ }
+
+ return {
+ paymaster: web3Utils.toChecksumAddress(toHex(arr[0])),
+ paymasterInput: web3Utils.bytesToUint8Array(toHex(arr[1])),
+ };
+}
+
+export class Eip712 {
+ static getSignInput(transaction: Eip712TxData) {
+ const maxFeePerGas = toHex(transaction.maxFeePerGas || transaction.gasPrice || 0n);
+ const maxPriorityFeePerGas = toHex(transaction.maxPriorityFeePerGas || maxFeePerGas);
+ const gasPerPubdataByteLimit = toHex(
+ transaction.customData?.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT,
+ );
+ return {
+ txType: transaction.type || EIP712_TX_TYPE,
+ from: transaction.from ? toHex(transaction.from) : undefined,
+ to: transaction.to ? toHex(transaction.to) : undefined,
+ gasLimit: transaction.gasLimit || 0,
+ gasPerPubdataByteLimit: gasPerPubdataByteLimit,
+ customData: transaction.customData,
+ maxFeePerGas,
+ maxPriorityFeePerGas,
+ paymaster: transaction.customData?.paymasterParams?.paymaster || ZERO_ADDRESS,
+ nonce: transaction.nonce || 0,
+ value: transaction.value || toHex(0),
+ data: transaction.data || '0x',
+ factoryDeps:
+ transaction.customData?.factoryDeps?.map((dep: Bytes) => hashBytecode(dep)) || [],
+ paymasterInput: transaction.customData?.paymasterParams?.paymasterInput || '0x',
+ };
+ }
+
+ static txTypedData(transaction: Eip712TxData): Eip712TypedData {
+ return {
+ types: EIP712_TYPES,
+ primaryType: 'Transaction',
+ domain: {
+ name: 'zkSync',
+ version: '2',
+ chainId: Number(transaction.chainId),
+ },
+ message: Eip712.getSignInput(transaction),
+ };
+ }
+ /**
+ * Returns the hash of an EIP712 transaction.
+ *
+ * @param transaction The EIP-712 transaction.
+ * @param ethSignature The ECDSA signature of the transaction.
+ *
+ * @example
+ *
+ *
+ */
+ static txHash(transaction: Eip712TxData, ethSignature?: EthereumSignature): string {
+ const bytes: string[] = [];
+
+ const typedDataStruct = Eip712.txTypedData(transaction);
+
+ bytes.push(web3Abi.getEncodedEip712Data(typedDataStruct, true));
+ bytes.push(web3Utils.keccak256(Eip712.getSignature(typedDataStruct.message, ethSignature)));
+ return web3Utils.keccak256(concat(bytes));
+ }
+ /**
+ * Parses an EIP712 transaction from a payload.
+ *
+ * @param payload The payload to parse.
+ *
+ * @example
+ *
+ *
+ * const serializedTx =
+ * "0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0";
+ * const tx: types.TransactionLike = utils.parse(serializedTx);
+ * /*
+ * tx: types.Eip712TxData = {
+ * type: 113,
+ * nonce: 0,
+ * maxPriorityFeePerGas: BigInt(0),
+ * maxFeePerGas: BigInt(0),
+ * gasLimit: BigInt(0),
+ * to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618",
+ * value: BigInt(1000000),
+ * data: "0x",
+ * chainId: BigInt(270),
+ * from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049",
+ * customData: {
+ * gasPerPubdata: BigInt(50000),
+ * factoryDeps: [],
+ * customSignature: "0x",
+ * paymasterParams: null,
+ * },
+ * hash: "0x9ed410ce33179ac1ff6b721060605afc72d64febfe0c08cacab5a246602131ee",
+ * };
+ * *\/
+ */
+
+ static fromSerializedTx(payload: Bytes): Eip712TxData {
+ const bytes = web3Utils.bytesToUint8Array(payload);
+
+ const raw = RLP.decode(bytes.slice(1)) as Array;
+ const transaction: Eip712TxData = {
+ type: EIP712_TX_TYPE,
+ nonce: handleNumber(raw[0]),
+ maxPriorityFeePerGas: handleNumber(raw[1]),
+ maxFeePerGas: handleNumber(raw[2]),
+ gasLimit: handleNumber(raw[3]),
+ to: handleAddress(raw[4]) as Address,
+ value: handleNumber(raw[5]),
+ data: bytesToHex(raw[6]),
+ chainId: handleNumber(raw[10]),
+ from: handleAddress(raw[11]) as Address,
+ customData: {
+ gasPerPubdata: handleNumber(raw[12]),
+ factoryDeps: raw[13] as unknown as string[],
+ customSignature: bytesToHex(raw[14]),
+ paymasterParams: arrayToPaymasterParams(raw[15]),
+ },
+ };
+ const ethSignature = {
+ v: Number(handleNumber(raw[7])),
+ r: raw[8],
+ s: raw[9],
+ };
+
+ if (
+ (web3Utils.toHex(ethSignature.r) === '0x' ||
+ web3Utils.toHex(ethSignature.s) === '0x') &&
+ !transaction.customData?.customSignature
+ ) {
+ return transaction;
+ }
+
+ if (
+ ethSignature.v !== 0 &&
+ ethSignature.v !== 1 &&
+ !transaction.customData?.customSignature
+ ) {
+ throw new Error('Failed to parse signature!');
+ }
+
+ if (!transaction.customData?.customSignature) {
+ transaction.signature = new SignatureObject(ethSignature).toString();
+ }
+
+ transaction.hash = Eip712.txHash(transaction, ethSignature);
+
+ return transaction;
+ }
+
+ static getSignature(transaction: Eip712TxData, ethSignature?: EthereumSignature): Uint8Array {
+ if (
+ transaction?.customData?.customSignature &&
+ transaction.customData.customSignature.length
+ ) {
+ return web3Utils.bytesToUint8Array(transaction.customData.customSignature);
+ }
+
+ if (!ethSignature) {
+ throw new Error('No signature provided!');
+ }
+
+ const r = web3Utils.bytesToUint8Array(
+ web3Utils.padLeft(web3Utils.toHex(ethSignature.r), 32 * 2),
+ );
+ const s = web3Utils.bytesToUint8Array(
+ web3Utils.padLeft(web3Utils.toHex(ethSignature.s), 32 * 2),
+ );
+ const v = ethSignature.v;
+
+ return new Uint8Array([...r, ...s, v]);
+ }
+
+ static serialize(transaction: Eip712TxData, signature?: SignatureLike): string {
+ if (!transaction.chainId) {
+ throw Error("Transaction chainId isn't set!");
+ }
+
+ if (!transaction.from) {
+ throw new Error(
+ 'Explicitly providing `from` field is required for EIP712 transactions!',
+ );
+ }
+ const from = transaction.from;
+ const meta: Eip712Meta = transaction.customData ?? {};
+ const maxFeePerGas = toHex(transaction.maxFeePerGas || transaction.gasPrice || 0);
+ const maxPriorityFeePerGas = toHex(transaction.maxPriorityFeePerGas || maxFeePerGas);
+
+ const nonce = toHex(transaction.nonce || 0);
+ const fields: Array = [
+ nonce === '0x0' ? new Uint8Array() : toBytes(nonce),
+ maxPriorityFeePerGas === '0x0' ? new Uint8Array() : toBytes(maxPriorityFeePerGas),
+ maxFeePerGas === '0x0' ? new Uint8Array() : toBytes(maxFeePerGas),
+ toHex(transaction.gasLimit || 0) === '0x0'
+ ? new Uint8Array()
+ : toBytes(transaction.gasLimit!),
+ transaction.to ? web3Utils.toChecksumAddress(toHex(transaction.to)) : '0x',
+ toHex(transaction.value || 0) === '0x0'
+ ? new Uint8Array()
+ : toHex(transaction.value || 0),
+ toHex(transaction.data || '0x'),
+ ];
+
+ if (signature) {
+ const signatureObject = new SignatureObject(signature);
+
+ fields.push(toHex(Number(signatureObject.v) === 27 ? 0 : 1));
+ fields.push(toHex(signatureObject.r));
+ fields.push(toHex(signatureObject.s));
+
+ // const sig = new SignatureObject(signature);
+ // fields.push(toBytes(sig.yParity));
+ } else {
+ fields.push(toHex(transaction.chainId));
+ fields.push('0x');
+ fields.push('0x');
+ }
+ fields.push(toHex(transaction.chainId));
+ fields.push(web3Utils.toChecksumAddress(from));
+
+ // Add meta
+ fields.push(toHex(meta.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT));
+ fields.push((meta.factoryDeps ?? []).map(dep => web3Utils.toHex(dep)));
+
+ if (
+ meta.customSignature &&
+ web3Utils.bytesToUint8Array(meta.customSignature).length === 0
+ ) {
+ throw new Error('Empty signatures are not supported!');
+ }
+ fields.push(meta.customSignature || '0x');
+
+ if (meta.paymasterParams) {
+ fields.push([
+ meta.paymasterParams.paymaster,
+ web3Utils.toHex(meta.paymasterParams.paymasterInput),
+ ]);
+ } else {
+ fields.push([]);
+ }
+
+ return concat([new Uint8Array([EIP712_TX_TYPE]), RLP.encode(fields)]);
+ }
+
+ static sign(hash: string, PRIVATE_KEY: string) {
+ return web3Acccounts.sign(hash, PRIVATE_KEY, true);
+ }
+ static computePublicKey(key: Bytes, compressed?: boolean): string {
+ let bytes = toBytes(key);
+
+ // private key
+ if (bytes.length === 32) {
+ const pubKey = ethereumCryptography.secp256k1.getPublicKey(bytes, !!compressed);
+ return toHex(pubKey);
+ }
+
+ // raw public key; use uncompressed key with 0x04 prefix
+ if (bytes.length === 64) {
+ const pub = new Uint8Array(65);
+ pub[0] = 0x04;
+ pub.set(bytes, 1);
+ bytes = pub;
+ }
+
+ const point = ethereumCryptography.secp256k1.ProjectivePoint.fromHex(bytes);
+ return toHex(point.toRawBytes(compressed));
+ }
+}
diff --git a/src/utils.ts b/src/utils.ts
index c068d74..6dd5ac6 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -11,18 +11,8 @@ import * as web3Accounts from 'web3-eth-accounts';
import * as web3Types from 'web3-types';
import * as web3Abi from 'web3-eth-abi';
import * as web3Contract from 'web3-eth-contract';
-
-import { RLP } from '@ethereumjs/rlp'; // to be used instead of the one at zksync-ethers: Provider from ./provider
-import { bytesToHex, toBigInt, toHex } from 'web3-utils';
-import type { Address } from 'web3';
-import type { Bytes, Eip712TypedData } from 'web3-types';
-import type {
- DeploymentInfo,
- Eip712Meta,
- Eip712TxData,
- EthereumSignature,
- PaymasterParams,
-} from './types';
+import type { Bytes } from 'web3-types';
+import type { DeploymentInfo, EthereumSignature } from './types';
import {
// PaymasterParams,
PriorityOpTree,
@@ -52,16 +42,14 @@ import {
EIP1271_MAGIC_VALUE,
L1_FEE_ESTIMATION_COEF_NUMERATOR,
L1_FEE_ESTIMATION_COEF_DENOMINATOR,
- EIP712_TX_TYPE,
- EIP712_TYPES,
- DEFAULT_GAS_PER_PUBDATA_LIMIT,
- ZERO_ADDRESS,
// EIP712_TX_TYPE,
// DEFAULT_GAS_PER_PUBDATA_LIMIT,
} from './constants';
import type { RpcMethods } from './rpc.methods';
+export * from './Eip712';
+
// export * from './paymaster-utils';
// export * from './smart-account-utils';
// export { EIP712_TYPES } from './signer';
@@ -140,7 +128,6 @@ export function contractFunctionId(value: string): string {
return web3Utils.keccak256(web3Utils.utf8ToBytes(value));
}
-// TODO: needs to test for the first parameter being Eip712TypedData
function recoverSignerAddress(
messageOrData: string | web3Types.Eip712TypedData,
signature: SignatureLike,
@@ -153,16 +140,31 @@ function recoverSignerAddress(
}
if (typeof signature === 'string') {
- return web3Accounts.recover(message, signature);
+ return web3Accounts.recover(message, signature, undefined, undefined, undefined, true);
}
const r = web3Utils.toHex(signature.r);
const s = web3Utils.toHex(signature.s);
const v = web3Utils.toHex(signature.v);
- return web3Accounts.recover(message, v, r, s);
+ return web3Accounts.recover(message, v, r, s, undefined, true);
}
+function _getBytes(value: Bytes): Uint8Array {
+ if (value instanceof Uint8Array) {
+ return value;
+ }
+ if (typeof value === 'string' && value.match(/^0x([0-9a-f][0-9a-f])*$/i)) {
+ const result = new Uint8Array((value.length - 2) / 2);
+ let offset = 2;
+ for (let i = 0; i < result.length; i++) {
+ result[i] = parseInt(value.substring(offset, offset + 2), 16);
+ offset += 2;
+ }
+ return result;
+ }
+ throw new Error('Invalid BytesLike value');
+}
export class SignatureObject {
public r: Uint8Array;
public s: Uint8Array;
@@ -176,14 +178,26 @@ export class SignatureObject {
v?: web3Types.Numbers,
) {
if (typeof rOrSignature === 'string') {
- // if (rOrSignature.length !== 132) {
- // throw new Error('Invalid signature length');
- // }
- // Initialize with a single string parameter
- const signature = rOrSignature;
- this.r = web3Accounts.toUint8Array(signature.slice(0, 66));
- this.s = web3Accounts.toUint8Array(`0x${signature.slice(66, 130)}`);
- this.v = BigInt(web3Utils.hexToNumber(`0x${signature.slice(130, 132)}`));
+ const bytes: Uint8Array = _getBytes(rOrSignature);
+
+ if (bytes.length === 64) {
+ const r = bytes.slice(0, 32);
+ const s = bytes.slice(32, 64);
+ const v = BigInt(s[0] & 0x80 ? 28 : 27);
+ s[0] &= 0x7f;
+ this.r = r;
+ this.s = s;
+ this.v = v;
+ } else if (bytes.length === 65) {
+ const r = bytes.slice(0, 32);
+ const s = bytes.slice(32, 64);
+ const v = BigInt(SignatureObject.getNormalizedV(bytes[64]));
+ this.r = r;
+ this.s = s;
+ this.v = v;
+ } else {
+ throw new Error('Invalid signature length');
+ }
} else if (
(rOrSignature as EthereumSignature).r &&
(rOrSignature as EthereumSignature).s &&
@@ -201,7 +215,26 @@ export class SignatureObject {
this.v = BigInt(signature.v!);
}
}
+ static getNormalizedV(v: number): 27 | 28 {
+ if (v === 0 || v === 27) {
+ return 27;
+ }
+ if (v === 1 || v === 28) {
+ return 28;
+ }
+ // Otherwise, EIP-155 v means odd is 27 and even is 28
+ return v & 1 ? 27 : 28;
+ }
+ concat(datas: ReadonlyArray): string {
+ return '0x' + datas.map(d => web3Utils.toHex(d).substring(2)).join('');
+ }
+ get yParity(): 0 | 1 {
+ return this.v === 27n ? 0 : 1;
+ }
+ public get serialized(): string {
+ return this.concat([this.r, this.s, this.yParity ? '0x1c' : '0x1b']);
+ }
public toString() {
return `${this.r}${this.s.slice(2)}${web3Utils.toHex(this.v).slice(2)}`;
}
@@ -487,270 +520,6 @@ export function hashBytecode(bytecode: web3Types.Bytes): Uint8Array {
return hash;
}
-function handleAddress(value?: Uint8Array): string | null {
- if (!value) {
- return null;
- }
- const hexValue = bytesToHex(value);
- if (hexValue === '0x') {
- return null;
- }
-
- return web3Utils.toChecksumAddress(hexValue);
-}
-
-function handleNumber(value?: Uint8Array): bigint {
- if (!value) {
- return 0n;
- }
- const hexValue = bytesToHex(value);
- if (hexValue === '0x') {
- return 0n;
- }
- return toBigInt(hexValue);
-}
-function arrayToPaymasterParams(arr: Uint8Array): PaymasterParams | undefined {
- if (arr.length === 0) {
- return undefined;
- }
- if (arr.length !== 2) {
- throw new Error(
- `Invalid paymaster parameters, expected to have length of 2, found ${arr.length}!`,
- );
- }
-
- return {
- paymaster: web3Utils.toChecksumAddress(toHex(arr[0])),
- paymasterInput: web3Utils.bytesToUint8Array(toHex(arr[1])),
- };
-}
-
-export const getSignInput = (transaction: Eip712TxData) => {
- const maxFeePerGas = toHex(transaction.maxFeePerGas || transaction.gasPrice || 0n);
- const maxPriorityFeePerGas = toHex(transaction.maxPriorityFeePerGas || maxFeePerGas);
- const gasPerPubdataByteLimit = toHex(
- transaction.customData?.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT,
- );
- return {
- chainId: transaction.chainId ? toHex(transaction.chainId) : undefined,
- txType: transaction.type || EIP712_TX_TYPE,
- from: transaction.from ? toHex(transaction.from) : undefined,
- to: transaction.to ? toHex(transaction.to) : undefined,
- gasLimit: transaction.gasLimit || 0,
- gasPerPubdataByteLimit: gasPerPubdataByteLimit,
- customData: transaction.customData,
- maxFeePerGas,
- maxPriorityFeePerGas,
- paymaster: transaction.customData?.paymasterParams?.paymaster || ZERO_ADDRESS,
- nonce: transaction.nonce || 0,
- value: transaction.value || toHex(0),
- data: transaction.data || '0x',
- factoryDeps:
- transaction.customData?.factoryDeps?.map((dep: Bytes) => hashBytecode(dep)) || [],
- paymasterInput: transaction.customData?.paymasterParams?.paymasterInput || '0x',
- };
-};
-
-export function eip712TxTypedData(transaction: Eip712TxData): Eip712TypedData {
- return {
- types: EIP712_TYPES,
- primaryType: 'Transaction',
- domain: {
- name: 'zkSync',
- version: '2',
- chainId: Number(transaction.chainId),
- },
- message: getSignInput(transaction),
- };
-}
-/**
- * Returns the hash of an EIP712 transaction.
- *
- * @param transaction The EIP-712 transaction.
- * @param ethSignature The ECDSA signature of the transaction.
- *
- * @example
- *
- *
- */
-export function eip712TxHash(transaction: Eip712TxData, ethSignature?: EthereumSignature): string {
- const bytes: string[] = [];
-
- const typedDataStruct = eip712TxTypedData(transaction);
-
- bytes.push(web3Abi.getEncodedEip712Data(typedDataStruct, true));
- bytes.push(web3Utils.keccak256(getSignature(typedDataStruct.message, ethSignature)));
- return web3Utils.keccak256(concat(bytes));
-}
-/**
- * Parses an EIP712 transaction from a payload.
- *
- * @param payload The payload to parse.
- *
- * @example
- *
- *
- * const serializedTx =
- * "0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0";
- * const tx: types.TransactionLike = utils.parseEip712(serializedTx);
- * /*
- * tx: types.Eip712TxData = {
- * type: 113,
- * nonce: 0,
- * maxPriorityFeePerGas: BigInt(0),
- * maxFeePerGas: BigInt(0),
- * gasLimit: BigInt(0),
- * to: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618",
- * value: BigInt(1000000),
- * data: "0x",
- * chainId: BigInt(270),
- * from: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049",
- * customData: {
- * gasPerPubdata: BigInt(50000),
- * factoryDeps: [],
- * customSignature: "0x",
- * paymasterParams: null,
- * },
- * hash: "0x9ed410ce33179ac1ff6b721060605afc72d64febfe0c08cacab5a246602131ee",
- * };
- * *\/
- */
-
-export function parseEip712(payload: web3Types.Bytes): Eip712TxData {
- const bytes = web3Utils.bytesToUint8Array(payload);
-
- // try using: RLP.decode
- const raw = RLP.decode(bytes.slice(1)) as Array;
- const transaction: Eip712TxData = {
- type: EIP712_TX_TYPE,
- nonce: handleNumber(raw[0]),
- maxPriorityFeePerGas: handleNumber(raw[1]),
- maxFeePerGas: handleNumber(raw[2]),
- gasLimit: handleNumber(raw[3]),
- to: handleAddress(raw[4]) as Address,
- value: handleNumber(raw[5]),
- data: bytesToHex(raw[6]),
- chainId: handleNumber(raw[10]),
- from: handleAddress(raw[11]) as Address,
- customData: {
- gasPerPubdata: handleNumber(raw[12]),
- factoryDeps: raw[13] as unknown as string[],
- customSignature: bytesToHex(raw[14]),
- paymasterParams: arrayToPaymasterParams(raw[15]),
- },
- };
- const ethSignature = {
- v: Number(handleNumber(raw[7])),
- r: raw[8],
- s: raw[9],
- };
-
- if (
- (web3Utils.toHex(ethSignature.r) === '0x' || web3Utils.toHex(ethSignature.s) === '0x') &&
- !transaction.customData?.customSignature
- ) {
- return transaction;
- }
-
- if (ethSignature.v !== 0 && ethSignature.v !== 1 && !transaction.customData?.customSignature) {
- throw new Error('Failed to parse signature!');
- }
-
- if (!transaction.customData?.customSignature) {
- transaction.signature = new SignatureObject(ethSignature).toString();
- }
-
- transaction.hash = eip712TxHash(transaction, ethSignature);
-
- return transaction;
-}
-
-export function getSignature(
- transaction: Eip712TxData,
- ethSignature?: EthereumSignature,
-): Uint8Array {
- if (transaction?.customData?.customSignature && transaction.customData.customSignature.length) {
- return web3Utils.bytesToUint8Array(transaction.customData.customSignature);
- }
-
- if (!ethSignature) {
- throw new Error('No signature provided!');
- }
-
- const r = web3Utils.bytesToUint8Array(
- web3Utils.padLeft(web3Utils.toHex(ethSignature.r), 32 * 2),
- );
- const s = web3Utils.bytesToUint8Array(
- web3Utils.padLeft(web3Utils.toHex(ethSignature.s), 32 * 2),
- );
- const v = ethSignature.v;
-
- return new Uint8Array([...r, ...s, v]);
-}
-
-export function serializeEip712(transaction: Eip712TxData, signature?: SignatureLike): string {
- if (!transaction.chainId) {
- throw Error("Transaction chainId isn't set!");
- }
-
- if (!transaction.from) {
- throw new Error('Explicitly providing `from` field is required for EIP712 transactions!');
- }
- const from = transaction.from;
- const meta: Eip712Meta = transaction.customData ?? {};
- const maxFeePerGas = toHex(transaction.maxFeePerGas || transaction.gasPrice || 0);
- const maxPriorityFeePerGas = toHex(transaction.maxPriorityFeePerGas || maxFeePerGas);
-
- const nonce = toHex(transaction.nonce || 0);
- const fields: Array = [
- nonce === '0x0' ? new Uint8Array() : toBytes(nonce),
- maxPriorityFeePerGas === '0x0' ? new Uint8Array() : toBytes(maxPriorityFeePerGas),
- maxFeePerGas === '0x0' ? new Uint8Array() : toBytes(maxFeePerGas),
- toHex(transaction.gasLimit || 0) === '0x0'
- ? new Uint8Array()
- : toBytes(transaction.gasLimit!),
- transaction.to ? web3Utils.toChecksumAddress(toHex(transaction.to)) : '0x',
- toHex(transaction.value || 0) === '0x0' ? new Uint8Array() : toHex(transaction.value || 0),
- toHex(transaction.data || '0x'),
- ];
-
- if (signature) {
- const sig = new SignatureObject(signature);
- // fields.push(toBytes(sig.yParity));
- fields.push(toHex(Number(sig.v) === 27 ? 0 : 1));
- fields.push(toHex(sig.r));
- fields.push(toHex(sig.s));
- } else {
- fields.push(toHex(transaction.chainId));
- fields.push('0x');
- fields.push('0x');
- }
- fields.push(toHex(transaction.chainId));
- fields.push(web3Utils.toChecksumAddress(from));
-
- // Add meta
- fields.push(toHex(meta.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT));
- fields.push((meta.factoryDeps ?? []).map(dep => web3Utils.toHex(dep)));
-
- if (meta.customSignature && web3Utils.bytesToUint8Array(meta.customSignature).length === 0) {
- throw new Error('Empty signatures are not supported!');
- }
- fields.push(meta.customSignature || '0x');
-
- if (meta.paymasterParams) {
- fields.push([
- meta.paymasterParams.paymaster,
- web3Utils.toHex(meta.paymasterParams.paymasterInput),
- ]);
- } else {
- fields.push([]);
- }
-
- console.log(fields);
-
- return concat([new Uint8Array([EIP712_TX_TYPE]), RLP.encode(fields)]);
-}
-
/**
* Returns the hash of the L2 priority operation from a given transaction receipt and L2 address.
*
@@ -1089,17 +858,14 @@ export async function isTypedDataSignatureCorrect(
value: Record,
signature: SignatureLike,
): Promise {
- const data: web3Types.Eip712TypedData = {
+ const typedDataStruct: web3Types.Eip712TypedData = {
domain,
types,
primaryType: 'Transaction',
message: value,
};
- // could be also:
- const message = web3Abi.getEncodedEip712Data(data);
- return isSignatureCorrect(context, address, message, signature);
- // return isSignatureCorrect(context, address, data, signature);
+ return isSignatureCorrect(context, address, typedDataStruct, signature);
}
/**
diff --git a/test/unit/utils.test.ts b/test/unit/utils.test.ts
index 570a96a..361ee57 100644
--- a/test/unit/utils.test.ts
+++ b/test/unit/utils.test.ts
@@ -1,5 +1,6 @@
import { Web3 } from 'web3';
import * as web3Accounts from 'web3-eth-accounts';
+import * as web3Abi from 'web3-eth-abi';
import type { types } from '../../src';
import { utils } from '../../src';
import { ADDRESS1, ADDRESS2 } from '../utils';
@@ -115,7 +116,7 @@ describe('utils', () => {
describe('#serializeEip712()', () => {
it('should throw an error when `tx.chainId` is not specified', () => {
try {
- utils.serializeEip712({});
+ utils.Eip712.serialize({});
} catch (e) {
expect((e as Error).message).toBe("Transaction chainId isn't set!");
}
@@ -123,7 +124,7 @@ describe('utils', () => {
it('should throw an error when `tx.from` is not specified', () => {
try {
- utils.serializeEip712({ chainId: 270 });
+ utils.Eip712.serialize({ chainId: 270 });
} catch (e) {
expect((e as Error).message).toBe(
'Explicitly providing `from` field is required for EIP712 transactions!',
@@ -133,7 +134,7 @@ describe('utils', () => {
it('should throw an error when `tx.customData.customSignature` is empty string', () => {
try {
- utils.serializeEip712({
+ utils.Eip712.serialize({
chainId: 270,
from: ADDRESS1,
customData: {
@@ -148,30 +149,24 @@ describe('utils', () => {
it('should return a serialized transaction with populated default values', () => {
const tx =
'0x71ea8080808080808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
- const result = utils.serializeEip712({
+ const result = utils.Eip712.serialize({
chainId: 270,
from: ADDRESS1,
});
expect(result).toBe(tx);
});
- it.only('should return a serialized transaction with provided signature', async () => {
+ it('should return a serialized transaction with provided signature', async () => {
const tx =
'0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
- const signature = new utils.SignatureObject(
- '0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a',
- );
- const recoveredTx = utils.parseEip712(tx);
- console.log('recoveredTx', recoveredTx);
- // const signature = s.toString();
- const result = utils.serializeEip712(
+ const result = utils.Eip712.serialize(
{
chainId: 270,
from: ADDRESS1,
to: ADDRESS2,
value: 1_000_000,
},
- signature,
+ '0x73a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aaf87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a',
);
expect(result).toBe(tx);
});
@@ -247,7 +242,7 @@ describe('utils', () => {
const serializedTx =
'0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
- const result = utils.parseEip712(serializedTx);
+ const result = utils.Eip712.fromSerializedTx(serializedTx);
expect(result).toEqual(tx);
});
@@ -275,16 +270,16 @@ describe('utils', () => {
const serializedTx =
'0x71f83e8080808094a61464658afeaf65cccaafd3a512b69a83b77618808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
- const result = utils.parseEip712(serializedTx);
+ const result = utils.Eip712.fromSerializedTx(serializedTx);
expect(result).toEqual(tx);
});
});
describe('#isMessageSignatureCorrect()', () => {
it('should return true if signature made by a private key was correct', async () => {
- const wallet = web3Accounts.create();
- const ADDRESS = wallet.address;
+ const account = web3Accounts.create();
+ const ADDRESS = account.address;
const message = 'Hello, world!';
- const signature = wallet.sign(message).signature;
+ const signature = utils.Eip712.sign(message, account.privateKey).signature;
const web3 = new Web3();
const isValidSignature = await utils.isMessageSignatureCorrect(
web3,
@@ -297,79 +292,39 @@ describe('utils', () => {
});
describe('#isTypedDataSignatureCorrect()', () => {
- // TODO: Needs investigation
- it.skip('should return true if correct', async () => {
- // const account = web3Accounts.create();
- // const ADDRESS = account.address;
- // const PRIVATE_KEY = account.privateKey;
-
+ it('should return true if correct', async () => {
const PRIVATE_KEY =
'0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110';
-
- const account = web3Accounts.privateKeyToAccount(PRIVATE_KEY);
-
- const web3 = new Web3('https://mainnet.era.zksync.io');
-
+ const ADDRESS = '0x36615Cf349d7F6344891B1e7CA7C72883F5dc049';
const tx = {
type: 113,
chainId: 270,
- from: account.address,
+ from: ADDRESS,
to: '0xa61464658AfeAf65CccaaFD3a512b69A83B77618',
value: 7_000_000n,
};
- // const eip712Signer = new EIP712Signer(new Wallet(PRIVATE_KEY), web3.config.chainId);
-
- const signInput = utils.getSignInput(tx);
-
- // const signInput = {
- // txType: 113,
- // from: '0x99F3629e38c617cb619682f721Aaf9F61a3DE3d3',
- // to: '0xa61464658AfeAf65CccaaFD3a512b69A83B77618',
- // gasLimit: 0n,
- // gasPerPubdataByteLimit: 50000,
- // maxFeePerGas: 0n,
- // maxPriorityFeePerGas: 0n,
- // paymaster: '0x0000000000000000000000000000000000000000',
- // nonce: 0,
- // value: 7000000n,
- // data: '0x',
- // factoryDeps: [],
- // paymasterInput: '0x',
- // };
- const domain = {
- name: 'zkSync',
- version: '2',
- chainId: tx.chainId,
- };
- console.log('domain', domain);
- web3.eth.accounts.wallet.add(account);
-
- console.log('account', account);
- console.log('signInput', signInput);
- // const hash = eip712TxHash(tx);
- // console.log('typedData', hash);
-
- const signature = utils.serializeEip712(signInput);
- console.log('signature', signature);
- // const signature = await web3.eth.signTypedData(account.address, typedData);
-
- // const signature =
- // '0x14920a5306c8b739fd2fbdd6bb933c54c05391ab9454741b5fa1132c31c6f35d4f8a716939e43df6eb660b5298d8354d08710a0d94e8d0c14dc88032cac5deda1b';
- // '0x71f8418080808094a61464658afeaf65cccaafd3a512b69a83b77618836acfc08082012c808082012c9434a535b103a4641ae33bb774a5922765a9ac2a6282c350c080c0'
- console.log('signature\n\t', signature);
- expect(signature).toBe(
- '0x14920a5306c8b739fd2fbdd6bb933c54c05391ab9454741b5fa1132c31c6f35d4f8a716939e43df6eb660b5298d8354d08710a0d94e8d0c14dc88032cac5deda1b',
+ const typedDataStruct = utils.Eip712.txTypedData(tx);
+
+ const message = web3Abi.getEncodedEip712Data(typedDataStruct);
+
+ const web3 = new Web3('http://localhost:8545');
+ const acc = web3.eth.accounts.privateKeyToAccount(PRIVATE_KEY);
+ web3.eth.accounts.wallet.add(acc);
+ // const signature = web3.eth.accounts.wallet.sign(message, true);
+ const signature = utils.Eip712.sign(message, PRIVATE_KEY);
+
+ expect(signature.signature).toBe(
+ '0x5ea12f3d54a1624d7e7f5161dbf6ab746c3335e643b2966264e740cf8e10e9b64b0251fb79d9a5b11730387085a0d58f105926f72e20242ecb274639991939ca1b',
);
const isValidSignature = await utils.isTypedDataSignatureCorrect(
web3,
- account.address,
- domain,
+ ADDRESS,
+ typedDataStruct.domain,
constants.EIP712_TYPES,
- signInput,
- signature,
+ utils.Eip712.getSignInput(tx),
+ signature.signature,
);
- console.log('isValidSignature', isValidSignature);
expect(isValidSignature).toBe(true);
});
});
From 0c5918f0e26d8f4c0b2974c2f2b4a30d0ef31532 Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Thu, 13 Jun 2024 13:16:31 -0400
Subject: [PATCH 20/30] fixes
---
src/Eip712.ts | 54 ++++++++++++++++++++++++-----------------
test/unit/utils.test.ts | 36 ++++++++++++---------------
2 files changed, 48 insertions(+), 42 deletions(-)
diff --git a/src/Eip712.ts b/src/Eip712.ts
index 73fc1ce..77d5624 100644
--- a/src/Eip712.ts
+++ b/src/Eip712.ts
@@ -5,7 +5,6 @@ import * as web3Utils from 'web3-utils';
import * as web3Acccounts from 'web3-eth-accounts';
import { RLP } from '@ethereumjs/rlp';
import type { Address } from 'web3';
-import * as ethereumCryptography from 'ethereum-cryptography/secp256k1';
import {
DEFAULT_GAS_PER_PUBDATA_LIMIT,
EIP712_TX_TYPE,
@@ -54,7 +53,7 @@ function arrayToPaymasterParams(arr: Uint8Array): PaymasterParams | undefined {
};
}
-export class Eip712 {
+export class EIP712 {
static getSignInput(transaction: Eip712TxData) {
const maxFeePerGas = toHex(transaction.maxFeePerGas || transaction.gasPrice || 0n);
const maxPriorityFeePerGas = toHex(transaction.maxPriorityFeePerGas || maxFeePerGas);
@@ -89,7 +88,7 @@ export class Eip712 {
version: '2',
chainId: Number(transaction.chainId),
},
- message: Eip712.getSignInput(transaction),
+ message: EIP712.getSignInput(transaction),
};
}
/**
@@ -105,10 +104,10 @@ export class Eip712 {
static txHash(transaction: Eip712TxData, ethSignature?: EthereumSignature): string {
const bytes: string[] = [];
- const typedDataStruct = Eip712.txTypedData(transaction);
+ const typedDataStruct = EIP712.txTypedData(transaction);
bytes.push(web3Abi.getEncodedEip712Data(typedDataStruct, true));
- bytes.push(web3Utils.keccak256(Eip712.getSignature(typedDataStruct.message, ethSignature)));
+ bytes.push(web3Utils.keccak256(EIP712.getSignature(typedDataStruct.message, ethSignature)));
return web3Utils.keccak256(concat(bytes));
}
/**
@@ -193,7 +192,7 @@ export class Eip712 {
transaction.signature = new SignatureObject(ethSignature).toString();
}
- transaction.hash = Eip712.txHash(transaction, ethSignature);
+ transaction.hash = EIP712.txHash(transaction, ethSignature);
return transaction;
}
@@ -295,24 +294,35 @@ export class Eip712 {
static sign(hash: string, PRIVATE_KEY: string) {
return web3Acccounts.sign(hash, PRIVATE_KEY, true);
}
- static computePublicKey(key: Bytes, compressed?: boolean): string {
- let bytes = toBytes(key);
+}
- // private key
- if (bytes.length === 32) {
- const pubKey = ethereumCryptography.secp256k1.getPublicKey(bytes, !!compressed);
- return toHex(pubKey);
- }
+export class EIP712Signer {
+ private eip712Domain: Eip712TypedData['domain'];
+ private web3Account: web3Acccounts.Web3Account;
+ private chainId: number;
+ constructor(web3Account: web3Acccounts.Web3Account, chainId: number) {
+ this.web3Account = web3Account;
+ this.chainId = Number(web3Utils.toNumber(chainId));
+ this.eip712Domain = {
+ name: 'zkSync',
+ version: '2',
+ chainId: Number(this.chainId),
+ };
+ }
- // raw public key; use uncompressed key with 0x04 prefix
- if (bytes.length === 64) {
- const pub = new Uint8Array(65);
- pub[0] = 0x04;
- pub.set(bytes, 1);
- bytes = pub;
- }
+ sign(tx: Eip712TxData): web3Acccounts.SignResult {
+ const typedDataStruct = EIP712.txTypedData({
+ chainId: this.chainId,
+ ...tx,
+ });
+ const message = web3Abi.getEncodedEip712Data(typedDataStruct);
+ return EIP712.sign(message, this.web3Account.privateKey);
+ }
- const point = ethereumCryptography.secp256k1.ProjectivePoint.fromHex(bytes);
- return toHex(point.toRawBytes(compressed));
+ /**
+ * Returns zkSync Era EIP712 domain.
+ */
+ getDomain(): Eip712TypedData['domain'] {
+ return this.eip712Domain;
}
}
diff --git a/test/unit/utils.test.ts b/test/unit/utils.test.ts
index 361ee57..be1c0c3 100644
--- a/test/unit/utils.test.ts
+++ b/test/unit/utils.test.ts
@@ -1,10 +1,10 @@
import { Web3 } from 'web3';
import * as web3Accounts from 'web3-eth-accounts';
-import * as web3Abi from 'web3-eth-abi';
import type { types } from '../../src';
import { utils } from '../../src';
import { ADDRESS1, ADDRESS2 } from '../utils';
import * as constants from '../../src/constants';
+import { EIP712Signer } from '../../src/Eip712';
describe('utils', () => {
describe('#getHashedL2ToL1Msg()', () => {
@@ -116,7 +116,7 @@ describe('utils', () => {
describe('#serializeEip712()', () => {
it('should throw an error when `tx.chainId` is not specified', () => {
try {
- utils.Eip712.serialize({});
+ utils.EIP712.serialize({});
} catch (e) {
expect((e as Error).message).toBe("Transaction chainId isn't set!");
}
@@ -124,7 +124,7 @@ describe('utils', () => {
it('should throw an error when `tx.from` is not specified', () => {
try {
- utils.Eip712.serialize({ chainId: 270 });
+ utils.EIP712.serialize({ chainId: 270 });
} catch (e) {
expect((e as Error).message).toBe(
'Explicitly providing `from` field is required for EIP712 transactions!',
@@ -134,7 +134,7 @@ describe('utils', () => {
it('should throw an error when `tx.customData.customSignature` is empty string', () => {
try {
- utils.Eip712.serialize({
+ utils.EIP712.serialize({
chainId: 270,
from: ADDRESS1,
customData: {
@@ -149,7 +149,7 @@ describe('utils', () => {
it('should return a serialized transaction with populated default values', () => {
const tx =
'0x71ea8080808080808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
- const result = utils.Eip712.serialize({
+ const result = utils.EIP712.serialize({
chainId: 270,
from: ADDRESS1,
});
@@ -159,7 +159,7 @@ describe('utils', () => {
it('should return a serialized transaction with provided signature', async () => {
const tx =
'0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
- const result = utils.Eip712.serialize(
+ const result = utils.EIP712.serialize(
{
chainId: 270,
from: ADDRESS1,
@@ -242,7 +242,7 @@ describe('utils', () => {
const serializedTx =
'0x71f87f8080808094a61464658afeaf65cccaafd3a512b69a83b77618830f42408001a073a20167b8d23b610b058c05368174495adf7da3a4ed4a57eb6dbdeb1fafc24aa02f87530d663a0d061f69bb564d2c6fb46ae5ae776bbd4bd2a2a4478b9cd1b42a82010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
- const result = utils.Eip712.fromSerializedTx(serializedTx);
+ const result = utils.EIP712.fromSerializedTx(serializedTx);
expect(result).toEqual(tx);
});
@@ -270,7 +270,7 @@ describe('utils', () => {
const serializedTx =
'0x71f83e8080808094a61464658afeaf65cccaafd3a512b69a83b77618808082010e808082010e9436615cf349d7f6344891b1e7ca7c72883f5dc04982c350c080c0';
- const result = utils.Eip712.fromSerializedTx(serializedTx);
+ const result = utils.EIP712.fromSerializedTx(serializedTx);
expect(result).toEqual(tx);
});
});
@@ -279,7 +279,7 @@ describe('utils', () => {
const account = web3Accounts.create();
const ADDRESS = account.address;
const message = 'Hello, world!';
- const signature = utils.Eip712.sign(message, account.privateKey).signature;
+ const signature = utils.EIP712.sign(message, account.privateKey).signature;
const web3 = new Web3();
const isValidSignature = await utils.isMessageSignatureCorrect(
web3,
@@ -303,16 +303,12 @@ describe('utils', () => {
to: '0xa61464658AfeAf65CccaaFD3a512b69A83B77618',
value: 7_000_000n,
};
-
- const typedDataStruct = utils.Eip712.txTypedData(tx);
-
- const message = web3Abi.getEncodedEip712Data(typedDataStruct);
-
+ const eip712Signer = new EIP712Signer(
+ web3Accounts.privateKeyToAccount(PRIVATE_KEY),
+ 270,
+ );
+ const signature = eip712Signer.sign(tx);
const web3 = new Web3('http://localhost:8545');
- const acc = web3.eth.accounts.privateKeyToAccount(PRIVATE_KEY);
- web3.eth.accounts.wallet.add(acc);
- // const signature = web3.eth.accounts.wallet.sign(message, true);
- const signature = utils.Eip712.sign(message, PRIVATE_KEY);
expect(signature.signature).toBe(
'0x5ea12f3d54a1624d7e7f5161dbf6ab746c3335e643b2966264e740cf8e10e9b64b0251fb79d9a5b11730387085a0d58f105926f72e20242ecb274639991939ca1b',
@@ -320,9 +316,9 @@ describe('utils', () => {
const isValidSignature = await utils.isTypedDataSignatureCorrect(
web3,
ADDRESS,
- typedDataStruct.domain,
+ eip712Signer.getDomain(),
constants.EIP712_TYPES,
- utils.Eip712.getSignInput(tx),
+ utils.EIP712.getSignInput(tx),
signature.signature,
);
expect(isValidSignature).toBe(true);
From 03252b3d958766e3c28615bea1c55c543748de48 Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Thu, 13 Jun 2024 13:33:11 -0400
Subject: [PATCH 21/30] remove unnecessary things
---
package.json | 3 +--
src/utils.ts | 17 +-------------
test/integration/utils.test.ts | 41 ++++++++++++++++++++++++++++++++++
test/unit/utils.test.ts | 35 -----------------------------
4 files changed, 43 insertions(+), 53 deletions(-)
create mode 100644 test/integration/utils.test.ts
diff --git a/package.json b/package.json
index eeaff5a..212268d 100644
--- a/package.json
+++ b/package.json
@@ -30,8 +30,7 @@
"web3-eth-accounts": "^4.1.2",
"web3-eth-contract": "^4.5.0",
"web3-types": "^1.6.0",
- "web3-utils": "^4.3.0",
- "web3-validator": "^2.0.6"
+ "web3-utils": "^4.3.0"
},
"devDependencies": {
"@chainsafe/eslint-config": "^2.1.1",
diff --git a/src/utils.ts b/src/utils.ts
index 6dd5ac6..a5641aa 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -149,22 +149,7 @@ function recoverSignerAddress(
return web3Accounts.recover(message, v, r, s, undefined, true);
}
-function _getBytes(value: Bytes): Uint8Array {
- if (value instanceof Uint8Array) {
- return value;
- }
- if (typeof value === 'string' && value.match(/^0x([0-9a-f][0-9a-f])*$/i)) {
- const result = new Uint8Array((value.length - 2) / 2);
- let offset = 2;
- for (let i = 0; i < result.length; i++) {
- result[i] = parseInt(value.substring(offset, offset + 2), 16);
- offset += 2;
- }
- return result;
- }
- throw new Error('Invalid BytesLike value');
-}
export class SignatureObject {
public r: Uint8Array;
public s: Uint8Array;
@@ -178,7 +163,7 @@ export class SignatureObject {
v?: web3Types.Numbers,
) {
if (typeof rOrSignature === 'string') {
- const bytes: Uint8Array = _getBytes(rOrSignature);
+ const bytes: Uint8Array = web3Utils.hexToBytes(rOrSignature);
if (bytes.length === 64) {
const r = bytes.slice(0, 32);
diff --git a/test/integration/utils.test.ts b/test/integration/utils.test.ts
new file mode 100644
index 0000000..3f6c9e4
--- /dev/null
+++ b/test/integration/utils.test.ts
@@ -0,0 +1,41 @@
+import { Web3 } from 'web3';
+import * as web3Accounts from 'web3-eth-accounts';
+import { utils } from '../../src';
+import * as constants from '../../src/constants';
+import { EIP712Signer } from '../../src/Eip712';
+
+describe('utils', () => {
+ describe('#isTypedDataSignatureCorrect()', () => {
+ it('should return true if correct', async () => {
+ const PRIVATE_KEY =
+ '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110';
+ const ADDRESS = '0x36615Cf349d7F6344891B1e7CA7C72883F5dc049';
+ const tx = {
+ type: 113,
+ chainId: 270,
+ from: ADDRESS,
+ to: '0xa61464658AfeAf65CccaaFD3a512b69A83B77618',
+ value: 7_000_000n,
+ };
+ const eip712Signer = new EIP712Signer(
+ web3Accounts.privateKeyToAccount(PRIVATE_KEY),
+ 270,
+ );
+ const signature = eip712Signer.sign(tx);
+ const web3 = new Web3('http://localhost:8545');
+
+ expect(signature.signature).toBe(
+ '0x5ea12f3d54a1624d7e7f5161dbf6ab746c3335e643b2966264e740cf8e10e9b64b0251fb79d9a5b11730387085a0d58f105926f72e20242ecb274639991939ca1b',
+ );
+ const isValidSignature = await utils.isTypedDataSignatureCorrect(
+ web3,
+ ADDRESS,
+ eip712Signer.getDomain(),
+ constants.EIP712_TYPES,
+ utils.EIP712.getSignInput(tx),
+ signature.signature,
+ );
+ expect(isValidSignature).toBe(true);
+ });
+ });
+});
diff --git a/test/unit/utils.test.ts b/test/unit/utils.test.ts
index be1c0c3..058afb3 100644
--- a/test/unit/utils.test.ts
+++ b/test/unit/utils.test.ts
@@ -4,7 +4,6 @@ import type { types } from '../../src';
import { utils } from '../../src';
import { ADDRESS1, ADDRESS2 } from '../utils';
import * as constants from '../../src/constants';
-import { EIP712Signer } from '../../src/Eip712';
describe('utils', () => {
describe('#getHashedL2ToL1Msg()', () => {
@@ -290,38 +289,4 @@ describe('utils', () => {
expect(isValidSignature).toBe(true);
});
});
-
- describe('#isTypedDataSignatureCorrect()', () => {
- it('should return true if correct', async () => {
- const PRIVATE_KEY =
- '0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110';
- const ADDRESS = '0x36615Cf349d7F6344891B1e7CA7C72883F5dc049';
- const tx = {
- type: 113,
- chainId: 270,
- from: ADDRESS,
- to: '0xa61464658AfeAf65CccaaFD3a512b69A83B77618',
- value: 7_000_000n,
- };
- const eip712Signer = new EIP712Signer(
- web3Accounts.privateKeyToAccount(PRIVATE_KEY),
- 270,
- );
- const signature = eip712Signer.sign(tx);
- const web3 = new Web3('http://localhost:8545');
-
- expect(signature.signature).toBe(
- '0x5ea12f3d54a1624d7e7f5161dbf6ab746c3335e643b2966264e740cf8e10e9b64b0251fb79d9a5b11730387085a0d58f105926f72e20242ecb274639991939ca1b',
- );
- const isValidSignature = await utils.isTypedDataSignatureCorrect(
- web3,
- ADDRESS,
- eip712Signer.getDomain(),
- constants.EIP712_TYPES,
- utils.EIP712.getSignInput(tx),
- signature.signature,
- );
- expect(isValidSignature).toBe(true);
- });
- });
});
From 7a2e1ea8fbda8e61e4620c6e746e7cf4ec224877 Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Thu, 13 Jun 2024 14:30:05 -0400
Subject: [PATCH 22/30] fix
---
src/Eip712.ts | 4 ----
src/types.ts | 14 --------------
2 files changed, 18 deletions(-)
diff --git a/src/Eip712.ts b/src/Eip712.ts
index 77d5624..f27402d 100644
--- a/src/Eip712.ts
+++ b/src/Eip712.ts
@@ -252,13 +252,9 @@ export class EIP712 {
if (signature) {
const signatureObject = new SignatureObject(signature);
-
fields.push(toHex(Number(signatureObject.v) === 27 ? 0 : 1));
fields.push(toHex(signatureObject.r));
fields.push(toHex(signatureObject.s));
-
- // const sig = new SignatureObject(signature);
- // fields.push(toBytes(sig.yParity));
} else {
fields.push(toHex(transaction.chainId));
fields.push('0x');
diff --git a/src/types.ts b/src/types.ts
index 3bd3c7b..cf5721c 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -13,20 +13,6 @@ import type {
TransactionReceipt,
} from 'web3-types';
-// import type {
-// FeeMarketEIP1559Transaction,
-// TxOptions
-// } from 'web3-eth-accounts';
-
-// import {
-// EIP712_TX_TYPE,
-// parseEip712,
-// serializeEip712,
-// sleep,
-// eip712TxHash,
-// isAddressEq,
-// } from './utils';
-
import type { RpcMethods } from './rpc.methods';
export type { Bytes, HexString, Numbers } from 'web3-types';
From 7fcf12d75cc8eccfbf4b47cd68e757384af79ded Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Fri, 14 Jun 2024 13:58:46 -0400
Subject: [PATCH 23/30] use baseTransaction for signing
---
src/Eip712.ts | 126 +++++++++++++++++++++++++++++----
src/plugin.ts | 7 +-
src/utils.ts | 18 ++---
test/integration/utils.test.ts | 14 ++--
test/unit/utils.test.ts | 2 +-
5 files changed, 134 insertions(+), 33 deletions(-)
diff --git a/src/Eip712.ts b/src/Eip712.ts
index f27402d..100673c 100644
--- a/src/Eip712.ts
+++ b/src/Eip712.ts
@@ -1,8 +1,10 @@
import { bytesToHex, toBigInt, toHex } from 'web3-utils';
-import type { Bytes, Eip712TypedData } from 'web3-types';
+import type { Bytes, Eip712TypedData, Numbers } from 'web3-types';
import * as web3Abi from 'web3-eth-abi';
import * as web3Utils from 'web3-utils';
-import * as web3Acccounts from 'web3-eth-accounts';
+import type * as web3Acccounts from 'web3-eth-accounts';
+import type { JsonTx, TxOptions, TxValuesArray } from 'web3-eth-accounts';
+import { BaseTransaction, toUint8Array } from 'web3-eth-accounts';
import { RLP } from '@ethereumjs/rlp';
import type { Address } from 'web3';
import {
@@ -219,8 +221,7 @@ export class EIP712 {
return new Uint8Array([...r, ...s, v]);
}
-
- static serialize(transaction: Eip712TxData, signature?: SignatureLike): string {
+ static raw(transaction: Eip712TxData, signature?: SignatureLike) {
if (!transaction.chainId) {
throw Error("Transaction chainId isn't set!");
}
@@ -283,12 +284,18 @@ export class EIP712 {
} else {
fields.push([]);
}
-
+ return fields;
+ }
+ static serialize(transaction: Eip712TxData, signature?: SignatureLike): string {
+ const fields = EIP712.raw(transaction, signature);
return concat([new Uint8Array([EIP712_TX_TYPE]), RLP.encode(fields)]);
}
- static sign(hash: string, PRIVATE_KEY: string) {
- return web3Acccounts.sign(hash, PRIVATE_KEY, true);
+ static sign(hash: string, privateKey: string) {
+ return new EIP712Transaction({}).ecsign(
+ toUint8Array(web3Utils.keccak256(hash)),
+ toUint8Array(privateKey),
+ );
}
}
@@ -306,13 +313,8 @@ export class EIP712Signer {
};
}
- sign(tx: Eip712TxData): web3Acccounts.SignResult {
- const typedDataStruct = EIP712.txTypedData({
- chainId: this.chainId,
- ...tx,
- });
- const message = web3Abi.getEncodedEip712Data(typedDataStruct);
- return EIP712.sign(message, this.web3Account.privateKey);
+ sign(tx: Eip712TxData): SignatureObject | undefined {
+ return new EIP712Transaction(tx).sign(toBytes(this.web3Account.privateKey)).getSignature();
}
/**
@@ -322,3 +324,99 @@ export class EIP712Signer {
return this.eip712Domain;
}
}
+export class EIP712Transaction extends BaseTransaction {
+ private txData: Eip712TxData;
+ private signature?: SignatureObject;
+ constructor(txData: Eip712TxData) {
+ super(txData, {} as 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()})`;
+ }
+
+ 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));
+ }
+ // @ts-ignore-next-line
+ raw(): TxValuesArray[] {
+ return EIP712.raw(this.txData) as unknown as TxValuesArray[];
+ }
+
+ serialize(): Uint8Array {
+ return toUint8Array(EIP712.serialize(this.txData));
+ }
+
+ toJSON(): JsonTx {
+ const data = EIP712.getSignInput(this.txData);
+ return {
+ to: data.to,
+ gasLimit: toHex(data.gasLimit),
+ // @ts-ignore-next-line
+ gasPerPubdataByteLimit: data.gasPerPubdataByteLimit,
+ customData: data.customData,
+ maxFeePerGas: data.maxFeePerGas,
+ maxPriorityFeePerGas: 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,
+ };
+ }
+}
diff --git a/src/plugin.ts b/src/plugin.ts
index 36d67c1..9041550 100644
--- a/src/plugin.ts
+++ b/src/plugin.ts
@@ -3,9 +3,10 @@ import type { Address } from 'web3-types';
import { Contract } from 'web3-eth-contract';
import { Web3PluginBase } from 'web3-core';
+import { TransactionFactory } from '../../web3.js/packages/web3-eth-accounts';
import { IERC20ABI } from './contracts/IERC20';
import { RpcMethods } from './rpc.methods';
-import { ETH_ADDRESS, ZERO_ADDRESS } from './constants';
+import { EIP712_TX_TYPE, ETH_ADDRESS, ZERO_ADDRESS } from './constants';
import { IL2BridgeABI } from './contracts/IL2Bridge';
import { IZkSyncABI } from './contracts/IZkSyncStateTransition';
import { IBridgehubABI } from './contracts/IBridgehub';
@@ -14,6 +15,7 @@ import { IL1MessengerABI } from './contracts/IL1Messenger';
import { IERC1271ABI } from './contracts/IERC1271';
import { IL1BridgeABI } from './contracts/IL1ERC20Bridge';
import { INonceHolderABI } from './contracts/INonceHolder';
+import { EIP712Transaction } from './Eip712';
export class ZkSyncPlugin extends Web3PluginBase {
public pluginNamespace = 'zkSync';
@@ -65,7 +67,8 @@ export class ZkSyncPlugin extends Web3PluginBase {
constructor() {
super();
-
+ // @ts-ignore-next-line
+ TransactionFactory.registerTransactionType(EIP712_TX_TYPE, EIP712Transaction);
this.erc20BridgeL1 = '';
this.erc20BridgeL2 = '';
this.wethBridgeL1 = '';
diff --git a/src/utils.ts b/src/utils.ts
index a5641aa..67af0d0 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -12,6 +12,7 @@ import * as web3Types from 'web3-types';
import * as web3Abi from 'web3-eth-abi';
import * as web3Contract from 'web3-eth-contract';
import type { Bytes } from 'web3-types';
+import { toUint8Array } from 'web3-eth-accounts';
import type { DeploymentInfo, EthereumSignature } from './types';
import {
// PaymasterParams,
@@ -139,15 +140,16 @@ function recoverSignerAddress(
message = messageOrData;
}
- if (typeof signature === 'string') {
- return web3Accounts.recover(message, signature, undefined, undefined, undefined, true);
- }
-
- const r = web3Utils.toHex(signature.r);
- const s = web3Utils.toHex(signature.s);
- const v = web3Utils.toHex(signature.v);
+ const signatureObject =
+ typeof signature === 'string'
+ ? new SignatureObject(signature)
+ : new SignatureObject(
+ toUint8Array(signature.r),
+ toUint8Array(signature.s),
+ signature.v,
+ );
- return web3Accounts.recover(message, v, r, s, undefined, true);
+ return web3Accounts.recover(web3Utils.keccak256(message), signatureObject.serialized, true);
}
export class SignatureObject {
diff --git a/test/integration/utils.test.ts b/test/integration/utils.test.ts
index 3f6c9e4..1c18066 100644
--- a/test/integration/utils.test.ts
+++ b/test/integration/utils.test.ts
@@ -1,8 +1,7 @@
-import { Web3 } from 'web3';
import * as web3Accounts from 'web3-eth-accounts';
-import { utils } from '../../src';
+import { Web3 } from '../../../web3.js/packages/web3';
import * as constants from '../../src/constants';
-import { EIP712Signer } from '../../src/Eip712';
+import * as utils from '../../src/utils';
describe('utils', () => {
describe('#isTypedDataSignatureCorrect()', () => {
@@ -17,14 +16,13 @@ describe('utils', () => {
to: '0xa61464658AfeAf65CccaaFD3a512b69A83B77618',
value: 7_000_000n,
};
- const eip712Signer = new EIP712Signer(
+ const eip712Signer = new utils.EIP712Signer(
web3Accounts.privateKeyToAccount(PRIVATE_KEY),
270,
);
- const signature = eip712Signer.sign(tx);
+ const signature = eip712Signer.sign(tx) as utils.SignatureObject;
const web3 = new Web3('http://localhost:8545');
-
- expect(signature.signature).toBe(
+ expect(signature.serialized).toBe(
'0x5ea12f3d54a1624d7e7f5161dbf6ab746c3335e643b2966264e740cf8e10e9b64b0251fb79d9a5b11730387085a0d58f105926f72e20242ecb274639991939ca1b',
);
const isValidSignature = await utils.isTypedDataSignatureCorrect(
@@ -33,7 +31,7 @@ describe('utils', () => {
eip712Signer.getDomain(),
constants.EIP712_TYPES,
utils.EIP712.getSignInput(tx),
- signature.signature,
+ signature.serialized,
);
expect(isValidSignature).toBe(true);
});
diff --git a/test/unit/utils.test.ts b/test/unit/utils.test.ts
index 058afb3..7a12da7 100644
--- a/test/unit/utils.test.ts
+++ b/test/unit/utils.test.ts
@@ -278,7 +278,7 @@ describe('utils', () => {
const account = web3Accounts.create();
const ADDRESS = account.address;
const message = 'Hello, world!';
- const signature = utils.EIP712.sign(message, account.privateKey).signature;
+ const signature = utils.EIP712.sign(message, account.privateKey).serialized;
const web3 = new Web3();
const isValidSignature = await utils.isMessageSignatureCorrect(
web3,
From 90d3a05dd14f1596a2f91862bd7e0298c36aafe6 Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Mon, 17 Jun 2024 21:45:10 -0400
Subject: [PATCH 24/30] latest version web3js
---
package.json | 6 +-
src/Eip712.ts | 40 ++++++-----
src/types.ts | 16 +++++
yarn.lock | 185 ++++++++++++--------------------------------------
4 files changed, 86 insertions(+), 161 deletions(-)
diff --git a/package.json b/package.json
index 212268d..ef67ea6 100644
--- a/package.json
+++ b/package.json
@@ -25,11 +25,11 @@
"dependencies": {
"ethereum-cryptography": "^2.1.3",
"hardhat": "^2.19.4",
- "web3-core": "^4.4.0",
+ "web3-core": "^4.5.0",
"web3-eth-abi": "^4.2.2",
"web3-eth-accounts": "^4.1.2",
"web3-eth-contract": "^4.5.0",
- "web3-types": "^1.6.0",
+ "web3-types": "^1.7.0",
"web3-utils": "^4.3.0"
},
"devDependencies": {
@@ -42,7 +42,7 @@
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
- "web3": "^4.9.0"
+ "web3": "^4.10.0"
},
"peerDependencies": {
"web3": ">= 4.0.3"
diff --git a/src/Eip712.ts b/src/Eip712.ts
index 100673c..8fa13ba 100644
--- a/src/Eip712.ts
+++ b/src/Eip712.ts
@@ -2,8 +2,7 @@ import { bytesToHex, toBigInt, toHex } from 'web3-utils';
import type { Bytes, Eip712TypedData, Numbers } from 'web3-types';
import * as web3Abi from 'web3-eth-abi';
import * as web3Utils from 'web3-utils';
-import type * as web3Acccounts from 'web3-eth-accounts';
-import type { JsonTx, TxOptions, TxValuesArray } from 'web3-eth-accounts';
+import type * as web3Accounts from 'web3-eth-accounts';
import { BaseTransaction, toUint8Array } from 'web3-eth-accounts';
import { RLP } from '@ethereumjs/rlp';
import type { Address } from 'web3';
@@ -13,7 +12,13 @@ import {
EIP712_TYPES,
ZERO_ADDRESS,
} from './constants';
-import type { Eip712Meta, Eip712TxData, EthereumSignature, PaymasterParams } from './types';
+import type {
+ Eip712Meta,
+ Eip712SignedInput,
+ Eip712TxData,
+ EthereumSignature,
+ PaymasterParams,
+} from './types';
import type { SignatureLike } from './utils';
import { concat, hashBytecode, SignatureObject, toBytes } from './utils';
@@ -56,7 +61,7 @@ function arrayToPaymasterParams(arr: Uint8Array): PaymasterParams | undefined {
}
export class EIP712 {
- static getSignInput(transaction: Eip712TxData) {
+ static getSignInput(transaction: Eip712TxData): Eip712SignedInput {
const maxFeePerGas = toHex(transaction.maxFeePerGas || transaction.gasPrice || 0n);
const maxPriorityFeePerGas = toHex(transaction.maxPriorityFeePerGas || maxFeePerGas);
const gasPerPubdataByteLimit = toHex(
@@ -66,15 +71,15 @@ export class EIP712 {
txType: transaction.type || EIP712_TX_TYPE,
from: transaction.from ? toHex(transaction.from) : undefined,
to: transaction.to ? toHex(transaction.to) : undefined,
- gasLimit: transaction.gasLimit || 0,
+ gasLimit: transaction.gasLimit ? toBigInt(transaction.gasLimit) : 0,
gasPerPubdataByteLimit: gasPerPubdataByteLimit,
customData: transaction.customData,
maxFeePerGas,
maxPriorityFeePerGas,
paymaster: transaction.customData?.paymasterParams?.paymaster || ZERO_ADDRESS,
- nonce: transaction.nonce || 0,
- value: transaction.value || toHex(0),
- data: transaction.data || '0x',
+ nonce: transaction.nonce ? toBigInt(transaction.nonce) : 0,
+ value: transaction.value ? toHex(transaction.value) : '0x0',
+ data: transaction.data ? toHex(transaction.data) : '0x',
factoryDeps:
transaction.customData?.factoryDeps?.map((dep: Bytes) => hashBytecode(dep)) || [],
paymasterInput: transaction.customData?.paymasterParams?.paymasterInput || '0x',
@@ -301,9 +306,9 @@ export class EIP712 {
export class EIP712Signer {
private eip712Domain: Eip712TypedData['domain'];
- private web3Account: web3Acccounts.Web3Account;
+ private web3Account: web3Accounts.Web3Account;
private chainId: number;
- constructor(web3Account: web3Acccounts.Web3Account, chainId: number) {
+ constructor(web3Account: web3Accounts.Web3Account, chainId: number) {
this.web3Account = web3Account;
this.chainId = Number(web3Utils.toNumber(chainId));
this.eip712Domain = {
@@ -328,7 +333,7 @@ export class EIP712Transaction extends BaseTransaction {
private txData: Eip712TxData;
private signature?: SignatureObject;
constructor(txData: Eip712TxData) {
- super(txData, {} as TxOptions);
+ super(txData, {} as web3Accounts.TxOptions);
const { v, r, s, ...data } = txData;
if (r && s) {
@@ -359,6 +364,7 @@ export class EIP712Transaction extends BaseTransaction {
});
}
public ecsign(msgHash: Uint8Array, privateKey: Uint8Array, chainId?: bigint) {
+ // @ts-ignore-next-time until new web3js release
const { s, r, v } = this._ecsign(msgHash, privateKey, chainId);
this.signature = new SignatureObject(toUint8Array(r), toUint8Array(s), toBigInt(v));
return this.signature;
@@ -389,24 +395,24 @@ export class EIP712Transaction extends BaseTransaction {
return toUint8Array(EIP712.txHash(this.txData));
}
// @ts-ignore-next-line
- raw(): TxValuesArray[] {
- return EIP712.raw(this.txData) as unknown as TxValuesArray[];
+ raw(): web3Accounts.TxValuesArray[] {
+ return EIP712.raw(this.txData) as unknown as web3Accounts.TxValuesArray[];
}
serialize(): Uint8Array {
return toUint8Array(EIP712.serialize(this.txData));
}
- toJSON(): JsonTx {
+ toJSON(): web3Accounts.JsonTx {
const data = EIP712.getSignInput(this.txData);
return {
- to: data.to,
+ to: data.to && toHex(data.to),
gasLimit: toHex(data.gasLimit),
// @ts-ignore-next-line
gasPerPubdataByteLimit: data.gasPerPubdataByteLimit,
customData: data.customData,
- maxFeePerGas: data.maxFeePerGas,
- maxPriorityFeePerGas: data.maxPriorityFeePerGas,
+ maxFeePerGas: toHex(data.maxFeePerGas),
+ maxPriorityFeePerGas: toHex(data.maxPriorityFeePerGas),
paymaster: data.paymaster,
nonce: toHex(data.nonce),
value: toHex(data.value),
diff --git a/src/types.ts b/src/types.ts
index cf5721c..243c60a 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -867,3 +867,19 @@ export type Eip712TxData = FeeMarketEIP1559TxData & {
hash?: string;
signature?: string;
};
+export type Eip712SignedInput = FeeMarketEIP1559TxData & {
+ customData?: null | Eip712Meta;
+ data: Bytes;
+ value: Bytes;
+ nonce: Numbers;
+ gasLimit: Numbers;
+ maxFeePerGas: Numbers;
+ maxPriorityFeePerGas: Numbers;
+ from?: Address;
+ txType: Numbers;
+ gasPerPubdataByteLimit?: Numbers;
+ paymaster: Address;
+ factoryDeps: Bytes[];
+ paymasterInput: Bytes;
+ [key: string]: unknown;
+};
diff --git a/yarn.lock b/yarn.lock
index f0b5754..0e64382 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5733,62 +5733,29 @@ walker@^1.0.8:
dependencies:
makeerror "1.0.12"
-web3-core@^4.3.0:
- version "4.3.1"
- resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.3.1.tgz#5c3b5b59f1e31537a64237caa5fd83f5ffd7b0f3"
- integrity sha512-xa3w5n/ESxp5HIbrwsYBhpAPx2KI5LprjRFEtRwP0GpqqhTcCSMMYoyItRqQQ+k9YnB0PoFpWJfJI6Qn5x8YUQ==
- dependencies:
- web3-errors "^1.1.4"
- web3-eth-iban "^4.0.7"
- web3-providers-http "^4.1.0"
- web3-providers-ws "^4.0.7"
- web3-types "^1.3.1"
- web3-utils "^4.0.7"
- web3-validator "^2.0.3"
- optionalDependencies:
- web3-providers-ipc "^4.0.7"
-
-web3-core@^4.4.0:
- version "4.4.0"
- resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.4.0.tgz#f2cd48aecda5ec34170edf7470001f90fdea1dc6"
- integrity sha512-sN1AkhTAFI89anOeCaO0c3GtiGeWtOGVc2tmTdQs2Rd14HuxLyDuLIF3/WwjtkDFRM2189uYy8HJJSWJvW2mYA==
+web3-core@^4.3.0, web3-core@^4.4.0, web3-core@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.5.0.tgz#f16e7f5bfa6373c7be45f0ed233aff479fd33079"
+ integrity sha512-Q8LIAqmF7vkRydBPiU+OC7wI44nEU6JEExolFaOakqrjMtQ1CWFHRUQMNJRDsk5bRirjyShuAsuqLeYByvvXhg==
dependencies:
web3-errors "^1.2.0"
web3-eth-accounts "^4.1.2"
web3-eth-iban "^4.0.7"
web3-providers-http "^4.1.0"
web3-providers-ws "^4.0.7"
- web3-types "^1.6.0"
+ web3-types "^1.7.0"
web3-utils "^4.3.0"
web3-validator "^2.0.6"
optionalDependencies:
web3-providers-ipc "^4.0.7"
-web3-errors@^1.1.3, web3-errors@^1.1.4:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/web3-errors/-/web3-errors-1.1.4.tgz#5667a0a5f66fc936e101ef32032ccc1e8ca4d5a1"
- integrity sha512-WahtszSqILez+83AxGecVroyZsMuuRT+KmQp4Si5P4Rnqbczno1k748PCrZTS1J4UCPmXMG2/Vt+0Bz2zwXkwQ==
- dependencies:
- web3-types "^1.3.1"
-
-web3-errors@^1.2.0:
+web3-errors@^1.1.3, web3-errors@^1.1.4, web3-errors@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/web3-errors/-/web3-errors-1.2.0.tgz#441acfd7fd744c9beedf23f277f20759fae92433"
integrity sha512-58Kczou5zyjcm9LuSs5Hrm6VrG8t9p2J8X0yGArZrhKNPZL66gMGkOUpPx+EopE944Sk4yE+Q25hKv4H5BH+kA==
dependencies:
web3-types "^1.6.0"
-web3-eth-abi@^4.1.4:
- version "4.1.4"
- resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.1.4.tgz#56ae7ebb1385db1a948e69fb35f4057bff6743af"
- integrity sha512-YLOBVVxxxLYKXjaiwZjEWYEnkMmmrm0nswZsvzSsINy/UgbWbzfoiZU+zn4YNWIEhORhx1p37iS3u/dP6VyC2w==
- dependencies:
- abitype "0.7.1"
- web3-errors "^1.1.3"
- web3-types "^1.3.0"
- web3-utils "^4.0.7"
- web3-validator "^2.0.3"
-
web3-eth-abi@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.2.2.tgz#d7592e2cc113fd34da3fb4c933537ddf8639d9b2"
@@ -5800,19 +5767,6 @@ web3-eth-abi@^4.2.2:
web3-utils "^4.3.0"
web3-validator "^2.0.6"
-web3-eth-accounts@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-4.1.0.tgz#5b5e6c60d457e7b829ec590021fc87ada8585920"
- integrity sha512-UFtAsOANsvihTQ6SSvOKguupmQkResyR9M9JNuOxYpKh7+3W+sTnbLXw2UbOSYIsKlc1mpqqW9bVr1SjqHDpUQ==
- dependencies:
- "@ethereumjs/rlp" "^4.0.1"
- crc-32 "^1.2.2"
- ethereum-cryptography "^2.0.0"
- web3-errors "^1.1.3"
- web3-types "^1.3.0"
- web3-utils "^4.0.7"
- web3-validator "^2.0.3"
-
web3-eth-accounts@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-4.1.2.tgz#652d6e3daf4d6cb3fe67cec6a878e768f6e8b8e8"
@@ -5839,18 +5793,18 @@ web3-eth-contract@^4.5.0:
web3-utils "^4.3.0"
web3-validator "^2.0.6"
-web3-eth-ens@^4.3.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-4.3.0.tgz#f44f279a4a07eae36e3de8384989ed069201a795"
- integrity sha512-QpiKT9GqJouH5kEI/pRFprh88YPCtbht2Ym6rrklZ+VoWl9D+wLfbwvW7Aox349FS7k0UX2qVins5tVNLJ5GCQ==
+web3-eth-ens@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-4.4.0.tgz#bc0d11d755cb15ed4b82e38747c5104622d9a4b9"
+ integrity sha512-DeyVIS060hNV9g8dnTx92syqvgbvPricE3MerCxe/DquNZT3tD8aVgFfq65GATtpCgDDJffO2bVeHp3XBemnSQ==
dependencies:
"@adraffy/ens-normalize" "^1.8.8"
- web3-core "^4.4.0"
+ web3-core "^4.5.0"
web3-errors "^1.2.0"
- web3-eth "^4.7.0"
+ web3-eth "^4.8.0"
web3-eth-contract "^4.5.0"
web3-net "^4.1.0"
- web3-types "^1.6.0"
+ web3-types "^1.7.0"
web3-utils "^4.3.0"
web3-validator "^2.0.6"
@@ -5876,50 +5830,23 @@ web3-eth-personal@^4.0.8:
web3-utils "^4.0.7"
web3-validator "^2.0.3"
-web3-eth@^4.3.1:
- version "4.3.1"
- resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.3.1.tgz#cb4224356716da71e694091aa3fbf10bcb813fb5"
- integrity sha512-zJir3GOXooHQT85JB8SrufE+Voo5TtXdjhf1D8IGXmxM8MrhI8AT+Pgt4siBTupJcu5hF17iGmTP/Nj2XnaibQ==
- dependencies:
- setimmediate "^1.0.5"
- web3-core "^4.3.0"
- web3-errors "^1.1.3"
- web3-eth-abi "^4.1.4"
- web3-eth-accounts "^4.1.0"
- web3-net "^4.0.7"
- web3-providers-ws "^4.0.7"
- web3-rpc-methods "^1.1.3"
- web3-types "^1.3.0"
- web3-utils "^4.0.7"
- web3-validator "^2.0.3"
-
-web3-eth@^4.7.0:
- version "4.7.0"
- resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.7.0.tgz#7d18815c7a79c200552bd0df8d3127f7532b3cc2"
- integrity sha512-gqlWq4Xjz+yKL2MdxQ+BgR3F4CRo4AXWDXzftb3LDzvauEfjk/yRyoxkMSK4S9RIG96ylRImS172cV6cYzcukw==
+web3-eth@^4.3.1, web3-eth@^4.7.0, web3-eth@^4.8.0:
+ version "4.8.0"
+ resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.8.0.tgz#1db0b573d933b69032038e814df730136f4837ba"
+ integrity sha512-fobkdpwN9SH785/0LSLfxOMH4rZNAD/EvTKIHdpl4ZVz5XdKehX+xPMpSGDGwMlAQ7yXByjZDX3opzoqEQLWxg==
dependencies:
setimmediate "^1.0.5"
- web3-core "^4.4.0"
+ web3-core "^4.5.0"
web3-errors "^1.2.0"
web3-eth-abi "^4.2.2"
web3-eth-accounts "^4.1.2"
web3-net "^4.1.0"
web3-providers-ws "^4.0.7"
web3-rpc-methods "^1.3.0"
- web3-types "^1.6.0"
+ web3-types "^1.7.0"
web3-utils "^4.3.0"
web3-validator "^2.0.6"
-web3-net@^4.0.7:
- version "4.0.7"
- resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-4.0.7.tgz#ed2c1bd700cf94be93a6dbd8bd8aa413d8681942"
- integrity sha512-SzEaXFrBjY25iQGk5myaOfO9ZyfTwQEa4l4Ps4HDNVMibgZji3WPzpjq8zomVHMwi8bRp6VV7YS71eEsX7zLow==
- dependencies:
- web3-core "^4.3.0"
- web3-rpc-methods "^1.1.3"
- web3-types "^1.3.0"
- web3-utils "^4.0.7"
-
web3-net@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-4.1.0.tgz#db7bde675e58b153339e4f149f29ec0410d6bab2"
@@ -5961,16 +5888,7 @@ web3-providers-ws@^4.0.7:
web3-utils "^4.0.7"
ws "^8.8.1"
-web3-rpc-methods@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/web3-rpc-methods/-/web3-rpc-methods-1.1.3.tgz#4be8a85628d8b69846e2e0afa0ed71e3f6eaf163"
- integrity sha512-XB6SsCZZPdZUMPIRqDxJkZFKMu0/Y+yaExAt+Z7RqmuM7xF55fJ/Qb84LQho8zarvUoYziy4jnIfs+SXImxQUw==
- dependencies:
- web3-core "^4.3.0"
- web3-types "^1.3.0"
- web3-validator "^2.0.3"
-
-web3-rpc-methods@^1.3.0:
+web3-rpc-methods@^1.1.3, web3-rpc-methods@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/web3-rpc-methods/-/web3-rpc-methods-1.3.0.tgz#d5ee299a69389d63822d354ddee2c6a121a6f670"
integrity sha512-/CHmzGN+IYgdBOme7PdqzF+FNeMleefzqs0LVOduncSaqsppeOEoskLXb2anSpzmQAP3xZJPaTrkQPWSJMORig==
@@ -5979,27 +5897,22 @@ web3-rpc-methods@^1.3.0:
web3-types "^1.6.0"
web3-validator "^2.0.6"
-web3-types@^1.3.0, web3-types@^1.3.1:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/web3-types/-/web3-types-1.3.1.tgz#cf6148ad46b68c5c89714613380b270d31e297be"
- integrity sha512-8fXi7h/t95VKRtgU4sxprLPZpsTh3jYDfSghshIDBgUD/OoGe5S+syP24SUzBZYllZ/L+hMr2gdp/0bGJa8pYQ==
-
-web3-types@^1.6.0:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/web3-types/-/web3-types-1.6.0.tgz#ebe7f140c31f7cc0ad15f238ad7e7ac72797ff3b"
- integrity sha512-qgOtADqlD5hw+KPKBUGaXAcdNLL0oh6qTeVgXwewCfbL/lG9R+/GrgMQB1gbTJ3cit8hMwtH8KX2Em6OwO0HRw==
-
-web3-utils@^4.0.7:
- version "4.0.7"
- resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-4.0.7.tgz#7df497b7cdd06cdfe7d02036c45fecbe3382d137"
- integrity sha512-sy8S6C2FIa5NNHc8DjND+Fx3S8KDAizuh5RbL1RX3h0PRbFgPfWzF5RfUns8gTt0mjJuOhs/IaDhrZfeTszG5A==
+web3-rpc-providers@^1.0.0-rc.0:
+ version "1.0.0-rc.0"
+ resolved "https://registry.yarnpkg.com/web3-rpc-providers/-/web3-rpc-providers-1.0.0-rc.0.tgz#fa5f4aac5e15707b50d6b72a10c087a58ea2e281"
+ integrity sha512-lmBOZ4PE+wf5JyptPZJ+GHeGPyTBfnCRbrfOxWLU+Q7g+D6NukgS3fk2xcunEvUsR/b5fp+uXk0TkmhX9/GCKg==
dependencies:
- ethereum-cryptography "^2.0.0"
- web3-errors "^1.1.3"
- web3-types "^1.3.0"
- web3-validator "^2.0.3"
+ web3-providers-http "^4.1.0"
+ web3-providers-ws "^4.0.7"
+ web3-types "^1.7.0"
+ web3-utils "^4.3.0"
-web3-utils@^4.2.3, web3-utils@^4.3.0:
+web3-types@^1.3.0, web3-types@^1.6.0, web3-types@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/web3-types/-/web3-types-1.7.0.tgz#9945fa644af96b20b1db18564aff9ab8db00df59"
+ integrity sha512-nhXxDJ7a5FesRw9UG5SZdP/C/3Q2EzHGnB39hkAV+YGXDMgwxBXFWebQLfEzZzuArfHnvC0sQqkIHNwSKcVjdA==
+
+web3-utils@^4.0.7, web3-utils@^4.2.3, web3-utils@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-4.3.0.tgz#c18918f0d692f745d622d22172406f6102528860"
integrity sha512-fGG2IZr0XB1vEoWZiyJzoy28HpsIfZgz4mgPeQA9aj5rIx8z0o80qUPtIyrCYX/Bo2gYALlV5SWIJWxJNUQn9Q==
@@ -6010,18 +5923,7 @@ web3-utils@^4.2.3, web3-utils@^4.3.0:
web3-types "^1.6.0"
web3-validator "^2.0.6"
-web3-validator@^2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/web3-validator/-/web3-validator-2.0.3.tgz#e5dcd4b4902612cff21b7f8817dd233393999d97"
- integrity sha512-fJbAQh+9LSNWy+l5Ze6HABreml8fra98o5+vS073T35jUcLbRZ0IOjF/ZPJhJNbJDt+jP1vseZsc3z3uX9mxxQ==
- dependencies:
- ethereum-cryptography "^2.0.0"
- util "^0.12.5"
- web3-errors "^1.1.3"
- web3-types "^1.3.0"
- zod "^3.21.4"
-
-web3-validator@^2.0.5, web3-validator@^2.0.6:
+web3-validator@^2.0.3, web3-validator@^2.0.5, web3-validator@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/web3-validator/-/web3-validator-2.0.6.tgz#a0cdaa39e1d1708ece5fae155b034e29d6a19248"
integrity sha512-qn9id0/l1bWmvH4XfnG/JtGKKwut2Vokl6YXP5Kfg424npysmtRLe9DgiNBM9Op7QL/aSiaA0TVXibuIuWcizg==
@@ -6032,25 +5934,26 @@ web3-validator@^2.0.5, web3-validator@^2.0.6:
web3-types "^1.6.0"
zod "^3.21.4"
-web3@^4.9.0:
- version "4.9.0"
- resolved "https://registry.yarnpkg.com/web3/-/web3-4.9.0.tgz#2d18f6a48f366601aef32308260542ed14bd2b1b"
- integrity sha512-O0R90ijjyqUlG1Wk3SXqfYMU1ZGJvLCAF/WfSg/isDz/0Fkpqxoj893wauZ+ieRvTXITlbQHVXGfpp8qrhWZ1g==
+web3@^4.10.0:
+ version "4.10.0"
+ resolved "https://registry.yarnpkg.com/web3/-/web3-4.10.0.tgz#b20a9b0c510efacfc6f776630a0d1b4e87f41398"
+ integrity sha512-0A0SEZG4QL5DRtZQtF1pL+LldHn0kAAb4/vUdQua1k4CrZ+hRoDAc3dfFZw94EOV69oXuAFo8fqhITxyWfCHaw==
dependencies:
- web3-core "^4.4.0"
+ web3-core "^4.5.0"
web3-errors "^1.2.0"
- web3-eth "^4.7.0"
+ web3-eth "^4.8.0"
web3-eth-abi "^4.2.2"
web3-eth-accounts "^4.1.2"
web3-eth-contract "^4.5.0"
- web3-eth-ens "^4.3.0"
+ web3-eth-ens "^4.4.0"
web3-eth-iban "^4.0.7"
web3-eth-personal "^4.0.8"
web3-net "^4.1.0"
web3-providers-http "^4.1.0"
web3-providers-ws "^4.0.7"
web3-rpc-methods "^1.3.0"
- web3-types "^1.6.0"
+ web3-rpc-providers "^1.0.0-rc.0"
+ web3-types "^1.7.0"
web3-utils "^4.3.0"
web3-validator "^2.0.6"
From 7d5bd657113a385dc5fcb8751ff86bc5639e8cb0 Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Mon, 17 Jun 2024 21:51:39 -0400
Subject: [PATCH 25/30] test
---
test/integration/rpc.test.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/test/integration/rpc.test.ts b/test/integration/rpc.test.ts
index 38685fa..d370053 100644
--- a/test/integration/rpc.test.ts
+++ b/test/integration/rpc.test.ts
@@ -7,6 +7,7 @@ import {
getTransactionDetailsData,
} from '../fixtures';
+
describe('ZkSyncPlugin rpc tests', () => {
let web3: Web3;
From 23288405e092ed0a8314c050fbbfa728855a1d0a Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Mon, 17 Jun 2024 21:53:12 -0400
Subject: [PATCH 26/30] fix tests
---
test/integration/utils.test.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/integration/utils.test.ts b/test/integration/utils.test.ts
index 1c18066..c00fa01 100644
--- a/test/integration/utils.test.ts
+++ b/test/integration/utils.test.ts
@@ -1,5 +1,5 @@
import * as web3Accounts from 'web3-eth-accounts';
-import { Web3 } from '../../../web3.js/packages/web3';
+import { Web3 } from 'web3';
import * as constants from '../../src/constants';
import * as utils from '../../src/utils';
From 167f04ef2704f9dcf630ca845b34b46145579c52 Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Mon, 17 Jun 2024 21:55:19 -0400
Subject: [PATCH 27/30] test
---
.github/workflows/build.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 31ca21f..5df3950 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -7,7 +7,7 @@ jobs:
test:
strategy:
matrix:
- node: [18, 20]
+ node: [20]
name: Install and test
runs-on: ubuntu-latest
steps:
@@ -15,6 +15,6 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- cache: yarn
+# cache: yarn
- run: yarn install
- run: yarn test
From c5c4098a50c7d3b0e32d4dec31778189bb63d906 Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Mon, 17 Jun 2024 21:57:03 -0400
Subject: [PATCH 28/30] fix tests
---
.github/workflows/build.yml | 4 ++--
src/plugin.ts | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 5df3950..31ca21f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -7,7 +7,7 @@ jobs:
test:
strategy:
matrix:
- node: [20]
+ node: [18, 20]
name: Install and test
runs-on: ubuntu-latest
steps:
@@ -15,6 +15,6 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
-# cache: yarn
+ cache: yarn
- run: yarn install
- run: yarn test
diff --git a/src/plugin.ts b/src/plugin.ts
index 9041550..774ad1e 100644
--- a/src/plugin.ts
+++ b/src/plugin.ts
@@ -3,7 +3,7 @@ import type { Address } from 'web3-types';
import { Contract } from 'web3-eth-contract';
import { Web3PluginBase } from 'web3-core';
-import { TransactionFactory } from '../../web3.js/packages/web3-eth-accounts';
+import { TransactionFactory } from 'web3-eth-accounts';
import { IERC20ABI } from './contracts/IERC20';
import { RpcMethods } from './rpc.methods';
import { EIP712_TX_TYPE, ETH_ADDRESS, ZERO_ADDRESS } from './constants';
From 51e36d25d0ab6bbe505a871181080169eb8a722d Mon Sep 17 00:00:00 2001
From: Oleksii Kosynskyi
Date: Mon, 17 Jun 2024 21:58:32 -0400
Subject: [PATCH 29/30] fix to test network
---
test/integration/utils.test.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/integration/utils.test.ts b/test/integration/utils.test.ts
index c00fa01..a42dfb7 100644
--- a/test/integration/utils.test.ts
+++ b/test/integration/utils.test.ts
@@ -21,7 +21,7 @@ describe('utils', () => {
270,
);
const signature = eip712Signer.sign(tx) as utils.SignatureObject;
- const web3 = new Web3('http://localhost:8545');
+ const web3 = new Web3('https://sepolia.era.zksync.dev');
expect(signature.serialized).toBe(
'0x5ea12f3d54a1624d7e7f5161dbf6ab746c3335e643b2966264e740cf8e10e9b64b0251fb79d9a5b11730387085a0d58f105926f72e20242ecb274639991939ca1b',
);
From 12db0dadf3d86111a8a620bdd5480d3e9323d3a3 Mon Sep 17 00:00:00 2001
From: Muhammad Altabba <24407834+Muhammad-Altabba@users.noreply.github.com>
Date: Wed, 10 Jul 2024 02:24:33 +0200
Subject: [PATCH 30/30] [DRAFT] MS2 (#11)
* add the draft for `Web3ZkSync`
* add a very early draft for `ZKSyncWallet`
* apply lint:fx
* add very early draft for AdapterL1 and AdapterL2
* fix
* Adapters, zk l2, zk l1
* add 1 unit test for Web3ZkSyncL2
* fix build
* fix a test
* added test file and a method with a TODO
* Fix tests. implement Wallet
* tiny fix and apply lint:fix
* deposit + withdraw
* add wallet methods
* few code fixes to wallet2.test.ts and apply lint:fix
* fix build
* apply lint:fix
* populate transaction, sign, send refactor
* fix withdraw and deposit
* fix tests
* apply lint:fix
* add gasLimit
* fixes
* fixes and tests
* update fixtures
* fix wallet. tests
* feat: enhance plugin context (#20)
* enhance plugin context
* enhance plugin contracts init
* enhance plugin contracts init and add a test
* ensure the correct provider used inside the plugin
* revert a draft change
* tiny to fix provider usage at plugin
* link to a web3.js issue
* pre deploy state
* fix tests
* fix
* add env
* test
* secret to env
* test
* test
* use envs
* test
* test
* test
* test var
* test
* test
* fix
* move web3 to dependencies
* fix web3 version compatibility
* apply yarn lint:fix
* fix version
* v1.0.0-alpha.0
* ignore file
---------
Co-authored-by: Oleksii Kosynskyi
---
.github/workflows/build.yml | 5 +-
.npmignore | 1 +
package.json | 96 +-
src/Eip712.ts | 70 +-
src/adapters.ts | 1932 +++++++++++++++++++
src/contracts/IERC20.ts | 241 ++-
src/index.ts | 3 +
src/paymaster-utils.ts | 81 +
src/plugin.ts | 317 ++-
src/rpc.methods.ts | 179 +-
src/schemas.ts | 43 +
src/types.ts | 56 +-
src/utils.ts | 240 ++-
src/web3zksync-l1.ts | 6 +
src/web3zksync-l2.ts | 397 ++++
src/web3zksync.ts | 630 ++++++
src/zksync-wallet.ts | 219 +++
test/fixtures.ts | 3 +
test/integration/_wallet.test.ts | 52 +
test/integration/mainnet.test.ts | 4 +-
test/integration/rpc.mainnet.test.ts | 14 +-
test/integration/rpc.test.ts | 18 +-
test/integration/wallet.test.ts | 1777 +++++++++++++++++
test/integration/zksync.contracts.test.ts | 40 +
test/jest.config.js | 4 +-
test/unit/index.test.ts | 4 +-
test/unit/signer.test.ts | 75 +
test/unit/utils.test.ts | 137 +-
test/unit/web3zksync-l2.as.provider.test.ts | 59 +
test/utils.ts | 23 +-
yarn.lock | 382 ++--
31 files changed, 6465 insertions(+), 643 deletions(-)
create mode 100644 src/adapters.ts
create mode 100644 src/paymaster-utils.ts
create mode 100644 src/web3zksync-l1.ts
create mode 100644 src/web3zksync-l2.ts
create mode 100644 src/web3zksync.ts
create mode 100644 src/zksync-wallet.ts
create mode 100644 test/integration/_wallet.test.ts
create mode 100644 test/integration/wallet.test.ts
create mode 100644 test/integration/zksync.contracts.test.ts
create mode 100644 test/unit/signer.test.ts
create mode 100644 test/unit/web3zksync-l2.as.provider.test.ts
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 31ca21f..3bcf60d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -7,7 +7,7 @@ jobs:
test:
strategy:
matrix:
- node: [18, 20]
+ node: [20]
name: Install and test
runs-on: ubuntu-latest
steps:
@@ -17,4 +17,5 @@ jobs:
node-version: ${{ matrix.node }}
cache: yarn
- run: yarn install
- - run: yarn test
+ - run: yarn build
+ - run: export PRIVATE_KEY=${{secrets.PRIVATE_KEY}} && yarn test
diff --git a/.npmignore b/.npmignore
index 44d5996..ea16e65 100644
--- a/.npmignore
+++ b/.npmignore
@@ -9,6 +9,7 @@
.prettierrc.json
.prettierignore
*.log
+.idea
coverage/
benchmark/
diff --git a/package.json b/package.json
index ef67ea6..810f758 100644
--- a/package.json
+++ b/package.json
@@ -1,50 +1,50 @@
{
- "name": "web3-plugin-zksync",
- "version": "0.1.5",
- "description": "web3.js plugin for ZkSync",
- "main": "lib/index.js",
- "types": "lib/index.d.ts",
- "homepage": "https://github.com/web3/web3-plugin-zksync#readme",
- "bugs": {
- "url": "https://github.com/web3/web3-plugin-zksync/issues"
- },
- "scripts": {
- "lint": "eslint '{src,test}/**/*.ts'",
- "lint:fix": "eslint '{src,test}/**/*.ts' --fix",
- "build": "tsc --project tsconfig.build.json",
- "test": "jest --config=./test/jest.config.js"
- },
- "contributors": [
- "ChainSafe "
- ],
- "license": "MIT",
- "repository": {
- "type": "git",
- "url": "git+ssh://git@github.com/web3/web3-plugin-zksync.git"
- },
- "dependencies": {
- "ethereum-cryptography": "^2.1.3",
- "hardhat": "^2.19.4",
- "web3-core": "^4.5.0",
- "web3-eth-abi": "^4.2.2",
- "web3-eth-accounts": "^4.1.2",
- "web3-eth-contract": "^4.5.0",
- "web3-types": "^1.7.0",
- "web3-utils": "^4.3.0"
- },
- "devDependencies": {
- "@chainsafe/eslint-config": "^2.1.1",
- "@types/jest": "^29.5.11",
- "@types/node": "^20.11.10",
- "eslint": "8.56.0",
- "jest": "^29.7.0",
- "jest-extended": "^4.0.2",
- "ts-jest": "^29.1.2",
- "ts-node": "^10.9.2",
- "typescript": "^5.3.3",
- "web3": "^4.10.0"
- },
- "peerDependencies": {
- "web3": ">= 4.0.3"
- }
+ "name": "web3-plugin-zksync",
+ "version": "1.0.0-alpha.0",
+ "description": "web3.js plugin for ZkSync",
+ "main": "lib/index.js",
+ "types": "lib/index.d.ts",
+ "homepage": "https://github.com/web3/web3-plugin-zksync#readme",
+ "bugs": {
+ "url": "https://github.com/web3/web3-plugin-zksync/issues"
+ },
+ "scripts": {
+ "lint": "eslint '{src,test}/**/*.ts'",
+ "lint:fix": "eslint '{src,test}/**/*.ts' --fix",
+ "build": "tsc --project tsconfig.build.json",
+ "test": "jest --config=./test/jest.config.js"
+ },
+ "contributors": [
+ "ChainSafe "
+ ],
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+ssh://git@github.com/web3/web3-plugin-zksync.git"
+ },
+ "dependencies": {
+ "ethereum-cryptography": "^2.1.3",
+ "hardhat": "^2.19.4",
+ "web3": "4.10.1-dev.1436228.0",
+ "web3-core": "4.5.1-dev.1436228.0",
+ "web3-eth-abi": "^4.2.2",
+ "web3-eth-accounts": "^4.1.2",
+ "web3-eth-contract": "4.5.1-dev.1436228.0",
+ "web3-types": "1.7.1-dev.1436228.0",
+ "web3-utils": "4.3.1-dev.1436228.0"
+ },
+ "devDependencies": {
+ "@chainsafe/eslint-config": "^2.1.1",
+ "@types/jest": "^29.5.11",
+ "@types/node": "^20.11.10",
+ "eslint": "8.56.0",
+ "jest": "^29.7.0",
+ "jest-extended": "^4.0.2",
+ "ts-jest": "^29.1.2",
+ "ts-node": "^10.9.2",
+ "typescript": "^5.3.3"
+ },
+ "peerDependencies": {
+ "web3": ">= 4.0.3"
+ }
}
diff --git a/src/Eip712.ts b/src/Eip712.ts
index 8fa13ba..94070a5 100644
--- a/src/Eip712.ts
+++ b/src/Eip712.ts
@@ -3,7 +3,7 @@ import type { Bytes, Eip712TypedData, Numbers } from 'web3-types';
import * as web3Abi from 'web3-eth-abi';
import * as web3Utils from 'web3-utils';
import type * as web3Accounts from 'web3-eth-accounts';
-import { BaseTransaction, toUint8Array } from 'web3-eth-accounts';
+import { BaseTransaction, bigIntToUint8Array, toUint8Array } from 'web3-eth-accounts';
import { RLP } from '@ethereumjs/rlp';
import type { Address } from 'web3';
import {
@@ -62,27 +62,37 @@ function arrayToPaymasterParams(arr: Uint8Array): PaymasterParams | undefined {
export class EIP712 {
static getSignInput(transaction: Eip712TxData): Eip712SignedInput {
- const maxFeePerGas = toHex(transaction.maxFeePerGas || transaction.gasPrice || 0n);
- const maxPriorityFeePerGas = toHex(transaction.maxPriorityFeePerGas || maxFeePerGas);
- const gasPerPubdataByteLimit = toHex(
- transaction.customData?.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT,
- );
+ const maxFeePerGas = toBigInt(transaction.maxFeePerGas || transaction.gasPrice || 0n);
+ const maxPriorityFeePerGas = toBigInt(transaction.maxPriorityFeePerGas || maxFeePerGas);
+ const gasPerPubdataByteLimit =
+ transaction.customData?.gasPerPubdata || DEFAULT_GAS_PER_PUBDATA_LIMIT;
return {
txType: transaction.type || EIP712_TX_TYPE,
- from: transaction.from ? toHex(transaction.from) : undefined,
- to: transaction.to ? toHex(transaction.to) : undefined,
- gasLimit: transaction.gasLimit ? toBigInt(transaction.gasLimit) : 0,
+ from: transaction.from
+ ? typeof transaction.from === 'string'
+ ? transaction.from
+ : toHex(transaction.from)
+ : undefined,
+ to: transaction.to
+ ? typeof transaction.to === 'string'
+ ? transaction.to
+ : toHex(transaction.to)
+ : undefined,
+ gasLimit: transaction.gasLimit ? toBigInt(transaction.gasLimit) : 0n,
gasPerPubdataByteLimit: gasPerPubdataByteLimit,
- customData: transaction.customData,
maxFeePerGas,
maxPriorityFeePerGas,
paymaster: transaction.customData?.paymasterParams?.paymaster || ZERO_ADDRESS,
nonce: transaction.nonce ? toBigInt(transaction.nonce) : 0,
- value: transaction.value ? toHex(transaction.value) : '0x0',
+ value: transaction.value ? toBigInt(transaction.value) : 0n,
data: transaction.data ? toHex(transaction.data) : '0x',
factoryDeps:
transaction.customData?.factoryDeps?.map((dep: Bytes) => hashBytecode(dep)) || [],
paymasterInput: transaction.customData?.paymasterParams?.paymasterInput || '0x',
+ customData:
+ transaction.customData && Object.keys(transaction.customData).length > 0
+ ? transaction.customData
+ : undefined,
};
}
@@ -241,14 +251,17 @@ export class EIP712 {
const maxFeePerGas = toHex(transaction.maxFeePerGas || transaction.gasPrice || 0);
const maxPriorityFeePerGas = toHex(transaction.maxPriorityFeePerGas || maxFeePerGas);
- const nonce = toHex(transaction.nonce || 0);
+ let gasLimitBytes = new Uint8Array();
+ if (transaction.gasLimit && toHex(transaction.gasLimit) !== '0x0') {
+ gasLimitBytes = toBytes(transaction.gasLimit);
+ }
+
+ const nonce = toBigInt(transaction.nonce || 0);
const fields: Array = [
- nonce === '0x0' ? new Uint8Array() : toBytes(nonce),
+ nonce === 0n ? new Uint8Array() : bigIntToUint8Array(nonce),
maxPriorityFeePerGas === '0x0' ? new Uint8Array() : toBytes(maxPriorityFeePerGas),
maxFeePerGas === '0x0' ? new Uint8Array() : toBytes(maxFeePerGas),
- toHex(transaction.gasLimit || 0) === '0x0'
- ? new Uint8Array()
- : toBytes(transaction.gasLimit!),
+ gasLimitBytes,
transaction.to ? web3Utils.toChecksumAddress(toHex(transaction.to)) : '0x',
toHex(transaction.value || 0) === '0x0'
? new Uint8Array()
@@ -322,6 +335,31 @@ export class EIP712Signer {
return new EIP712Transaction(tx).sign(toBytes(this.web3Account.privateKey)).getSignature();
}
+ /**
+ * Hashes the transaction request using EIP712.
+ *
+ * @param transaction The transaction request that needs to be hashed.
+ * @returns A hash (digest) of the transaction request.
+ *
+ * @throws {Error} If `transaction.chainId` is not set.
+ */
+ static getSignedDigest(transaction: Eip712TxData): Bytes {
+ if (!transaction.chainId) {
+ throw Error("Transaction chainId isn't set!");
+ }
+
+ return EIP712.txHash(transaction);
+
+ // const domain = {
+ // name: 'zkSync',
+ // version: '2',
+ // chainId: transaction.chainId,
+ // };
+ // TODO: Implement replacement of the following line
+ // @ts-ignore
+ // return ethers.TypedDataEncoder.hash(domain, EIP712_TYPES, EIP712.getSignInput(transaction));
+ }
+
/**
* Returns zkSync Era EIP712 domain.
*/
diff --git a/src/adapters.ts b/src/adapters.ts
new file mode 100644
index 0000000..2b153a3
--- /dev/null
+++ b/src/adapters.ts
@@ -0,0 +1,1932 @@
+import type * as web3Types from 'web3-types';
+import * as web3Utils from 'web3-utils';
+import * as Web3EthAbi from 'web3-eth-abi';
+import { DEFAULT_RETURN_FORMAT } from 'web3';
+import * as Web3 from 'web3';
+import type { PayableMethodObject, PayableTxOptions } from 'web3-eth-contract';
+import { toBigInt, toHex, toNumber } from 'web3-utils';
+import type { Transaction, TransactionHash, TransactionReceipt } from 'web3-types';
+import type { Web3ZkSyncL2 } from './web3zksync-l2';
+
+import type { EIP712Signer } from './utils';
+import {
+ getPriorityOpResponse,
+ checkBaseCost,
+ estimateCustomBridgeDepositL2Gas,
+ estimateDefaultBridgeDepositL2Gas,
+ getERC20DefaultBridgeData,
+ isETH,
+ layer1TxDefaults,
+ scaleGasLimit,
+ undoL1ToL2Alias,
+ isAddressEq,
+ id,
+ dataSlice,
+ toBytes,
+} from './utils';
+
+import {
+ BOOTLOADER_FORMAL_ADDRESS,
+ L1_MESSENGER_ADDRESS,
+ L1_RECOMMENDED_MIN_ERC20_DEPOSIT_GAS_LIMIT,
+ L1_RECOMMENDED_MIN_ETH_DEPOSIT_GAS_LIMIT,
+ REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT,
+ NONCE_HOLDER_ADDRESS,
+ ETH_ADDRESS_IN_CONTRACTS,
+ LEGACY_ETH_ADDRESS,
+ EIP712_TX_TYPE,
+} from './constants';
+import type {
+ Address,
+ FinalizeWithdrawalParams,
+ FullDepositFee,
+ TransactionOverrides,
+ PaymasterParams,
+ PriorityOpResponse,
+ WalletBalances,
+ Eip712TxData,
+} from './types';
+import { ZeroAddress, ZeroHash } from './types';
+import { IZkSyncABI } from './contracts/IZkSyncStateTransition';
+import { IBridgehubABI } from './contracts/IBridgehub';
+import { IERC20ABI } from './contracts/IERC20';
+import { IL1BridgeABI } from './contracts/IL1Bridge';
+import { Abi as IL1SharedBridgeABI } from './contracts/IL1SharedBridge';
+import { IL2BridgeABI } from './contracts/IL2Bridge';
+import { INonceHolderABI } from './contracts/INonceHolder';
+import type { Web3ZkSyncL1 } from './web3zksync-l1';
+
+interface TxSender {
+ getAddress(): Promise;
+}
+
+export class AdapterL1 implements TxSender {
+ /**
+ * Returns a provider instance for connecting to an L2 network.
+ */
+ protected _contextL2(): Web3ZkSyncL2 {
+ throw new Error('Must be implemented by the derived class!');
+ }
+
+ /**
+ * Returns a context (provider + Signer) instance for connecting to a L1 network.
+ */
+ protected _contextL1(): Web3ZkSyncL1 {
+ throw new Error('Must be implemented by the derived class!');
+ }
+
+ /**
+ * Returns `Contract` wrapper of the zkSync Era smart contract.
+ */
+ async getMainContract(
+ returnFormat: web3Types.DataFormat = DEFAULT_RETURN_FORMAT,
+ ): Promise> {
+ const address = await this._contextL2().getMainContract(returnFormat);
+ const contract = new Web3.Contract(IZkSyncABI, address, returnFormat);
+ contract.setProvider(this._contextL2().provider);
+ return contract;
+ }
+
+ /**
+ * Returns `Contract` wrapper of the Bridgehub smart contract.
+ */
+ async getBridgehubContract(
+ returnFormat: web3Types.DataFormat = DEFAULT_RETURN_FORMAT,
+ ): Promise> {
+ const address = await this._contextL2().getBridgehubContractAddress();
+ return new (this._contextL1().eth.Contract)(IBridgehubABI, address, returnFormat);
+ }
+
+ /**
+ * Returns L1 bridge contracts.
+ *
+ * @remarks There is no separate Ether bridge contract, {@link getBridgehubContractAddress Bridgehub} is used instead.
+ */
+ async getL1BridgeContracts(
+ returnFormat: web3Types.DataFormat = DEFAULT_RETURN_FORMAT,
+ ): Promise<{
+ erc20: Web3.Contract;
+ weth: Web3.Contract;
+ shared: Web3.Contract;
+ }> {
+ const addresses = await this._contextL2().getDefaultBridgeAddresses();
+ const erc20 = new (this._contextL1().eth.Contract)(
+ IERC20ABI,
+ addresses.erc20L1,
+ returnFormat,
+ );
+ const weth = new (this._contextL1().eth.Contract)(
+ IERC20ABI,
+ addresses.wethL1,
+ returnFormat,
+ );
+ const shared = new (this._contextL1().eth.Contract)(
+ IL1SharedBridgeABI,
+ addresses.sharedL1,
+ returnFormat,
+ );
+
+ return {
+ erc20,
+ weth,
+ shared,
+ };
+ }
+
+ /**
+ * Returns the address of the base token on L1.
+ */
+ async getBaseToken(): Promise {
+ const bridgehub = await this.getBridgehubContract();
+ const chainId = await this._contextL2().eth.getChainId();
+ return bridgehub.methods.baseToken(chainId).call();
+ }
+
+ /**
+ * Returns whether the chain is ETH-based.
+ */
+ async isETHBasedChain(): Promise {
+ return this._contextL2().isEthBasedChain();
+ }
+
+ /**
+ * Returns the amount of the token held by the account on the L1 network.
+ *
+ * @param [token] The address of the token. Defaults to ETH if not provided.
+ * @param [blockTag] The block in which the balance should be checked.
+ * Defaults to 'committed', i.e., the latest processed block.
+ */
+ async getBalanceL1(token?: Address, blockTag?: web3Types.BlockNumberOrTag): Promise {
+ token ??= LEGACY_ETH_ADDRESS;
+ if (isETH(token)) {
+ return await this._contextL1().eth.getBalance(this.getAddress(), blockTag);
+ } else {
+ const erc20 = new (this._contextL1().eth.Contract)(IERC20ABI, token);
+
+ return await erc20.methods.balanceOf(this.getAddress()).call();
+ }
+ }
+
+ /**
+ * Returns the amount of approved tokens for a specific L1 bridge.
+ *
+ * @param token The Ethereum address of the token.
+ * @param [bridgeAddress] The address of the bridge contract to be used.
+ * Defaults to the default zkSync Era bridge, either `L1EthBridge` or `L1Erc20Bridge`.
+ * @param [blockTag] The block in which an allowance should be checked.
+ * Defaults to 'committed', i.e., the latest processed block.
+ */
+ async getAllowanceL1(
+ token: Address,
+ bridgeAddress?: Address,
+ blockTag?: web3Types.BlockNumberOrTag,
+ ): Promise {
+ if (!bridgeAddress) {
+ const bridgeContracts = await this.getL1BridgeContracts();
+ bridgeAddress = bridgeContracts.shared.options.address;
+ }
+
+ const erc20 = new (this._contextL1().eth.Contract)(IERC20ABI, token);
+
+ return erc20.methods
+ .allowance(this.getAddress(), bridgeAddress, {
+ blockTag,
+ })
+ .call();
+ }
+
+ /**
+ * Returns the L2 token address equivalent for a L1 token address as they are not necessarily equal.
+ * The ETH address is set to the zero address.
+ *
+ * @remarks Only works for tokens bridged on default zkSync Era bridges.
+ *
+ * @param token The address of the token on L1.
+ */
+ async l2TokenAddress(token: Address): Promise {
+ return this._contextL2().l2TokenAddress(token);
+ }
+
+ /**
+ * Bridging ERC20 tokens from L1 requires approving the tokens to the zkSync Era smart contract.
+ *
+ * @param token The L1 address of the token.
+ * @param amount The amount of the token to be approved.
+ * @param [overrides] Transaction's overrides which may be used to pass L1 `gasLimit`, `gasPrice`, `value`, etc.
+ * @returns A promise that resolves to the response of the approval transaction.
+ * @throws {Error} If attempting to approve an ETH token.
+ */
+ async approveERC20(
+ token: Address,
+ amount: web3Types.Numbers,
+ overrides?: TransactionOverrides & { bridgeAddress?: Address },
+ ) {
+ if (isETH(token)) {
+ throw new Error(
+ "ETH token can't be approved! The address of the token does not exist on L1.",
+ );
+ }
+
+ overrides ??= {};
+ let bridgeAddress = overrides.bridgeAddress;
+
+ const erc20 = new (this._contextL1().eth.Contract)(IERC20ABI, token);
+
+ if (!bridgeAddress) {
+ bridgeAddress = (await this.getL1BridgeContracts()).shared.options.address;
+ } else {
+ delete overrides.bridgeAddress;
+ }
+
+ return erc20.methods.approve(bridgeAddress, amount, overrides).send({
+ from: this.getAddress(),
+ });
+ }
+
+ /**
+ * Returns the base cost for an L2 transaction.
+ *
+ * @param params The parameters for calculating the base cost.
+ * @param params.gasLimit The gasLimit for the L2 contract call.
+ * @param [params.gasPerPubdataByte] The L2 gas price for each published L1 calldata byte.
+ * @param [params.gasPrice] The L1 gas price of the L1 transaction that will send the request for an execute call.
+ */
+ async getBaseCost(params: {
+ gasLimit: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ gasPrice?: web3Types.Numbers;
+ chainId?: web3Types.Numbers;
+ }): Promise {
+ const bridgehub = await this.getBridgehubContract();
+ const parameters = { ...layer1TxDefaults(), ...params };
+ parameters.gasPrice ??= (await this._contextL1().eth.calculateFeeData()).gasPrice!;
+ parameters.gasPerPubdataByte ??= REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT;
+
+ return await bridgehub.methods
+ .l2TransactionBaseCost(
+ parameters.chainId ?? (await this._contextL2().eth.getChainId()),
+ parameters.gasPrice,
+ parameters.gasLimit,
+ parameters.gasPerPubdataByte,
+ )
+ .call();
+ }
+
+ /**
+ * Returns the parameters for the approval token transaction based on the deposit token and amount.
+ * Some deposit transactions require multiple approvals. Existing allowance for the bridge is not checked;
+ * allowance is calculated solely based on the specified amount.
+ *
+ * @param token The address of the token to deposit.
+ * @param amount The amount of the token to deposit.
+ */
+ async getDepositAllowanceParams(
+ token: Address,
+ amount: web3Types.Numbers,
+ ): Promise<{ token: Address; allowance: web3Types.Numbers }[]> {
+ if (isAddressEq(token, LEGACY_ETH_ADDRESS)) {
+ token = ETH_ADDRESS_IN_CONTRACTS;
+ }
+ const baseTokenAddress = await this.getBaseToken();
+ const isETHBasedChain = await this.isETHBasedChain();
+
+ if (isETHBasedChain && isAddressEq(token, ETH_ADDRESS_IN_CONTRACTS)) {
+ throw new Error(
+ "ETH token can't be approved! The address of the token does not exist on L1.",
+ );
+ } else if (isAddressEq(baseTokenAddress, ETH_ADDRESS_IN_CONTRACTS)) {
+ return [{ token, allowance: amount }];
+ } else if (isAddressEq(token, ETH_ADDRESS_IN_CONTRACTS)) {
+ return [
+ {
+ token: baseTokenAddress,
+ allowance: (await this._getDepositETHOnNonETHBasedChainTx({ token, amount }))
+ .mintValue,
+ },
+ ];
+ } else if (isAddressEq(token, baseTokenAddress)) {
+ return [
+ {
+ token: baseTokenAddress,
+ allowance: (
+ await this._getDepositBaseTokenOnNonETHBasedChainTx({
+ token,
+ amount,
+ })
+ ).mintValue,
+ },
+ ];
+ } else {
+ // A deposit of a non-base token to a non-ETH-based chain requires two approvals.
+ return [
+ {
+ token: baseTokenAddress,
+ allowance: (
+ await this._getDepositNonBaseTokenToNonETHBasedChainTx({
+ token,
+ amount,
+ })
+ ).mintValue,
+ },
+ {
+ token: token,
+ allowance: amount,
+ },
+ ];
+ }
+ }
+
+ /**
+ * Transfers the specified token from the associated account on the L1 network to the target account on the L2 network.
+ * The token can be either ETH or any ERC20 token. For ERC20 tokens, enough approved tokens must be associated with
+ * the specified L1 bridge (default one or the one defined in `transaction.bridgeAddress`).
+ * In this case, depending on is the chain ETH-based or not `transaction.approveERC20` or `transaction.approveBaseERC20`
+ * can be enabled to perform token approval. If there are already enough approved tokens for the L1 bridge,
+ * token approval will be skipped. To check the amount of approved tokens for a specific bridge,
+ * use the {@link getAllowanceL1} method.
+ *
+ * @param transaction The transaction object containing deposit details.
+ * @param transaction.token The address of the token to deposit. ETH by default.
+ * @param transaction.amount The amount of the token to deposit.
+ * @param [transaction.to] The address that will receive the deposited tokens on L2.
+ * @param [transaction.operatorTip] (currently not used) If the ETH value passed with the transaction is not
+ * explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of
+ * the base cost of the transaction.
+ * @param [transaction.bridgeAddress] The address of the bridge contract to be used.
+ * Defaults to the default zkSync Era bridge (either `L1EthBridge` or `L1Erc20Bridge`).
+ * @param [transaction.approveERC20] Whether or not token approval should be performed under the hood.
+ * Set this flag to true if you bridge an ERC20 token and didn't call the {@link approveERC20} function beforehand.
+ * @param [transaction.approveBaseERC20] Whether or not base token approval should be performed under the hood.
+ * Set this flag to true if you bridge a base token and didn't call the {@link approveERC20} function beforehand.
+ * @param [transaction.l2GasLimit] Maximum amount of L2 gas that the transaction can consume during execution on L2.
+ * @param [transaction.gasPerPubdataByte] The L2 gas price for each published L1 calldata byte.
+ * @param [transaction.refundRecipient] The address on L2 that will receive the refund for the transaction.
+ * If the transaction fails, it will also be the address to receive `l2Value`.
+ * @param [transaction.overrides] Transaction's overrides for deposit which may be used to pass
+ * L1 `gasLimit`, `gasPrice`, `value`, etc.
+ * @param [transaction.approveOverrides] Transaction's overrides for approval of an ERC20 token which may be used
+ * to pass L1 `gasLimit`, `gasPrice`, `value`, etc.
+ * @param [transaction.approveBaseOverrides] Transaction's overrides for approval of a base token which may be used
+ * to pass L1 `gasLimit`, `gasPrice`, `value`, etc.
+ * @param [transaction.customBridgeData] Additional data that can be sent to a bridge.
+ */
+ async deposit(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ approveERC20?: boolean;
+ approveBaseERC20?: boolean;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ approveOverrides?: TransactionOverrides;
+ approveBaseOverrides?: TransactionOverrides;
+ customBridgeData?: web3Types.Bytes;
+ }): Promise {
+ if (isAddressEq(transaction.token, LEGACY_ETH_ADDRESS)) {
+ transaction.token = ETH_ADDRESS_IN_CONTRACTS;
+ }
+ const baseTokenAddress = await this.getBaseToken();
+
+ const isETHBasedChain = isAddressEq(baseTokenAddress, ETH_ADDRESS_IN_CONTRACTS);
+
+ if (isETHBasedChain && isAddressEq(transaction.token, ETH_ADDRESS_IN_CONTRACTS)) {
+ return await this._depositETHToETHBasedChain(transaction);
+ } else if (isAddressEq(baseTokenAddress, ETH_ADDRESS_IN_CONTRACTS)) {
+ return await this._depositTokenToETHBasedChain(transaction);
+ } else if (isAddressEq(transaction.token, ETH_ADDRESS_IN_CONTRACTS)) {
+ return await this._depositETHToNonETHBasedChain(transaction);
+ } else if (isAddressEq(transaction.token, baseTokenAddress)) {
+ return await this._depositBaseTokenToNonETHBasedChain(transaction);
+ } else {
+ return await this._depositNonBaseTokenToNonETHBasedChain(transaction);
+ }
+ }
+
+ async _depositNonBaseTokenToNonETHBasedChain(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ approveERC20?: boolean;
+ approveBaseERC20?: boolean;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ approveOverrides?: TransactionOverrides;
+ approveBaseOverrides?: TransactionOverrides;
+ customBridgeData?: web3Types.Bytes;
+ }): Promise {
+ // Deposit a non-ETH and non-base token to a non-ETH-based chain.
+ // Go through the BridgeHub and obtain approval for both tokens.
+ const bridgehub = await this.getBridgehubContract();
+ const chainId = await this._contextL2().eth.getChainId();
+ const baseTokenAddress = await bridgehub.methods.baseToken(chainId).call();
+ const bridgeContracts = await this.getL1BridgeContracts();
+ const { tx, mintValue } =
+ await this._getDepositNonBaseTokenToNonETHBasedChainTx(transaction);
+
+ if (transaction.approveBaseERC20) {
+ // Only request the allowance if the current one is not enough.
+ const allowance = await this.getAllowanceL1(
+ baseTokenAddress,
+ bridgeContracts.shared.options.address,
+ );
+ if (allowance < mintValue) {
+ await this.approveERC20(baseTokenAddress, mintValue, {
+ bridgeAddress: bridgeContracts.shared.options.address,
+ ...transaction.approveBaseOverrides,
+ });
+ }
+ }
+
+ if (transaction.approveERC20) {
+ const bridgeAddress = transaction.bridgeAddress
+ ? transaction.bridgeAddress
+ : bridgeContracts.shared.options.address;
+
+ // Only request the allowance if the current one is not enough.
+ const allowance = await this.getAllowanceL1(transaction.token, bridgeAddress);
+ if (allowance < BigInt(transaction.amount)) {
+ await this.approveERC20(transaction.token, transaction.amount, {
+ bridgeAddress,
+ ...transaction.approveOverrides,
+ });
+ }
+ }
+
+ const baseGasLimit = await tx.estimateGas();
+ const gasLimit = scaleGasLimit(baseGasLimit);
+
+ return this.signAndSend(tx.populateTransaction({ gasLimit } as PayableTxOptions));
+ }
+
+ async _depositBaseTokenToNonETHBasedChain(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ approveERC20?: boolean;
+ approveBaseERC20?: boolean;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ approveOverrides?: TransactionOverrides;
+ approveBaseOverrides?: TransactionOverrides;
+ customBridgeData?: web3Types.Bytes;
+ }): Promise {
+ // Bridging the base token to a non-ETH-based chain.
+ // Go through the BridgeHub, and give approval.
+ const bridgehub = await this.getBridgehubContract();
+ const chainId = await this._contextL2().eth.getChainId();
+ const baseTokenAddress = await bridgehub.methods.baseToken(chainId).call();
+ const sharedBridge = (await this.getL1BridgeContracts()).shared.options.address;
+ const { tx, mintValue } = await this._getDepositBaseTokenOnNonETHBasedChainTx(transaction);
+
+ if (transaction.approveERC20 || transaction.approveBaseERC20) {
+ const approveOverrides =
+ transaction.approveBaseOverrides ?? transaction.approveOverrides!;
+ // Only request the allowance if the current one is not enough.
+ const allowance = await this.getAllowanceL1(baseTokenAddress, sharedBridge);
+ if (allowance < mintValue) {
+ await this.approveERC20(baseTokenAddress, mintValue, {
+ bridgeAddress: sharedBridge,
+ ...approveOverrides,
+ });
+ }
+ }
+ const baseGasLimit = await this.estimateGasRequestExecute(tx);
+ const gasLimit = scaleGasLimit(baseGasLimit);
+
+ tx.overrides ??= {};
+ tx.overrides.gasLimit ??= gasLimit;
+
+ return this.requestExecute(tx);
+ }
+
+ async _depositETHToNonETHBasedChain(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ approveERC20?: boolean;
+ approveBaseERC20?: boolean;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ approveOverrides?: TransactionOverrides;
+ approveBaseOverrides?: TransactionOverrides;
+ customBridgeData?: web3Types.Bytes;
+ }): Promise {
+ // Depositing ETH into a non-ETH-based chain.
+ // Use requestL2TransactionTwoBridges, secondBridge is the wETH bridge.
+ const bridgehub = await this.getBridgehubContract();
+ const chainId = await this._contextL2().eth.getChainId();
+ const baseTokenAddress = await bridgehub.methods.baseToken(chainId).call();
+ const sharedBridge = (await this.getL1BridgeContracts()).shared.options.address;
+ const { tx, overrides, mintValue } =
+ await this._getDepositETHOnNonETHBasedChainTx(transaction);
+
+ if (transaction.approveBaseERC20) {
+ // Only request the allowance if the current one is not enough.
+ const allowance = await this.getAllowanceL1(baseTokenAddress, sharedBridge);
+ if (allowance < mintValue) {
+ await this.approveERC20(baseTokenAddress, mintValue, {
+ bridgeAddress: sharedBridge,
+ ...transaction.approveBaseOverrides,
+ });
+ }
+ }
+
+ const baseGasLimit = await tx.estimateGas({
+ value: overrides.value ? web3Utils.toHex(overrides.value) : undefined,
+ });
+ const gasLimit = scaleGasLimit(baseGasLimit);
+
+ overrides.gasLimit ??= gasLimit;
+
+ return this.signAndSend(tx.populateTransaction(overrides as PayableTxOptions));
+ }
+
+ async _depositTokenToETHBasedChain(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ approveERC20?: boolean;
+ approveBaseERC20?: boolean;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ approveOverrides?: TransactionOverrides;
+ approveBaseOverrides?: TransactionOverrides;
+ customBridgeData?: web3Types.Bytes;
+ }): Promise {
+ const bridgeContracts = await this.getL1BridgeContracts();
+ const { tx, overrides } = await this._getDepositTokenOnETHBasedChainTx(transaction);
+
+ if (transaction.approveERC20) {
+ const proposedBridge = bridgeContracts.shared.options.address;
+ const bridgeAddress = transaction.bridgeAddress
+ ? transaction.bridgeAddress
+ : proposedBridge;
+
+ // Only request the allowance if the current one is not enough.
+ const allowance = await this.getAllowanceL1(transaction.token, bridgeAddress);
+ if (allowance < BigInt(transaction.amount)) {
+ await this.approveERC20(transaction.token, transaction.amount, {
+ bridgeAddress,
+ ...transaction.approveOverrides,
+ });
+ }
+ }
+
+ const baseGasLimit = await tx.estimateGas(overrides as PayableTxOptions);
+ const gasLimit = scaleGasLimit(baseGasLimit);
+
+ overrides.gasLimit ??= gasLimit;
+
+ return this.signAndSend(tx.populateTransaction(overrides as PayableTxOptions));
+ }
+
+ async _depositETHToETHBasedChain(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ approveERC20?: boolean;
+ approveBaseERC20?: boolean;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ approveOverrides?: TransactionOverrides;
+ approveBaseOverrides?: TransactionOverrides;
+ customBridgeData?: web3Types.Bytes;
+ }): Promise {
+ const tx = await this._getDepositETHOnETHBasedChainTx(transaction);
+ const baseGasLimit = await this.estimateGasRequestExecute(tx);
+ const gasLimit = scaleGasLimit(baseGasLimit);
+
+ tx.overrides ??= {};
+ tx.overrides.gasLimit ??= gasLimit;
+
+ return this.requestExecute(tx);
+ }
+
+ /**
+ * Estimates the amount of gas required for a deposit transaction on the L1 network.
+ * Gas for approving ERC20 tokens is not included in the estimation.
+ *
+ * In order for estimation to work, enough token allowance is required in the following cases:
+ * - Depositing ERC20 tokens on an ETH-based chain.
+ * - Depositing any token (including ETH) on a non-ETH-based chain.
+ *
+ * @param transaction The transaction details.
+ * @param transaction.token The address of the token to deposit. ETH by default.
+ * @param transaction.amount The amount of the token to deposit.
+ * @param [transaction.to] The address that will receive the deposited tokens on L2.
+ * @param [transaction.operatorTip] (currently not used) If the ETH value passed with the transaction is not
+ * explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the
+ * base cost of the transaction.
+ * @param [transaction.bridgeAddress] The address of the bridge contract to be used.
+ * Defaults to the default zkSync Era bridge (either `L1EthBridge` or `L1Erc20Bridge`).
+ * @param [transaction.l2GasLimit] Maximum amount of L2 gas that the transaction can consume during execution on L2.
+ * @param [transaction.gasPerPubdataByte] The L2 gas price for each published L1 calldata byte.
+ * @param [transaction.customBridgeData] Additional data that can be sent to a bridge.
+ * @param [transaction.refundRecipient] The address on L2 that will receive the refund for the transaction.
+ * If the transaction fails, it will also be the address to receive `l2Value`.
+ * @param [transaction.overrides] Transaction's overrides which may be used to pass L1 `gasLimit`, `gasPrice`, `value`, etc.
+ */
+ async estimateGasDeposit(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ customBridgeData?: web3Types.Bytes;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ }): Promise {
+ if (isAddressEq(transaction.token, LEGACY_ETH_ADDRESS)) {
+ transaction.token = ETH_ADDRESS_IN_CONTRACTS;
+ }
+ const tx = await this.getDepositTx(transaction);
+
+ let baseGasLimit: bigint;
+ if (tx.token && isAddressEq(tx.token, await this.getBaseToken())) {
+ baseGasLimit = await this.estimateGasRequestExecute(tx);
+ } else {
+ baseGasLimit = await this._contextL1().eth.estimateGas(tx);
+ }
+
+ return scaleGasLimit(baseGasLimit);
+ }
+
+ /**
+ * Returns a populated deposit transaction.
+ *
+ * @param transaction The transaction details.
+ * @param transaction.token The address of the token to deposit. ETH by default.
+ * @param transaction.amount The amount of the token to deposit.
+ * @param [transaction.to] The address that will receive the deposited tokens on L2.
+ * @param [transaction.operatorTip] (currently not used) If the ETH value passed with the transaction is not
+ * explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the
+ * base cost of the transaction.
+ * @param [transaction.bridgeAddress] The address of the bridge contract to be used. Defaults to the default zkSync
+ * Era bridge (either `L1EthBridge` or `L1Erc20Bridge`).
+ * @param [transaction.l2GasLimit] Maximum amount of L2 gas that the transaction can consume during execution on L2.
+ * @param [transaction.gasPerPubdataByte] The L2 gas price for each published L1 calldata byte.
+ * @param [transaction.customBridgeData] Additional data that can be sent to a bridge.
+ * @param [transaction.refundRecipient] The address on L2 that will receive the refund for the transaction.
+ * If the transaction fails, it will also be the address to receive `l2Value`.
+ * @param [transaction.overrides] Transaction's overrides which may be used to pass L1 `gasLimit`, `gasPrice`, `value`, etc.
+ */
+ async getDepositTx(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ customBridgeData?: web3Types.Bytes;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ }): Promise {
+ if (isAddressEq(transaction.token, LEGACY_ETH_ADDRESS)) {
+ transaction.token = ETH_ADDRESS_IN_CONTRACTS;
+ }
+ const baseTokenAddress = await this.getBaseToken();
+ const isETHBasedChain = isAddressEq(baseTokenAddress, ETH_ADDRESS_IN_CONTRACTS);
+
+ if (isETHBasedChain && isAddressEq(transaction.token, ETH_ADDRESS_IN_CONTRACTS)) {
+ return await this._getDepositETHOnETHBasedChainTx(transaction);
+ } else if (isETHBasedChain) {
+ return await this._getDepositTokenOnETHBasedChainTx(transaction);
+ } else if (isAddressEq(transaction.token, ETH_ADDRESS_IN_CONTRACTS)) {
+ return (await this._getDepositETHOnNonETHBasedChainTx(transaction)).tx;
+ } else if (isAddressEq(transaction.token, baseTokenAddress)) {
+ return (await this._getDepositBaseTokenOnNonETHBasedChainTx(transaction)).tx;
+ } else {
+ return (await this._getDepositNonBaseTokenToNonETHBasedChainTx(transaction)).tx;
+ }
+ }
+
+ async _getDepositNonBaseTokenToNonETHBasedChainTx(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ customBridgeData?: web3Types.Bytes;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ }) {
+ const bridgehub = await this.getBridgehubContract();
+ const chainId = await this._contextL2().eth.getChainId();
+ const bridgeContracts = await this.getL1BridgeContracts();
+
+ const tx = await this._getDepositTxWithDefaults(transaction);
+ const {
+ token,
+ operatorTip,
+ amount,
+ overrides,
+ l2GasLimit,
+ to,
+ refundRecipient,
+ gasPerPubdataByte,
+ } = tx;
+
+ const baseCost = await this.getBaseCost({
+ gasPrice: overrides.maxFeePerGas || overrides.gasPrice,
+ gasLimit: l2GasLimit,
+ gasPerPubdataByte: gasPerPubdataByte,
+ chainId,
+ });
+
+ const mintValue = web3Utils.toBigInt(baseCost) + web3Utils.toBigInt(operatorTip);
+ await checkBaseCost(baseCost, mintValue);
+ overrides.value ??= 0;
+
+ return {
+ tx: bridgehub.methods.requestL2TransactionTwoBridges({
+ chainId: chainId,
+ mintValue,
+ l2Value: 0,
+ l2GasLimit: l2GasLimit,
+ l2GasPerPubdataByteLimit: gasPerPubdataByte,
+ refundRecipient: refundRecipient ?? ZeroAddress,
+ secondBridgeAddress: bridgeContracts.shared.options.address,
+ secondBridgeValue: 0,
+ secondBridgeCalldata: Web3EthAbi.encodeParameters(
+ ['address', 'uint256', 'address'],
+ [token, amount, to],
+ ),
+ }),
+ overrides,
+ mintValue: mintValue,
+ };
+ }
+
+ async _getDepositBaseTokenOnNonETHBasedChainTx(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ customBridgeData?: web3Types.Bytes;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ }) {
+ // Depositing the base token to a non-ETH-based chain.
+ // Goes through the BridgeHub.
+ // Have to give approvals for the sharedBridge.
+
+ const tx = await this._getDepositTxWithDefaults(transaction);
+ const { operatorTip, amount, to, overrides, l2GasLimit, gasPerPubdataByte } = tx;
+
+ const baseCost = await this.getBaseCost({
+ gasPrice: overrides.maxFeePerGas || overrides.gasPrice,
+ gasLimit: l2GasLimit,
+ gasPerPubdataByte: gasPerPubdataByte,
+ });
+
+ tx.overrides.value = 0;
+ return {
+ tx: {
+ contractAddress: to,
+ calldata: '0x',
+ mintValue: toBigInt(baseCost) + BigInt(operatorTip) + BigInt(amount),
+ l2Value: amount,
+ ...tx,
+ },
+ mintValue: toBigInt(baseCost) + BigInt(operatorTip) + BigInt(amount),
+ };
+ }
+
+ async _getDepositETHOnNonETHBasedChainTx(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ customBridgeData?: web3Types.Bytes;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ }) {
+ const bridgehub = await this.getBridgehubContract();
+ const chainId = await this._contextL2().eth.getChainId();
+ const sharedBridge = (await this.getL1BridgeContracts()).shared.options.address;
+
+ const tx = await this._getDepositTxWithDefaults(transaction);
+ const {
+ operatorTip,
+ amount,
+ overrides,
+ l2GasLimit,
+ to,
+ refundRecipient,
+ gasPerPubdataByte,
+ } = tx;
+
+ const baseCost = await this.getBaseCost({
+ gasPrice: overrides.maxFeePerGas || overrides.gasPrice,
+ gasLimit: l2GasLimit,
+ chainId: chainId,
+ gasPerPubdataByte: gasPerPubdataByte,
+ });
+
+ overrides.value ??= amount;
+ const mintValue = web3Utils.toBigInt(baseCost) + web3Utils.toBigInt(operatorTip);
+ await checkBaseCost(baseCost, mintValue);
+
+ return {
+ tx: bridgehub.methods.requestL2TransactionTwoBridges({
+ chainId,
+ mintValue,
+ l2Value: 0,
+ l2GasLimit: l2GasLimit,
+ l2GasPerPubdataByteLimit: gasPerPubdataByte,
+ refundRecipient: refundRecipient ?? ZeroAddress,
+ secondBridgeAddress: sharedBridge,
+ secondBridgeValue: amount,
+ secondBridgeCalldata: Web3EthAbi.encodeParameters(
+ ['address', 'uint256', 'address'],
+ [ETH_ADDRESS_IN_CONTRACTS, 0, to],
+ ),
+ }),
+ overrides,
+ mintValue: mintValue,
+ };
+ }
+
+ async _getDepositTokenOnETHBasedChainTx(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ customBridgeData?: web3Types.Bytes;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ }): Promise<{
+ tx: PayableMethodObject;
+ overrides: TransactionOverrides;
+ }> {
+ // Depositing token to an ETH-based chain. Use the ERC20 bridge as done before.
+ const bridgehub = await this.getBridgehubContract();
+ const chainId = await this._contextL2().eth.getChainId();
+
+ const tx = await this._getDepositTxWithDefaults(transaction);
+ const {
+ token,
+ operatorTip,
+ amount,
+ overrides,
+ l2GasLimit,
+ to,
+ refundRecipient,
+ gasPerPubdataByte,
+ } = tx;
+
+ const baseCost = await this.getBaseCost({
+ gasPrice: overrides.maxFeePerGas || overrides.gasPrice,
+ gasLimit: l2GasLimit,
+ gasPerPubdataByte,
+ chainId,
+ });
+
+ const mintValue = web3Utils.toBigInt(baseCost) + web3Utils.toBigInt(operatorTip);
+ overrides.value ??= mintValue;
+ await checkBaseCost(baseCost, mintValue);
+
+ let secondBridgeAddress: Address;
+ let secondBridgeCalldata: web3Types.Bytes;
+ if (tx.bridgeAddress) {
+ secondBridgeAddress = tx.bridgeAddress;
+ secondBridgeCalldata = await getERC20DefaultBridgeData(
+ transaction.token,
+ this._contextL1(),
+ );
+ } else {
+ secondBridgeAddress = (await this.getL1BridgeContracts()).shared.options
+ .address as Address;
+ secondBridgeCalldata = Web3EthAbi.encodeParameters(
+ ['address', 'uint256', 'address'],
+ [token, amount, to],
+ );
+ }
+
+ return {
+ tx: bridgehub.methods.requestL2TransactionTwoBridges({
+ chainId,
+ mintValue,
+ l2Value: 0,
+ l2GasLimit,
+ l2GasPerPubdataByteLimit: gasPerPubdataByte,
+ refundRecipient: refundRecipient ?? ZeroAddress,
+ secondBridgeAddress,
+ secondBridgeValue: 0,
+ secondBridgeCalldata,
+ }),
+ overrides,
+ };
+ }
+
+ async _getDepositETHOnETHBasedChainTx(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ customBridgeData?: web3Types.Bytes;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ }) {
+ // Call the BridgeHub directly, like it's done with the DiamondProxy.
+
+ const tx = await this._getDepositTxWithDefaults(transaction);
+ const { operatorTip, amount, overrides, l2GasLimit, gasPerPubdataByte, to } = tx;
+ const baseCost = await this.getBaseCost({
+ gasPrice: overrides.maxFeePerGas || overrides.gasPrice,
+ gasLimit: l2GasLimit,
+ gasPerPubdataByte,
+ });
+
+ overrides.value ??=
+ web3Utils.toBigInt(baseCost) +
+ web3Utils.toBigInt(operatorTip) +
+ web3Utils.toBigInt(amount);
+
+ return {
+ contractAddress: to,
+ calldata: '0x',
+ mintValue: overrides.value,
+ l2Value: amount,
+ ...tx,
+ };
+ }
+
+ // Creates a shallow copy of a transaction and populates missing fields with defaults.
+ async _getDepositTxWithDefaults(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ customBridgeData?: web3Types.Bytes;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ }): Promise<{
+ token: Address;
+ amount: web3Types.Numbers;
+ to: Address;
+ operatorTip: web3Types.Numbers;
+ bridgeAddress?: Address;
+ l2GasLimit: web3Types.Numbers;
+ gasPerPubdataByte: web3Types.Numbers;
+ customBridgeData?: web3Types.Bytes;
+ refundRecipient?: Address;
+ overrides: TransactionOverrides;
+ }> {
+ const { ...tx } = transaction;
+ tx.to = tx.to ?? this.getAddress();
+ tx.operatorTip ??= 0;
+ tx.overrides ??= {};
+ tx.overrides.from = this.getAddress();
+ tx.gasPerPubdataByte ??= REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT;
+ tx.l2GasLimit ??= await this._getL2GasLimit(tx);
+ await insertGasPrice(this._contextL1(), tx.overrides);
+
+ return tx as {
+ token: Address;
+ amount: web3Types.Numbers;
+ to: Address;
+ operatorTip: web3Types.Numbers;
+ bridgeAddress?: Address;
+ l2GasLimit: web3Types.Numbers;
+ gasPerPubdataByte: web3Types.Numbers;
+ customBridgeData?: web3Types.Bytes;
+ refundRecipient?: Address;
+ overrides: TransactionOverrides;
+ };
+ }
+
+ // Default behaviour for calculating l2GasLimit of deposit transaction.
+ async _getL2GasLimit(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ customBridgeData?: web3Types.Bytes;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ }): Promise {
+ if (transaction.bridgeAddress) {
+ return await this._getL2GasLimitFromCustomBridge(transaction);
+ } else {
+ return await estimateDefaultBridgeDepositL2Gas(
+ this._contextL1(),
+ this._contextL2(),
+ transaction.token,
+ transaction.amount,
+ transaction.to!,
+ this.getAddress(),
+ transaction.gasPerPubdataByte,
+ );
+ }
+ }
+
+ // Calculates the l2GasLimit of deposit transaction using custom bridge.
+ async _getL2GasLimitFromCustomBridge(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ operatorTip?: web3Types.Numbers;
+ bridgeAddress?: Address;
+ l2GasLimit?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ customBridgeData?: web3Types.Bytes;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ }): Promise {
+ const customBridgeData =
+ transaction.customBridgeData ??
+ (await getERC20DefaultBridgeData(transaction.token, this._contextL1()));
+
+ const bridge = new (this._contextL1().eth.Contract)(
+ IL1BridgeABI,
+ transaction.bridgeAddress,
+ );
+ const chainId = (await this._contextL2().eth.getChainId()) as web3Types.Numbers;
+ const l2Address = await bridge.methods.l2BridgeAddress(chainId).call();
+ return await estimateCustomBridgeDepositL2Gas(
+ this._contextL2(),
+ transaction.bridgeAddress!,
+ l2Address,
+ transaction.token,
+ transaction.amount,
+ transaction.to!,
+ customBridgeData,
+ this.getAddress(),
+ transaction.gasPerPubdataByte,
+ );
+ }
+
+ /**
+ * Retrieves the full needed ETH fee for the deposit. Returns the L1 fee and the L2 fee {@link FullDepositFee}.
+ *
+ * @param transaction The transaction details.
+ * @param transaction.token The address of the token to deposit. ETH by default.
+ * @param [transaction.to] The address that will receive the deposited tokens on L2.
+ * @param [transaction.bridgeAddress] The address of the bridge contract to be used.
+ * Defaults to the default zkSync Era bridge (either `L1EthBridge` or `L1Erc20Bridge`).
+ * @param [transaction.customBridgeData] Additional data that can be sent to a bridge.
+ * @param [transaction.gasPerPubdataByte] The L2 gas price for each published L1 calldata byte.
+ * @param [transaction.overrides] Transaction's overrides which may be used to pass L1 `gasLimit`, `gasPrice`, `value`, etc.
+ * @throws {Error} If:
+ * - There's not enough balance for the deposit under the provided gas price.
+ * - There's not enough allowance to cover the deposit.
+ */
+ async getFullRequiredDepositFee(transaction: {
+ token: Address;
+ to?: Address;
+ bridgeAddress?: Address;
+ customBridgeData?: web3Types.Bytes;
+ gasPerPubdataByte?: web3Types.Numbers;
+ overrides?: TransactionOverrides;
+ }): Promise {
+ if (isAddressEq(transaction.token, LEGACY_ETH_ADDRESS)) {
+ transaction.token = ETH_ADDRESS_IN_CONTRACTS;
+ }
+ // It is assumed that the L2 fee for the transaction does not depend on its value.
+ const dummyAmount = 1n;
+ const bridgehub = await this.getBridgehubContract();
+
+ const chainId = await this._contextL2().eth.getChainId();
+ const baseTokenAddress = await this.getBaseToken();
+
+ const isETHBasedChain = isAddressEq(baseTokenAddress, ETH_ADDRESS_IN_CONTRACTS);
+
+ const tx = await this._getDepositTxWithDefaults({
+ ...transaction,
+ amount: dummyAmount,
+ });
+
+ const gasPriceForEstimation = tx.overrides.maxFeePerGas || tx.overrides.gasPrice;
+ const baseCost = await bridgehub.methods
+ .l2TransactionBaseCost(
+ chainId as web3Types.Numbers,
+ gasPriceForEstimation as web3Types.Numbers,
+ tx.l2GasLimit,
+ tx.gasPerPubdataByte,
+ )
+ .call();
+
+ if (isETHBasedChain) {
+ // To ensure that L1 gas estimation succeeds when using estimateGasDeposit,
+ // the account needs to have a sufficient ETH balance.
+ const selfBalanceETH = await this.getBalanceL1();
+ if (toBigInt(baseCost) >= toBigInt(selfBalanceETH) + toBigInt(dummyAmount)) {
+ const recommendedL1GasLimit = isAddressEq(tx.token, LEGACY_ETH_ADDRESS)
+ ? L1_RECOMMENDED_MIN_ETH_DEPOSIT_GAS_LIMIT
+ : L1_RECOMMENDED_MIN_ERC20_DEPOSIT_GAS_LIMIT;
+ const recommendedETHBalance =
+ BigInt(recommendedL1GasLimit) * BigInt(gasPriceForEstimation!) +
+ toBigInt(baseCost);
+ const formattedRecommendedBalance = web3Utils.fromWei(
+ recommendedETHBalance,
+ 'ether',
+ );
+ throw new Error(
+ `Not enough balance for deposit! Under the provided gas price, the recommended balance to perform a deposit is ${formattedRecommendedBalance} ETH`,
+ );
+ }
+ // In case of token deposit, a sufficient token allowance is also required.
+ if (
+ !isAddressEq(tx.token, ETH_ADDRESS_IN_CONTRACTS) &&
+ (await this.getAllowanceL1(tx.token, tx.bridgeAddress)) < dummyAmount
+ ) {
+ throw new Error('Not enough allowance to cover the deposit!');
+ }
+ } else {
+ const mintValue = toBigInt(baseCost) + BigInt(tx.operatorTip);
+ if ((await this.getAllowanceL1(baseTokenAddress)) < mintValue) {
+ throw new Error('Not enough base token allowance to cover the deposit!');
+ }
+ if (
+ isAddressEq(tx.token, ETH_ADDRESS_IN_CONTRACTS) ||
+ isAddressEq(tx.token, baseTokenAddress)
+ ) {
+ tx.overrides.value ??= tx.amount;
+ } else {
+ tx.overrides.value ??= 0;
+ if ((await this.getAllowanceL1(tx.token)) < dummyAmount) {
+ throw new Error('Not enough token allowance to cover the deposit!');
+ }
+ }
+ }
+
+ // Deleting the explicit gas limits in the fee estimation
+ // in order to prevent the situation where the transaction
+ // fails because the user does not have enough balance
+ const estimationOverrides = { ...tx.overrides };
+ delete estimationOverrides.gasPrice;
+ delete estimationOverrides.maxFeePerGas;
+ delete estimationOverrides.maxPriorityFeePerGas;
+
+ const l1GasLimit = await this.estimateGasDeposit({
+ ...tx,
+ amount: dummyAmount,
+ overrides: estimationOverrides,
+ l2GasLimit: tx.l2GasLimit,
+ });
+
+ const fullCost: FullDepositFee = {
+ baseCost: toBigInt(baseCost),
+ l1GasLimit,
+ l2GasLimit: BigInt(tx.l2GasLimit),
+ };
+
+ if (tx.overrides.gasPrice) {
+ fullCost.gasPrice = BigInt(tx.overrides.gasPrice);
+ } else {
+ fullCost.maxFeePerGas = BigInt(tx.overrides.maxFeePerGas!);
+ fullCost.maxPriorityFeePerGas = BigInt(tx.overrides.maxPriorityFeePerGas!);
+ }
+
+ return fullCost;
+ }
+
+ /**
+ * Returns the transaction confirmation data that is part of `L2->L1` message.
+ *
+ * @param txHash The hash of the L2 transaction where the message was initiated.
+ * @param [index=0] In case there were multiple transactions in one message, you may pass an index of the
+ * transaction which confirmation data should be fetched.
+ * @throws {Error} If log proof can not be found.
+ */
+ async getPriorityOpConfirmation(txHash: string, index = 0) {
+ return this._contextL2().getPriorityOpConfirmation(txHash, index);
+ }
+
+ async _getWithdrawalLog(withdrawalHash: web3Types.Bytes, index = 0) {
+ const hash = web3Utils.toHex(withdrawalHash);
+ const receipt = await this._contextL2().getZKTransactionReceipt(hash);
+ if (!receipt) {
+ // @todo: or throw?
+ return {};
+ }
+ // @ts-ignore
+ const log = (receipt?.logs || []).filter(
+ // @ts-ignore
+ log =>
+ isAddressEq(String(log?.address), L1_MESSENGER_ADDRESS) &&
+ log?.topics &&
+ String(log?.topics[0]) === id('L1MessageSent(address,bytes32,bytes)'),
+ )[index];
+
+ return {
+ log,
+ l1BatchTxId: receipt.l1BatchTxIndex,
+ };
+ }
+
+ async _getWithdrawalL2ToL1Log(withdrawalHash: web3Types.Bytes, index = 0) {
+ const hash = web3Utils.toHex(withdrawalHash);
+ const receipt = await this._contextL2().getZKTransactionReceipt(hash);
+ if (!receipt) {
+ // @todo: or throw?
+ return {};
+ }
+ const messages = Array.from(receipt.l2ToL1Logs.entries()).filter(([, log]) =>
+ isAddressEq(log.sender, L1_MESSENGER_ADDRESS),
+ );
+ const [l2ToL1LogIndex, l2ToL1Log] = messages[index];
+
+ return {
+ l2ToL1LogIndex,
+ l2ToL1Log,
+ };
+ }
+
+ /**
+ * Returns the {@link FinalizeWithdrawalParams parameters} required for finalizing a withdrawal from the
+ * withdrawal transaction's log on the L1 network.
+ *
+ * @param withdrawalHash Hash of the L2 transaction where the withdrawal was initiated.
+ * @param [index=0] In case there were multiple withdrawals in one transaction, you may pass an index of the
+ * withdrawal you want to finalize.
+ * @throws {Error} If log proof can not be found.
+ */
+ async finalizeWithdrawalParams(
+ withdrawalHash: web3Types.Bytes,
+ index = 0,
+ ): Promise {
+ const { log, l1BatchTxId } = await this._getWithdrawalLog(withdrawalHash, index);
+ const { l2ToL1LogIndex } = await this._getWithdrawalL2ToL1Log(withdrawalHash, index);
+ const sender = log?.topics && dataSlice(toBytes(log?.topics[1]), 12);
+ const proof = await this._contextL2().getL2ToL1LogProof(
+ toHex(withdrawalHash),
+ l2ToL1LogIndex,
+ );
+ if (!proof) {
+ throw new Error('Log proof not found!');
+ }
+ const message = Web3EthAbi.decodeParameters(['bytes'], log?.data)[0];
+ return {
+ l1BatchNumber: log.l1BatchNumber,
+ l2MessageIndex: Number(toNumber(proof.id)),
+ l2TxNumberInBlock: l1BatchTxId ? Number(toNumber(l1BatchTxId)) : null,
+ message,
+ sender,
+ proof: proof.proof,
+ };
+ }
+
+ /**
+ * Proves the inclusion of the `L2->L1` withdrawal message.
+ *
+ * @param withdrawalHash Hash of the L2 transaction where the withdrawal was initiated.
+ * @param [index=0] In case there were multiple withdrawals in one transaction, you may pass an index of the
+ * withdrawal you want to finalize.
+ * @param [overrides] Transaction's overrides which may be used to pass L1 `gasLimit`, `gasPrice`, `value`, etc.
+ * @returns A promise that resolves to the proof of inclusion of the withdrawal message.
+ * @throws {Error} If log proof can not be found.
+ */
+ async finalizeWithdrawal(
+ withdrawalHash: web3Types.Bytes,
+ index = 0,
+ overrides?: TransactionOverrides,
+ ) {
+ const { l1BatchNumber, l2MessageIndex, l2TxNumberInBlock, message, proof } =
+ await this.finalizeWithdrawalParams(withdrawalHash, index);
+
+ const l1Contracts = await this.getL1BridgeContracts();
+ const contract = new (this._contextL1().eth.Contract)(
+ IL1BridgeABI,
+ l1Contracts.shared.options.address,
+ );
+ overrides = overrides ?? {};
+ overrides.from ??= this.getAddress();
+
+ return (
+ contract.methods
+ .finalizeWithdrawal(
+ (await this._contextL2().eth.getChainId()) as web3Types.Numbers,
+ l1BatchNumber as web3Types.Numbers,
+ l2MessageIndex as web3Types.Numbers,
+ l2TxNumberInBlock as web3Types.Numbers,
+ message,
+ proof,
+ )
+ // @ts-ignore
+ .send(overrides ?? {})
+ );
+ }
+
+ /**
+ * Returns whether the withdrawal transaction is finalized on the L1 network.
+ *
+ * @param withdrawalHash Hash of the L2 transaction where the withdrawal was initiated.
+ * @param [index=0] In case there were multiple withdrawals in one transaction, you may pass an index of the
+ * withdrawal you want to finalize.
+ * @throws {Error} If log proof can not be found.
+ */
+ async isWithdrawalFinalized(withdrawalHash: web3Types.Bytes, index = 0): Promise {
+ const { log } = await this._getWithdrawalLog(withdrawalHash, index);
+ const { l2ToL1LogIndex } = await this._getWithdrawalL2ToL1Log(withdrawalHash, index);
+ const sender = dataSlice(log.topics[1], 12);
+ // `getLogProof` is called not to get proof but
+ // to get the index of the corresponding L2->L1 log,
+ // which is returned as `proof.id`.
+ const proof = await this._contextL2().getL2ToL1LogProof(
+ toHex(withdrawalHash),
+ l2ToL1LogIndex,
+ );
+ if (!proof) {
+ throw new Error('Log proof not found!');
+ }
+
+ const chainId = await this._contextL2().eth.getChainId();
+
+ let l1Bridge: Web3.Contract | Web3.Contract;
+
+ if (await this._contextL2().isBaseToken(sender)) {
+ l1Bridge = (await this.getL1BridgeContracts()).shared;
+ } else {
+ const l2BridgeContract = new (this._contextL2().eth.Contract)(IL2BridgeABI, sender);
+ const l1BridgeAddress = await l2BridgeContract.methods.l1Bridge().call();
+ l1Bridge = new (this._contextL1().eth.Contract)(IL1BridgeABI, l1BridgeAddress);
+ }
+
+ return await l1Bridge.methods
+ .isWithdrawalFinalized(chainId, log.l1BatchNumber, proof.id)
+ .call();
+ }
+
+ /**
+ * Withdraws funds from the initiated deposit, which failed when finalizing on L2.
+ * If the deposit L2 transaction has failed, it sends an L1 transaction calling `claimFailedDeposit` method of the
+ * L1 bridge, which results in returning L1 tokens back to the depositor.
+ *
+ * @param depositHash The L2 transaction hash of the failed deposit.
+ * @param [overrides] Transaction's overrides which may be used to pass L1 `gasLimit`, `gasPrice`, `value`, etc.
+ * @returns A promise that resolves to the response of the `claimFailedDeposit` transaction.
+ * @throws {Error} If attempting to claim successful deposit.
+ */
+ async claimFailedDeposit(
+ depositHash: web3Types.Bytes,
+ overrides?: TransactionOverrides,
+ ): Promise {
+ const receipt = await this._contextL2().getZKTransactionReceipt(
+ web3Utils.toHex(depositHash),
+ );
+ if (!receipt) {
+ throw new Error('Transaction not found!');
+ }
+ const successL2ToL1LogIndex = receipt.l2ToL1Logs.findIndex(
+ l2ToL1log =>
+ isAddressEq(l2ToL1log.sender, BOOTLOADER_FORMAL_ADDRESS) &&
+ l2ToL1log.key === depositHash,
+ );
+ const successL2ToL1Log = receipt.l2ToL1Logs[successL2ToL1LogIndex];
+ if (successL2ToL1Log.value !== ZeroHash) {
+ throw new Error('Cannot claim successful deposit!');
+ }
+
+ const tx = await this._contextL2().eth.getTransaction(web3Utils.toHex(depositHash));
+
+ // Undo the aliasing, since the Mailbox contract set it as for contract address.
+ const l1BridgeAddress = undoL1ToL2Alias(receipt.from);
+ const l2BridgeAddress = receipt.to;
+ if (!l2BridgeAddress) {
+ throw new Error('L2 bridge address not found!');
+ }
+ const l1Bridge = new (this._contextL1().eth.Contract)(IL1BridgeABI, l1BridgeAddress);
+
+ const l2Bridge = new Web3.Contract(IL2BridgeABI, l1BridgeAddress);
+ l2Bridge.setProvider(this._contextL2().provider);
+
+ const calldata = l2Bridge.methods
+ .finalizeDeposit()
+ .decodeData(web3Utils.toHex(String(tx.data)));
+
+ const proof = await this._contextL2().getL2ToL1LogProof(
+ web3Utils.toHex(depositHash),
+ successL2ToL1LogIndex,
+ );
+ if (!proof) {
+ throw new Error('Log proof not found!');
+ }
+ return (
+ l1Bridge.methods
+ .claimFailedDeposit(
+ (await this._contextL2().eth.getChainId()) as web3Types.Numbers,
+ calldata[0], //_l1Sender
+ calldata[2], //_l1Token
+ calldata[3], //_amount
+ depositHash,
+ receipt.l1BatchNumber,
+ proof.id,
+ receipt.l1BatchTxIndex,
+ proof.proof,
+ )
+ // @ts-ignore
+ .send(overrides ?? {})
+ );
+ }
+
+ /**
+ * Requests execution of an L2 transaction from L1.
+ *
+ * @param transaction The transaction details.
+ * @param transaction.contractAddress The L2 contract to be called.
+ * @param transaction.calldata The input of the L2 transaction.
+ * @param [transaction.l2GasLimit] Maximum amount of L2 gas that transaction can consume during execution on L2.
+ * @param [transaction.mintValue] The amount of base token that needs to be minted on non-ETH-based L2.
+ * @param [transaction.l2Value] `msg.value` of L2 transaction.
+ * @param [transaction.factoryDeps] An array of L2 bytecodes that will be marked as known on L2.
+ * @param [transaction.operatorTip] (currently not used) If the ETH value passed with the transaction is not
+ * explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of
+ * the base cost of the transaction.
+ * @param [transaction.gasPerPubdataByte] The L2 gas price for each published L1 calldata byte.
+ * @param [transaction.refundRecipient] The address on L2 that will receive the refund for the transaction.
+ * If the transaction fails, it will also be the address to receive `l2Value`.
+ * @param [transaction.overrides] Transaction's overrides which may be used to pass L2 `gasLimit`, `gasPrice`, `value`, etc.
+ * @returns A promise that resolves to the response of the execution request.
+ */
+ async requestExecute(transaction: {
+ contractAddress: Address;
+ calldata: string;
+ l2GasLimit?: web3Types.Numbers;
+ mintValue?: web3Types.Numbers;
+ l2Value?: web3Types.Numbers;
+ factoryDeps?: web3Types.Bytes[];
+ operatorTip?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ }): Promise {
+ const tx = await this.getRequestExecuteTx(transaction);
+ return this.signAndSend(tx);
+ }
+ async signAndSend(tx: Transaction, _context?: Web3ZkSyncL1 | Web3ZkSyncL2) {
+ const context = _context || this._contextL1();
+ const populated = await context.populateTransaction(tx);
+ const signed = await context.signTransaction(populated as Transaction);
+
+ return getPriorityOpResponse(
+ context,
+ context.sendRawTransaction(signed),
+ this._contextL2(),
+ );
+ }
+ async signTransaction(tx: Transaction): Promise {
+ return this._contextL1().signTransaction(tx);
+ }
+ async sendRawTransaction(signedTx: string): Promise {
+ return this._contextL1().sendRawTransaction(signedTx);
+ }
+ /**
+ * Estimates the amount of gas required for a request execute transaction.
+ *
+ * @param transaction The transaction details.
+ * @param transaction.contractAddress The L2 contract to be called.
+ * @param transaction.calldata The input of the L2 transaction.
+ * @param [transaction.l2GasLimit] Maximum amount of L2 gas that transaction can consume during execution on L2.
+ * @param [transaction.mintValue] The amount of base token that needs to be minted on non-ETH-based L2.
+ * @param [transaction.l2Value] `msg.value` of L2 transaction.
+ * @param [transaction.factoryDeps] An array of L2 bytecodes that will be marked as known on L2.
+ * @param [transaction.operatorTip] (currently not used) If the ETH value passed with the transaction is not
+ * explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top
+ * of the base cost of the transaction.
+ * @param [transaction.gasPerPubdataByte] The L2 gas price for each published L1 calldata byte.
+ * @param [transaction.refundRecipient] The address on L2 that will receive the refund for the transaction.
+ * If the transaction fails, it will also be the address to receive `l2Value`.
+ * @param [transaction.overrides] Transaction's overrides which may be used to pass L1 `gasLimit`, `gasPrice`, `value`, etc.
+ */
+ async estimateGasRequestExecute(transaction: {
+ contractAddress: Address;
+ calldata: string;
+ l2GasLimit?: web3Types.Numbers;
+ mintValue?: web3Types.Numbers;
+ l2Value?: web3Types.Numbers;
+ factoryDeps?: web3Types.Bytes[];
+ operatorTip?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ }): Promise {
+ const { method, overrides } = await this.getRequestExecuteContractMethod(transaction);
+
+ delete overrides.gasPrice;
+ delete overrides.maxFeePerGas;
+ delete overrides.maxPriorityFeePerGas;
+
+ return method.estimateGas(overrides as PayableTxOptions);
+ }
+
+ /**
+ * Returns the parameters for the approval token transaction based on the request execute transaction.
+ * Existing allowance for the bridge is not checked; allowance is calculated solely based on the specified transaction.
+ *
+ * @param transaction The request execute transaction on which approval parameters are calculated.
+ */
+ async getRequestExecuteAllowanceParams(transaction: {
+ contractAddress: Address;
+ calldata: string;
+ l2GasLimit?: web3Types.Numbers;
+ l2Value?: web3Types.Numbers;
+ factoryDeps?: web3Types.Bytes[];
+ operatorTip?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ }): Promise<{ token: Address; allowance: web3Types.Numbers }> {
+ const bridgehub = await this.getBridgehubContract();
+ const chainId = await this._contextL2().eth.getChainId();
+ const isETHBaseToken = isAddressEq(
+ await bridgehub.methods.baseToken(chainId).call(),
+ ETH_ADDRESS_IN_CONTRACTS,
+ );
+
+ if (isETHBaseToken) {
+ throw new Error(
+ "ETH token can't be approved! The address of the token does not exist on L1.",
+ );
+ }
+
+ const { ...tx } = transaction;
+ tx.l2Value ??= 0n;
+ tx.operatorTip ??= 0n;
+ tx.factoryDeps ??= [];
+ tx.overrides ??= {};
+ tx.gasPerPubdataByte ??= REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT;
+ tx.refundRecipient ??= this.getAddress();
+ tx.l2GasLimit ??= await this._contextL2().estimateL1ToL2Execute(transaction);
+
+ const { l2Value, l2GasLimit, operatorTip, overrides, gasPerPubdataByte } = tx;
+
+ await insertGasPrice(this._contextL1(), overrides);
+ const gasPriceForEstimation = overrides.maxFeePerGas || overrides.gasPrice;
+
+ const baseCost = await this.getBaseCost({
+ gasPrice: gasPriceForEstimation!,
+ gasPerPubdataByte,
+ gasLimit: l2GasLimit,
+ });
+
+ return {
+ token: await this.getBaseToken(),
+ allowance: baseCost + BigInt(operatorTip) + BigInt(l2Value),
+ };
+ }
+ async getRequestExecuteContractMethod(transaction: {
+ contractAddress: Address;
+ calldata: string;
+ l2GasLimit?: web3Types.Numbers;
+ mintValue?: web3Types.Numbers;
+ l2Value?: web3Types.Numbers;
+ factoryDeps?: web3Types.Bytes[];
+ operatorTip?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ }) {
+ const bridgehub = await this.getBridgehubContract();
+ const chainId = await this._contextL2().eth.getChainId();
+ const isETHBaseToken = isAddressEq(
+ await bridgehub.methods.baseToken(chainId).call(),
+ ETH_ADDRESS_IN_CONTRACTS,
+ );
+
+ const { ...tx } = transaction;
+ tx.l2Value ??= 0;
+ tx.mintValue ??= 0;
+ tx.operatorTip ??= 0;
+ tx.factoryDeps ??= [];
+ tx.overrides ??= {};
+ tx.overrides.from ??= this.getAddress();
+ tx.gasPerPubdataByte ??= REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT;
+ tx.refundRecipient ??= this.getAddress();
+ tx.l2GasLimit ??= await this._contextL2().estimateL1ToL2Execute(transaction);
+
+ const {
+ contractAddress,
+ mintValue,
+ l2Value,
+ calldata,
+ l2GasLimit,
+ factoryDeps,
+ operatorTip,
+ overrides,
+ gasPerPubdataByte,
+ refundRecipient,
+ } = tx;
+
+ await insertGasPrice(this._contextL1(), overrides);
+ const gasPriceForEstimation = overrides.maxFeePerGas || overrides.gasPrice;
+
+ const baseCost = await this.getBaseCost({
+ gasPrice: gasPriceForEstimation!,
+ gasPerPubdataByte,
+ gasLimit: l2GasLimit,
+ });
+
+ const l2Costs = baseCost + BigInt(operatorTip) + BigInt(l2Value);
+ let providedValue = isETHBaseToken ? overrides.value : mintValue;
+ if (providedValue === undefined || providedValue === null || BigInt(providedValue) === 0n) {
+ providedValue = l2Costs;
+ if (isETHBaseToken) overrides.value = providedValue;
+ }
+ await checkBaseCost(baseCost, providedValue);
+
+ const method = bridgehub.methods.requestL2TransactionDirect({
+ chainId,
+ mintValue: providedValue,
+ l2Contract: contractAddress,
+ l2Value: l2Value,
+ l2Calldata: calldata,
+ l2GasLimit: l2GasLimit,
+ l2GasPerPubdataByteLimit: REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT,
+ factoryDeps: factoryDeps,
+ refundRecipient: refundRecipient,
+ });
+ return { method, overrides };
+ }
+ /**
+ * Returns a populated request execute transaction.
+ *
+ * @param transaction The transaction details.
+ * @param transaction.contractAddress The L2 contract to be called.
+ * @param transaction.calldata The input of the L2 transaction.
+ * @param [transaction.l2GasLimit] Maximum amount of L2 gas that transaction can consume during execution on L2.
+ * @param [transaction.mintValue] The amount of base token that needs to be minted on non-ETH-based L2.
+ * @param [transaction.l2Value] `msg.value` of L2 transaction.
+ * @param [transaction.factoryDeps] An array of L2 bytecodes that will be marked as known on L2.
+ * @param [transaction.operatorTip] (currently not used) If the ETH value passed with the transaction is not
+ * explicitly stated in the overrides, this field will be equal to the tip the operator will receive on top of the
+ * base cost of the transaction.
+ * @param [transaction.gasPerPubdataByte] The L2 gas price for each published L1 calldata byte.
+ * @param [transaction.refundRecipient] The address on L2 that will receive the refund for the transaction.
+ * If the transaction fails, it will also be the address to receive `l2Value`.
+ * @param [transaction.overrides] Transaction's overrides which may be used to pass L1 `gasLimit`, `gasPrice`, `value`, etc.
+ */
+ async getRequestExecuteTx(transaction: {
+ contractAddress: Address;
+ calldata: string;
+ l2GasLimit?: web3Types.Numbers;
+ mintValue?: web3Types.Numbers;
+ l2Value?: web3Types.Numbers;
+ factoryDeps?: web3Types.Bytes[];
+ operatorTip?: web3Types.Numbers;
+ gasPerPubdataByte?: web3Types.Numbers;
+ refundRecipient?: Address;
+ overrides?: TransactionOverrides;
+ }) {
+ const { method, overrides } = await this.getRequestExecuteContractMethod(transaction);
+ return method.populateTransaction(overrides as PayableTxOptions);
+ }
+
+ async populateTransaction(tx: Transaction): Promise {
+ tx.from = this.getAddress();
+
+ if (
+ (!tx.type || (tx.type && toHex(tx.type) !== toHex(EIP712_TX_TYPE))) &&
+ !(tx as Eip712TxData).customData
+ ) {
+ return this._contextL1().populateTransaction(tx);
+ }
+
+ const populated = (await this._contextL1().populateTransaction(tx)) as Eip712TxData;
+ populated.type = EIP712_TX_TYPE;
+ populated.value ??= 0;
+ populated.data ??= '0x';
+
+ return populated;
+ }
+ // @ts-ignore
+ public getAddress(): string {
+ throw new Error('Must be implemented by the derived class!');
+ }
+}
+
+export class AdapterL2 implements TxSender {
+ /**
+ * Returns a context (provider + Signer) instance for connecting to an L2 network.
+ */
+ _contextL2(): Web3ZkSyncL2 {
+ throw new Error('Must be implemented by the derived class!');
+ }
+ async _eip712Signer(): Promise {
+ throw new Error('Must be implemented by the derived class!');
+ }
+
+ /**
+ * Returns the balance of the account.
+ *
+ * @param [token] The token address to query balance for. Defaults to the native token.
+ * @param [blockTag='committed'] The block tag to get the balance at.
+ */
+ async getBalance(
+ token?: Address,
+ blockTag: web3Types.BlockNumberOrTag = 'committed',
+ ): Promise {
+ if (token) {
+ const contract = new (this._contextL2().eth.Contract)(IERC20ABI, token);
+ return contract.methods.balanceOf(this.getAddress()).call();
+ }
+
+ return await this._contextL2().eth.getBalance(this.getAddress(), blockTag);
+ }
+
+ /**
+ * Returns all token balances of the account.
+ */
+ async getAllBalances(): Promise {
+ return this._contextL2().getAllAccountBalances(this.getAddress());
+ }
+
+ /**
+ * Returns the deployment nonce of the account.
+ */
+ async getDeploymentNonce(): Promise {
+ const contract = new Web3.Contract(INonceHolderABI, NONCE_HOLDER_ADDRESS);
+ contract.setProvider(this._contextL2().provider);
+ return contract.methods.getDeploymentNonce(this.getAddress()).call();
+ }
+
+ /**
+ * Returns L2 bridge contracts.
+ */
+ async getL2BridgeContracts(): Promise<{
+ erc20: Web3.Contract;
+ weth: Web3.Contract;
+ shared: Web3.Contract;
+ }> {
+ const addresses = await this._contextL2().getDefaultBridgeAddresses();
+ const erc20 = new Web3.Contract(IL2BridgeABI, addresses.erc20L2);
+ const weth = new Web3.Contract(IL2BridgeABI, addresses.wethL2);
+ const shared = new Web3.Contract(IL2BridgeABI, addresses.sharedL2);
+
+ erc20.setProvider(this._contextL2().provider);
+ weth.setProvider(this._contextL2().provider);
+ shared.setProvider(this._contextL2().provider);
+
+ return {
+ erc20,
+ weth,
+ shared,
+ };
+ }
+
+ /**
+ * Initiates the withdrawal process which withdraws ETH or any ERC20 token
+ * from the associated account on L2 network to the target account on L1 network.
+ *
+ * @param transaction Withdrawal transaction request.
+ * @param transaction.token The address of the token. Defaults to ETH.
+ * @param transaction.amount The amount of the token to withdraw.
+ * @param [transaction.to] The address of the recipient on L1.
+ * @param [transaction.bridgeAddress] The address of the bridge contract to be used.
+ * @param [transaction.paymasterParams] Paymaster parameters.
+ * @param [transaction.overrides] Transaction's overrides which may be used to pass L2 `gasLimit`, `gasPrice`, `value`, etc.
+ * @returns A Promise resolving to a withdrawal transaction response.
+ */
+ async withdraw(transaction: {
+ token: Address;
+ amount: web3Types.Numbers;
+ to?: Address;
+ bridgeAddress?: Address;
+ paymasterParams?: PaymasterParams;
+ overrides?: TransactionOverrides;
+ }) {
+ const tx = await this._contextL2().getWithdrawTx({
+ ...transaction,
+ from: this.getAddress(),
+ });
+ const populated = await this.populateTransaction(tx as Transaction);
+ const signed = await this.signTransaction(populated as Transaction);
+ return getPriorityOpResponse(
+ this._contextL2(),
+ this.sendRawTransaction(signed),
+ this._contextL2(),
+ );
+ }
+
+ async signTransaction(tx: Transaction): Promise {
+ return this._contextL2().signTransaction(tx);
+ }
+ async sendRawTransaction(signedTx: string): Promise {
+ return this._contextL2().sendRawTransaction(signedTx);
+ }
+
+ /**
+ * Transfer ETH or any ERC20 token within the same interface.
+ *
+ * @param transaction Transfer transaction request.
+ * @param transaction.to The address of the recipient.
+ * @param transaction.amount The amount of the token to transfer.
+ * @param [transaction.token] The address of the token. Defaults to ETH.
+ * @param [transaction.paymasterParams] Paymaster parameters.
+ * @param [transaction.overrides] Transaction's overrides which may be used to pass L2 `gasLimit`, `gasPrice`, `value`, etc.
+ * @returns A Promise resolving to a transfer transaction response.
+ */
+ async transfer(transaction: {
+ to: Address;
+ amount: web3Types.Numbers;
+ token?: Address;
+ paymasterParams?: PaymasterParams;
+ overrides?: TransactionOverrides;
+ }) {
+ return this._contextL2().getTransferTx({
+ from: this.getAddress(),
+ ...transaction,
+ });
+ }
+ // @ts-ignore
+ public getAddress(): string {
+ throw new Error('Must be implemented by the derived class!');
+ }
+
+ async populateTransaction(tx: Transaction): Promise {
+ tx.from = this.getAddress();
+ if (
+ (!tx.type || (tx.type && toHex(tx.type) !== toHex(EIP712_TX_TYPE))) &&
+ !(tx as Eip712TxData).customData
+ ) {
+ return this._contextL2().populateTransaction(tx);
+ }
+
+ const populated = (await this._contextL2().populateTransaction(tx)) as Eip712TxData;
+ populated.type = EIP712_TX_TYPE;
+ populated.value ??= 0;
+ populated.data ??= '0x';
+
+ return populated;
+ }
+}
+
+// This method checks if the overrides contain a gasPrice (or maxFeePerGas),
+// if not it will insert the maxFeePerGas
+async function insertGasPrice(
+ l1Provider: Web3.Web3,
+ overrides: TransactionOverrides,
+): Promise {
+ if (!overrides.gasPrice && !overrides.maxFeePerGas) {
+ const l1FeeData = await l1Provider.eth.calculateFeeData();
+ // Sometimes baseFeePerGas is not available, so we use gasPrice instead.
+ const baseFee = BigInt(
+ l1FeeData.maxFeePerGas! ? getBaseCostFromFeeData(l1FeeData) : l1FeeData.gasPrice!,
+ );
+ if (!baseFee) {
+ throw new Error('Failed to calculate base fee!');
+ }
+
+ // ethers.js by default uses multiplication by 2, but since the price for the L2 part
+ // will depend on the L1 part, doubling base fee is typically too much.
+ overrides.maxFeePerGas =
+ (baseFee * 3n) / 2n + (BigInt(l1FeeData.maxPriorityFeePerGas!) ?? 0n);
+ overrides.maxPriorityFeePerGas =
+ l1FeeData.maxPriorityFeePerGas && toHex(l1FeeData.maxPriorityFeePerGas);
+ }
+}
+
+function getBaseCostFromFeeData(feeData: web3Types.FeeData): bigint {
+ const maxFeePerGas = feeData.maxFeePerGas!;
+ const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas!;
+
+ return (BigInt(maxFeePerGas) - BigInt(maxPriorityFeePerGas)) / 2n;
+}
diff --git a/src/contracts/IERC20.ts b/src/contracts/IERC20.ts
index 7b64f8f..03239cf 100644
--- a/src/contracts/IERC20.ts
+++ b/src/contracts/IERC20.ts
@@ -17,124 +17,223 @@ along with web3.js. If not, see .
export const IERC20ABI = [
{
- inputs: [{ internalType: 'uint256', name: 'initialSupply', type: 'uint256' }],
- stateMutability: 'nonpayable',
- type: 'constructor',
- },
- {
- anonymous: false,
- inputs: [
- { indexed: true, internalType: 'address', name: 'owner', type: 'address' },
- { indexed: true, internalType: 'address', name: 'spender', type: 'address' },
- { indexed: false, internalType: 'uint256', name: 'value', type: 'uint256' },
- ],
- name: 'Approval',
- type: 'event',
- },
- {
- anonymous: false,
- inputs: [
- { indexed: true, internalType: 'address', name: 'from', type: 'address' },
- { indexed: true, internalType: 'address', name: 'to', type: 'address' },
- { indexed: false, internalType: 'uint256', name: 'value', type: 'uint256' },
- ],
- name: 'Transfer',
- type: 'event',
- },
- {
- inputs: [
- { internalType: 'address', name: 'owner', type: 'address' },
- { internalType: 'address', name: 'spender', type: 'address' },
+ constant: true,
+ inputs: [],
+ name: 'name',
+ outputs: [
+ {
+ name: '',
+ type: 'string',
+ },
],
- name: 'allowance',
- outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
+ payable: false,
stateMutability: 'view',
type: 'function',
},
{
+ constant: false,
inputs: [
- { internalType: 'address', name: 'spender', type: 'address' },
- { internalType: 'uint256', name: 'amount', type: 'uint256' },
+ {
+ name: '_spender',
+ type: 'address',
+ },
+ {
+ name: '_value',
+ type: 'uint256',
+ },
],
name: 'approve',
- outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
+ outputs: [
+ {
+ name: '',
+ type: 'bool',
+ },
+ ],
+ payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
- inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
- name: 'balanceOf',
- outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
- stateMutability: 'view',
- type: 'function',
- },
- {
+ constant: true,
inputs: [],
- name: 'decimals',
- outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }],
+ name: 'totalSupply',
+ outputs: [
+ {
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
stateMutability: 'view',
type: 'function',
},
{
+ constant: false,
inputs: [
- { internalType: 'address', name: 'spender', type: 'address' },
- { internalType: 'uint256', name: 'subtractedValue', type: 'uint256' },
+ {
+ name: '_from',
+ type: 'address',
+ },
+ {
+ name: '_to',
+ type: 'address',
+ },
+ {
+ name: '_value',
+ type: 'uint256',
+ },
],
- name: 'decreaseAllowance',
- outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
- stateMutability: 'nonpayable',
- type: 'function',
- },
- {
- inputs: [
- { internalType: 'address', name: 'spender', type: 'address' },
- { internalType: 'uint256', name: 'addedValue', type: 'uint256' },
+ name: 'transferFrom',
+ outputs: [
+ {
+ name: '',
+ type: 'bool',
+ },
],
- name: 'increaseAllowance',
- outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
+ payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
+ constant: true,
inputs: [],
- name: 'name',
- outputs: [{ internalType: 'string', name: '', type: 'string' }],
+ name: 'decimals',
+ outputs: [
+ {
+ name: '',
+ type: 'uint8',
+ },
+ ],
+ payable: false,
stateMutability: 'view',
type: 'function',
},
{
- inputs: [],
- name: 'symbol',
- outputs: [{ internalType: 'string', name: '', type: 'string' }],
+ constant: true,
+ inputs: [
+ {
+ name: '_owner',
+ type: 'address',
+ },
+ ],
+ name: 'balanceOf',
+ outputs: [
+ {
+ name: 'balance',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
stateMutability: 'view',
type: 'function',
},
{
+ constant: true,
inputs: [],
- name: 'totalSupply',
- outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
+ name: 'symbol',
+ outputs: [
+ {
+ name: '',
+ type: 'string',
+ },
+ ],
+ payable: false,
stateMutability: 'view',
type: 'function',
},
{
+ constant: false,
inputs: [
- { internalType: 'address', name: 'to', type: 'address' },
- { internalType: 'uint256', name: 'amount', type: 'uint256' },
+ {
+ name: '_to',
+ type: 'address',
+ },
+ {
+ name: '_value',
+ type: 'uint256',
+ },
],
name: 'transfer',
- outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
+ outputs: [
+ {
+ name: '',
+ type: 'bool',
+ },
+ ],
+ payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
+ constant: true,
inputs: [
- { internalType: 'address', name: 'from', type: 'address' },
- { internalType: 'address', name: 'to', type: 'address' },
- { internalType: 'uint256', name: 'amount', type: 'uint256' },
+ {
+ name: '_owner',
+ type: 'address',
+ },
+ {
+ name: '_spender',
+ type: 'address',
+ },
],
- name: 'transferFrom',
- outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
- stateMutability: 'nonpayable',
+ name: 'allowance',
+ outputs: [
+ {
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
type: 'function',
},
+ {
+ payable: true,
+ stateMutability: 'payable',
+ type: 'fallback',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ name: 'owner',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ name: 'spender',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ name: 'value',
+ type: 'uint256',
+ },
+ ],
+ name: 'Approval',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ name: 'from',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ name: 'value',
+ type: 'uint256',
+ },
+ ],
+ name: 'Transfer',
+ type: 'event',
+ },
] as const;
diff --git a/src/index.ts b/src/index.ts
index 705e799..31a1263 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,4 +1,7 @@
export { ZkSyncPlugin } from './plugin';
+export { Web3ZkSyncL1 } from './web3zksync-l1';
+export { Web3ZkSyncL2 } from './web3zksync-l2';
+export { ZKSyncWallet } from './zksync-wallet';
export * as utils from './utils';
export * as types from './types';
export * as constants from './constants';
diff --git a/src/paymaster-utils.ts b/src/paymaster-utils.ts
new file mode 100644
index 0000000..0d11837
--- /dev/null
+++ b/src/paymaster-utils.ts
@@ -0,0 +1,81 @@
+import { Contract } from 'web3-eth-contract';
+
+import type {
+ Address,
+ ApprovalBasedPaymasterInput,
+ GeneralPaymasterInput,
+ PaymasterInput,
+ PaymasterParams,
+} from './types';
+import { Abi } from './contracts/IPaymasterFlow';
+
+/**
+ * The ABI for the `IPaymasterFlow` interface, which is utilized
+ * for encoding input parameters for paymaster flows.
+ * @constant
+ */
+export const PAYMASTER_FLOW_ABI = new Contract(Abi);
+
+/**
+ * Returns encoded input for an approval-based paymaster.
+ *
+ * @param paymasterInput The input data for the paymaster.
+ */
+export function getApprovalBasedPaymasterInput(paymasterInput: ApprovalBasedPaymasterInput) {
+ return PAYMASTER_FLOW_ABI.methods
+ .approvalBased(
+ paymasterInput.token,
+ paymasterInput.minimalAllowance,
+ paymasterInput.innerInput,
+ )
+ .encodeABI();
+}
+
+/**
+ * Returns encoded input for a general-based paymaster.
+ *
+ * @param paymasterInput The input data for the paymaster.
+ */
+export function getGeneralPaymasterInput(paymasterInput: GeneralPaymasterInput) {
+ return PAYMASTER_FLOW_ABI.methods.general(paymasterInput.innerInput).encodeABI();
+}
+
+/**
+ * Returns a correctly-formed {@link PaymasterParams|paymasterParams} object for common paymaster flows.
+ *
+ * @param paymasterAddress The non-zero paymaster address.
+ * @param paymasterInput The input data for the paymaster.
+ *
+ * @example Create general-based parameters.
+ *
+ * const paymasterAddress = "0x0a67078A35745947A37A552174aFe724D8180c25";
+ * const paymasterParams = utils.getPaymasterParams(paymasterAddress, {
+ * type: "General",
+ * innerInput: new Uint8Array(),
+ * });
+ *
+ * @example Create approval-based parameters.
+ *
+ * const result = utils.getPaymasterParams("0x0a67078A35745947A37A552174aFe724D8180c25", {
+ * type: "ApprovalBased",
+ * token: "0x65C899B5fb8Eb9ae4da51D67E1fc417c7CB7e964",
+ * minimalAllowance: BigInt(1),
+ * innerInput: new Uint8Array(),
+ * });
+ */
+export function getPaymasterParams(
+ paymasterAddress: Address,
+ paymasterInput: PaymasterInput,
+): PaymasterParams {
+ if (paymasterInput.type === 'General') {
+ return {
+ paymaster: paymasterAddress,
+ paymasterInput: getGeneralPaymasterInput(paymasterInput),
+ };
+ } else {
+ return {
+ paymaster: paymasterAddress,
+ paymasterInput: getApprovalBasedPaymasterInput(paymasterInput),
+ };
+ }
+}
diff --git a/src/plugin.ts b/src/plugin.ts
index 774ad1e..a90a3e3 100644
--- a/src/plugin.ts
+++ b/src/plugin.ts
@@ -1,4 +1,5 @@
-import type { Web3Context, Web3RequestManager } from 'web3-core';
+import type { Web3Context, Web3ContextInitOptions, Web3RequestManager } from 'web3-core';
+import type * as web3Types from 'web3-types';
import type { Address } from 'web3-types';
import { Contract } from 'web3-eth-contract';
import { Web3PluginBase } from 'web3-core';
@@ -6,7 +7,7 @@ import { Web3PluginBase } from 'web3-core';
import { TransactionFactory } from 'web3-eth-accounts';
import { IERC20ABI } from './contracts/IERC20';
import { RpcMethods } from './rpc.methods';
-import { EIP712_TX_TYPE, ETH_ADDRESS, ZERO_ADDRESS } from './constants';
+import * as constants from './constants';
import { IL2BridgeABI } from './contracts/IL2Bridge';
import { IZkSyncABI } from './contracts/IZkSyncStateTransition';
import { IBridgehubABI } from './contracts/IBridgehub';
@@ -16,17 +17,27 @@ import { IERC1271ABI } from './contracts/IERC1271';
import { IL1BridgeABI } from './contracts/IL1ERC20Bridge';
import { INonceHolderABI } from './contracts/INonceHolder';
import { EIP712Transaction } from './Eip712';
+import { ZKSyncWallet } from './zksync-wallet';
+import { Web3ZkSyncL2 } from './web3zksync-l2';
+import { Web3ZkSyncL1 } from './web3zksync-l1';
+import type { ContractsAddresses } from './types';
-export class ZkSyncPlugin extends Web3PluginBase {
- public pluginNamespace = 'zkSync';
- public erc20BridgeL1: string;
- public erc20BridgeL2: string;
- public wethBridgeL1: string;
- public wethBridgeL2: string;
- public _rpc?: RpcMethods;
- public _l2BridgeContracts: Record>;
- public _erc20Contracts: Record>;
- public Contracts: {
+interface ZKSyncWalletConstructor {
+ new (privateKey: string): ZKSyncWallet;
+}
+
+export type ZKSyncContractsCollection = {
+ Generic: {
+ /**
+ * The web3.js Contract instance for the `IERC20` interface, which is utilized for interacting with ERC20 tokens.
+ */
+ IERC20Contract: Contract;
+ /**
+ * The web3.js Contract instance for the `IERC1271` interface, which is utilized for signature validation by contracts.
+ */
+ IERC1271Contract: Contract;
+ };
+ L1: {
/**
* The web3.js Contract instance for the `ZkSync` interface.
*/
@@ -35,6 +46,12 @@ export class ZkSyncPlugin extends Web3PluginBase {
* The ABI of the `Bridgehub` interface.
*/
BridgehubContract: Contract;
+ /**
+ * The web3.js Contract instance for the `IL1Bridge` interface, which is utilized for transferring ERC20 tokens from L1 to L2.
+ */
+ L1BridgeContract: Contract;
+ };
+ L2: {
/**
* The web3.js Contract instance for the `IContractDeployer` interface, which is utilized for deploying smart contracts.
*/
@@ -43,79 +60,176 @@ export class ZkSyncPlugin extends Web3PluginBase {
* The web3.js Contract instance for the `IL1Messenger` interface, which is utilized for sending messages from the L2 to L1.
*/
L1MessengerContract: Contract;
- /**
- * The web3.js Contract instance for the `IERC20` interface, which is utilized for interacting with ERC20 tokens.
- */
- IERC20Contract: Contract;
- /**
- * The web3.js Contract instance for the `IERC1271` interface, which is utilized for signature validation by contracts.
- */
- IERC1271Contract: Contract