From 8372b1fc29133d296cfbb02f44d3e2e8b4149625 Mon Sep 17 00:00:00 2001 From: saurabh Date: Tue, 12 Dec 2023 11:44:36 +0530 Subject: [PATCH] added support for type-2 tx signing --- CHANGELOG.md | 3 +- package-lock.json | 237 +++++++++++++++++++++++++++++++++++++--------- package.json | 3 +- src/index.js | 39 ++++---- test/index.js | 69 ++++++++++---- 5 files changed, 271 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0599288..c35307d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,4 +46,5 @@ ### 1.0.9 (2023-12-12) -- Added gas estimation for EIP-1559 transaction on arbitrum. \ No newline at end of file +- Added gas estimation for EIP-1559 transaction on arbitrum. +- Added support for Type-2 transactions signing. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index dcc7df0..6847971 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "1.0.9", "license": "MIT", "dependencies": { + "@ethereumjs/common": "^4.1.0", + "@ethereumjs/tx": "^5.1.0", "axios": "^1.6.2", "bip39": "^3.0.4", "browser-passworder": "^2.0.3", @@ -16,7 +18,6 @@ "eth-hd-keyring": "^3.6.0", "eth-sig-util": "^3.0.1", "eth-simple-keyring": "^4.2.0", - "ethereumjs-tx": "^1.3.7", "ethereumjs-util": "^7.1.0", "hdkey": "^2.0.1", "loglevel": "^1.7.1", @@ -515,21 +516,128 @@ "dev": true }, "node_modules/@ethereumjs/common": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.5.0.tgz", - "integrity": "sha512-DEHjW6e38o+JmB/NO3GZBpW4lpaiBpkFgXF6jLcJ6gETBYpEyaA5nTimsWBUJR3Vmtm/didUEbNjajskugZORg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-4.1.0.tgz", + "integrity": "sha512-XWdQvUjlQHVwh4uGEPFKHpsic69GOsMXEhlHrggS5ju/+2zAmmlz6B25TkCCymeElC9DUp13tH5Tc25Iuvtlcg==", "dependencies": { - "crc-32": "^1.2.0", - "ethereumjs-util": "^7.1.1" + "@ethereumjs/util": "^9.0.1", + "crc": "^4.3.2" + } + }, + "node_modules/@ethereumjs/common/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "peer": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/@ethereumjs/common/node_modules/crc": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/crc/-/crc-4.3.2.tgz", + "integrity": "sha512-uGDHf4KLLh2zsHa8D8hIQ1H/HtFQhyHrc0uhHBcoKGol/Xnb+MPYfUMw7cvON6ze/GUESTudKayDcJC5HnJv1A==", + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "buffer": ">=6.0.3" + }, + "peerDependenciesMeta": { + "buffer": { + "optional": true + } + } + }, + "node_modules/@ethereumjs/rlp": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.1.tgz", + "integrity": "sha512-Ab/Hfzz+T9Zl+65Nkg+9xAmwKPLicsnQ4NW49pgvJp9ovefuic95cgOS9CbPc9izIEgsqm1UitV0uNveCvud9w==", + "bin": { + "rlp": "bin/rlp.cjs" + }, + "engines": { + "node": ">=18" } }, "node_modules/@ethereumjs/tx": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.3.2.tgz", - "integrity": "sha512-6AaJhwg4ucmwTvw/1qLaZUX5miWrwZ4nLOUsKyb/HtzS3BMw/CasKhdi1ims9mBKeK9sOJCH4qGKOBGyJCeeog==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-5.1.0.tgz", + "integrity": "sha512-VUhw2+4yXArJZRWhPjmZFrN4WUjUo0qUZUszVpW2KzsGlqCFf67kwJcH9Rca5eS0CRHjr2qHJLpvYOjNuaXVdA==", "dependencies": { - "@ethereumjs/common": "^2.5.0", - "ethereumjs-util": "^7.1.2" + "@ethereumjs/common": "^4.1.0", + "@ethereumjs/rlp": "^5.0.1", + "@ethereumjs/util": "^9.0.1", + "ethereum-cryptography": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@ethereumjs/tx/node_modules/ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + }, + "node_modules/@ethereumjs/util": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-9.0.1.tgz", + "integrity": "sha512-NdFFEzCc3H1sYkNnnySwLg6owdQMhjUc2jfuDyx8Xv162WSluCnnSKouKOSG3njGNEyy2I9NmF8zTRDwuqpZWA==", + "dependencies": { + "@ethereumjs/rlp": "^5.0.1", + "ethereum-cryptography": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" } }, "node_modules/@ethersproject/abi": { @@ -1135,6 +1243,61 @@ "semver": "bin/semver.js" } }, + "node_modules/@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "dependencies": { + "@noble/hashes": "1.3.1" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/base": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.4.tgz", + "integrity": "sha512-wznebWtt+ejH8el87yuD4i9xLSbYZXf1Pe4DY0o/zq/eg5I0VQVXVbFs6XIM0pNVCJ/uE3t5wI9kh90mdLUxtw==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "dependencies": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -3101,11 +3264,6 @@ "js-sha3": "^0.8.0" } }, - "node_modules/ethereum-common": { - "version": "0.0.18", - "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz", - "integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8=" - }, "node_modules/ethereum-cryptography": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", @@ -3164,35 +3322,6 @@ "rlp": "^2.2.3" } }, - "node_modules/ethereumjs-tx": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz", - "integrity": "sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA==", - "deprecated": "New package name format for new versions: @ethereumjs/tx. Please update.", - "dependencies": { - "ethereum-common": "^0.0.18", - "ethereumjs-util": "^5.0.0" - } - }, - "node_modules/ethereumjs-tx/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "node_modules/ethereumjs-tx/node_modules/ethereumjs-util": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", - "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", - "dependencies": { - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "^0.1.3", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - } - }, "node_modules/ethereumjs-util": { "version": "7.1.5", "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", @@ -7599,6 +7728,24 @@ "node": ">=8.0.0" } }, + "node_modules/web3-eth-accounts/node_modules/@ethereumjs/common": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.5.0.tgz", + "integrity": "sha512-DEHjW6e38o+JmB/NO3GZBpW4lpaiBpkFgXF6jLcJ6gETBYpEyaA5nTimsWBUJR3Vmtm/didUEbNjajskugZORg==", + "dependencies": { + "crc-32": "^1.2.0", + "ethereumjs-util": "^7.1.1" + } + }, + "node_modules/web3-eth-accounts/node_modules/@ethereumjs/tx": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.3.2.tgz", + "integrity": "sha512-6AaJhwg4ucmwTvw/1qLaZUX5miWrwZ4nLOUsKyb/HtzS3BMw/CasKhdi1ims9mBKeK9sOJCH4qGKOBGyJCeeog==", + "dependencies": { + "@ethereumjs/common": "^2.5.0", + "ethereumjs-util": "^7.1.2" + } + }, "node_modules/web3-eth-accounts/node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", diff --git a/package.json b/package.json index d5abeff..9f6f574 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,8 @@ "nyc": "^15.0.0" }, "dependencies": { + "@ethereumjs/common": "^4.1.0", + "@ethereumjs/tx": "^5.1.0", "axios": "^1.6.2", "bip39": "^3.0.4", "browser-passworder": "^2.0.3", @@ -52,7 +54,6 @@ "eth-hd-keyring": "^3.6.0", "eth-sig-util": "^3.0.1", "eth-simple-keyring": "^4.2.0", - "ethereumjs-tx": "^1.3.7", "ethereumjs-util": "^7.1.0", "hdkey": "^2.0.1", "loglevel": "^1.7.1", diff --git a/src/index.js b/src/index.js index 55ea058..6e7826d 100644 --- a/src/index.js +++ b/src/index.js @@ -2,13 +2,16 @@ const { EventEmitter } = require('events') const log = require('loglevel') const ethUtil = require('ethereumjs-util') -const Tx = require('ethereumjs-tx'); const bip39 = require('bip39') const ObservableStore = require('obs-store') const encryptor = require('browser-passworder') const { normalize: normalizeAddress } = require('eth-sig-util') +const { Common, Hardfork } =require('@ethereumjs/common') +const { FeeMarketEIP1559Transaction } = require('@ethereumjs/tx'); +const { bufferToHex } = require('ethereumjs-util') + const SimpleKeyring = require('eth-simple-keyring') const HdKeyring = require('eth-hd-keyring') @@ -254,27 +257,29 @@ class KeyringController extends EventEmitter { // SIGNING METHODS // - /** - * Sign Arbitrum Transaction - * - * Signs an Arbitrum transaction object. - * - * @param {Object} arbitrumTx - The transaction to sign. - * @param {Object} web3 - web3 object. - * @returns {string} The signed transaction raw string. - */ + /** + * Sign arbitrum Transaction + * + * Signs an arbitrum transaction object. + * + * @param {Object} rawTx - The transaction to sign. + * @returns {string} The signed transaction raw string. + */ - async signTransaction(arbitrumTx, privateKey) { - const tx = new Tx(arbitrumTx); + async signTransaction(rawTx, privateKey) { - const pkey = Buffer.from(privateKey, 'hex'); + const pkey = Buffer.from(privateKey, 'hex'); - tx.sign(pkey); + const common = Common.custom({ chainId: chainId }, { hardfork: Hardfork.London }) - const signedTx = `0x${tx.serialize().toString('hex')}`; + const tx = FeeMarketEIP1559Transaction.fromTxData(rawTx, { common }); - return signedTx; - } + const signedTransaction = tx.sign(pkey); + + const signedTx = bufferToHex(signedTransaction.serialize()); + + return signedTx +} /** * Sign Transaction or Message to get v,r,s diff --git a/test/index.js b/test/index.js index 4390759..cb05faf 100644 --- a/test/index.js +++ b/test/index.js @@ -66,7 +66,7 @@ describe('Initialize wallet ', () => { it("Create new vault and restore", async () => { const res = await arbitrumKeyring.createNewVaultAndRestore(PASSWORD, HD_WALLET_12_MNEMONIC) assert(arbitrumKeyring.keyrings[0].mnemonic === HD_WALLET_12_MNEMONIC, "Wrong mnemonic") - }) + }) it("Export account (privateKey)", async () => { const res = await arbitrumKeyring.getAccounts() @@ -82,21 +82,21 @@ describe('Initialize wallet ', () => { }) it("Get fees with manual gasLimit", async () => { - const accounts = await arbitrumKeyring.getAccounts() - const web3 = new Web3(TESTNET.URL); - const tx = { - gasLimit: 2100 - } - const fees = await arbitrumKeyring.getFees(tx, web3) - console.log(" with manual gasLimit ", fees) - - const privateKey = await arbitrumKeyring.exportAccount(accounts[0]) - console.log('privatekey:',privateKey) - const tx3 = await arbitrumKeyring.sign(TESTING_MESSAGE_1, privateKey, web3) - console.log("tx3 ", tx3) - }) - - it("Should import correct account ", async () => { + const accounts = await arbitrumKeyring.getAccounts() + const web3 = new Web3(TESTNET.URL); + const tx = { + gasLimit: 2100 + } + const fees = await arbitrumKeyring.getFees(tx, web3) + console.log(" with manual gasLimit ", fees) + + const privateKey = await arbitrumKeyring.exportAccount(accounts[0]) + console.log('privatekey:', privateKey) + const tx3 = await arbitrumKeyring.sign(TESTING_MESSAGE_1, privateKey, web3) + console.log("tx3 ", tx3) + }) + + it("Should import correct account ", async () => { const address = await arbitrumKeyring.importWallet(EXTERNAL_ACCOUNT_PRIVATE_KEY) assert(address.toLowerCase() === EXTERNAL_ACCOUNT_ADDRESS.toLowerCase(), "Wrong address") assert(arbitrumKeyring.importedWallets.length === 1, "Should have 1 imported wallet") @@ -121,4 +121,41 @@ describe('Initialize wallet ', () => { const getEstimate = await arbitrumKeyring.getFees(tx, web3) console.log(" get gas estimate ", getEstimate) }) + it("sign Transaction ", async () => { + + const accounts = await arbitrumKeyring.getAccounts() + const from = accounts[0] + const web3 = new Web3(TESTNET.URL); + + const count = await web3.eth.getTransactionCount(from); + + const defaultNonce = await web3.utils.toHex(count); + const to = '0x641BB2596D8c0b32471260712566BF933a2f1a8e' + + const getFeeEstimate = await arbitrumKeyring.getFees({ + from, to, + value: web3.utils.numberToHex(web3.utils.toWei('0', 'ether')), data: "0x00" + }, web3); + console.log(getFeeEstimate); + + + + const rawTx = { + to, + from, + value: web3.utils.numberToHex(web3.utils.toWei('0', 'ether')), + gasLimit: getFeeEstimate.gasLimit, + maxPriorityFeePerGas: getFeeEstimate.fees.slow.maxPriorityFeePerGas, + maxFeePerGas: getFeeEstimate.fees.slow.maxFeePerGas, + nonce: defaultNonce, + data: '0x', + type: 2, + chainId: TESTNET.CHAIN_ID, + }; + + const privateKey = await arbitrumKeyring.exportAccount(accounts[0]) + const signedTX = await arbitrumKeyring.signTransaction(rawTx, privateKey) + console.log("signedTX ", signedTX) + + }) }) \ No newline at end of file