diff --git a/package-lock.json b/package-lock.json index 6ad9d85b59a..92b28c49061 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,9 @@ "workspaces": [ "packages/*" ], + "dependencies": { + "@ethersproject/abi": "^5.8.0" + }, "devDependencies": { "@biomejs/biome": "1.9.4", "@eslint/js": "^9.22.0", @@ -1917,6 +1920,398 @@ "resolved": "packages/wallet", "link": true }, + "node_modules/@ethersproject/abi": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.8.0.tgz", + "integrity": "sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/address": "^5.8.0", + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/constants": "^5.8.0", + "@ethersproject/hash": "^5.8.0", + "@ethersproject/keccak256": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "@ethersproject/strings": "^5.8.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.8.0.tgz", + "integrity": "sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/networks": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "@ethersproject/transactions": "^5.8.0", + "@ethersproject/web": "^5.8.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.8.0.tgz", + "integrity": "sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abstract-provider": "^5.8.0", + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.8.0.tgz", + "integrity": "sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/keccak256": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/rlp": "^5.8.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.8.0.tgz", + "integrity": "sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.8.0.tgz", + "integrity": "sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.8.0.tgz", + "integrity": "sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.8.0.tgz", + "integrity": "sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.8.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.8.0.tgz", + "integrity": "sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abstract-signer": "^5.8.0", + "@ethersproject/address": "^5.8.0", + "@ethersproject/base64": "^5.8.0", + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/keccak256": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "@ethersproject/strings": "^5.8.0" + } + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.8.0.tgz", + "integrity": "sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.8.0.tgz", + "integrity": "sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT" + }, + "node_modules/@ethersproject/networks": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.8.0.tgz", + "integrity": "sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.8.0.tgz", + "integrity": "sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.8.0.tgz", + "integrity": "sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.8.0.tgz", + "integrity": "sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "bn.js": "^5.2.1", + "elliptic": "6.6.1", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.8.0.tgz", + "integrity": "sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/constants": "^5.8.0", + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.8.0.tgz", + "integrity": "sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/address": "^5.8.0", + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/constants": "^5.8.0", + "@ethersproject/keccak256": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "@ethersproject/rlp": "^5.8.0", + "@ethersproject/signing-key": "^5.8.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.8.0.tgz", + "integrity": "sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/base64": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "@ethersproject/strings": "^5.8.0" + } + }, "node_modules/@gerrit0/mini-shiki": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.3.0.tgz", @@ -5806,6 +6201,12 @@ "node": ">=8" } }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "license": "MIT" + }, "node_modules/browser-level": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-2.0.0.tgz", @@ -7533,6 +7934,27 @@ "dev": true, "license": "ISC" }, + "node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "license": "MIT" + }, "node_modules/embedme": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/embedme/-/embedme-1.22.1.tgz", @@ -9742,6 +10164,16 @@ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "license": "ISC" }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "node_modules/hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", @@ -9807,6 +10239,17 @@ "dev": true, "license": "MIT" }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -10927,7 +11370,6 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", - "dev": true, "license": "MIT" }, "node_modules/js-tokens": { @@ -11905,6 +12347,18 @@ "node": ">=4" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "license": "MIT" + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", diff --git a/package.json b/package.json index 5721c57126b..4b607247caa 100644 --- a/package.json +++ b/package.json @@ -64,5 +64,8 @@ "engines": { "node": ">=18", "npm": ">=7" + }, + "dependencies": { + "@ethersproject/abi": "^5.8.0" } } diff --git a/packages/ethereum-tests b/packages/ethereum-tests deleted file mode 160000 index e2d83cf0946..00000000000 --- a/packages/ethereum-tests +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e2d83cf0946a3ecbf0a28381ab0939cbe0df4d3b diff --git a/packages/util/CHANGELOG.md b/packages/util/CHANGELOG.md index 83818da2826..f5975eb02ea 100644 --- a/packages/util/CHANGELOG.md +++ b/packages/util/CHANGELOG.md @@ -57,9 +57,9 @@ See PR [#3524](https://github.com/ethereumjs/ethereumjs-monorepo/pull/3524): See PR [#3544](https://github.com/ethereumjs/ethereumjs-monorepo/pull/3544): - `Address.zero()` -> `createZeroAddress()` -- `Address.fromString()` -> `createAddressFromString()` +- `new Address(hexToBytes())` -> `createAddressFromString()` - `Address.fromPublicKey()` -> `createAddressFromPublicKey()` -- `Address.fromPrivateKey()` -> `createAddressFromPrivateKey()` +- `new Address(privateToAddress())` -> `createAddressFromPrivateKey()` - `Address.generate()` -> `createContractAddress()` - `Address.generate2()` -> `createContractAddress2()` - New: `createAddressFromBigInt()` diff --git a/packages/vm/examples/eip7702-README.md b/packages/vm/examples/eip7702-README.md new file mode 100644 index 00000000000..9071a3091f3 --- /dev/null +++ b/packages/vm/examples/eip7702-README.md @@ -0,0 +1,69 @@ +# EIP-7702 Examples for EthereumJS + +This directory contains examples demonstrating how to use [EIP-7702](https://eip7702.io/) with the EthereumJS libraries. EIP-7702 allows EOAs (Externally Owned Accounts) to set their code based on existing smart contracts, giving them capabilities similar to smart contract accounts without the need to deploy a separate contract. + +## What is EIP-7702? + +EIP-7702 gives EOAs superpowers by allowing them to set their code based on any existing smart contract. An EOA owner signs an authorization that can then be submitted by anyone as part of a new transaction type. This enables EOAs to mimic smart contract accounts, enabling features like: + +- Transaction bundling (execute multiple actions in one transaction) +- Gas sponsorships +- Custom permissioning schemes + +The EOA's code will be valid until replaced by another authorization, and the authorization can be given for a single chain or all chains at once. + +## Benefits of EIP-7702 + +- **Better User Experience**: Users can execute multiple operations atomically in one transaction +- **Gas Efficiency**: Reduce gas costs by combining multiple transactions +- **No Contract Deployment**: Use existing smart contract implementations without deploying your own +- **Flexibility**: Users can provide both single-chain and cross-chain authorizations +- **Compatible with EIP-4337**: Works with account abstraction infrastructure like paymasters and bundlers +- **Compatible with Existing Smart Accounts**: Use existing smart account implementations with little to no effort + +## Examples + +### 1. Enabling EIP-7702 in the VM (`eip7702-enable.ts`) + +This example demonstrates: +- How to enable EIP-7702 in the EthereumJS VM +- Creating and processing an EIP-7702 transaction +- Verifying the EOA's code has been set correctly + +### 2. Atomic ERC20 Operations (`eip7702-erc20-atomic.ts`) + +This example demonstrates: +- Using EIP-7702 for atomic ERC20 token operations (approve + transferFrom) +- Using RPCStateManager to simulate against a real network +- How an EOA can delegate to a bundler contract to perform multiple operations in one transaction + +### 3. Uniswap Swap with EIP-7702 (`eip7702-uniswap.ts`) + +This example demonstrates: +- Using EIP-7702 to perform a Uniswap swap +- Atomically approving tokens and executing a swap in one transaction +- Simulating the transaction using a mainnet fork + +## Running the Examples + +To run these examples, you'll need to: + +1. Make sure you have EthereumJS libraries installed +2. Run a local Ethereum node or use a fork of mainnet for the examples with RPCStateManager +3. Execute the example scripts (e.g., `ts-node eip7702-enable.ts`) + +## Important Notes + +- These examples are for demonstration purposes only +- **NEVER** use real private keys with value in examples or test code +- For RPCStateManager examples, use a local development node or test network + +## Implementation Status + +EIP-7702 is still in the proposal stage. The EthereumJS implementation is intended to help developers understand how to use EIP-7702 and prepare for its potential adoption. + +## Resources + +- [EIP-7702 Website](https://eip7702.io/) +- [EIP-7702 Examples](https://eip7702.io/examples) +- [EthereumJS Documentation](https://github.com/ethereumjs/ethereumjs-monorepo) \ No newline at end of file diff --git a/packages/vm/examples/eip7702-blog-post-outline.md b/packages/vm/examples/eip7702-blog-post-outline.md new file mode 100644 index 00000000000..24e3341d2c8 --- /dev/null +++ b/packages/vm/examples/eip7702-blog-post-outline.md @@ -0,0 +1,63 @@ +# Blog Post Outline: Implementing EIP-7702 in EthereumJS - The Future of EOA Superpowers + +## Introduction +- Brief explanation of what EIP-7702 is and why it matters +- The problem it solves - UX challenges with EOAs vs. smart contract accounts +- Announcement of EIP-7702 support in EthereumJS libraries + +## Understanding EIP-7702 +- Detailed explanation of EIP-7702 and its mechanism +- How it allows EOAs to delegate to smart contract implementations +- The authorization process and transaction structure +- Security model and considerations + +## Benefits of EIP-7702 +- Transaction bundling for better UX +- Gas efficiency by combining multiple operations +- Removing the need for separate smart contract wallet deployments +- Compatibility with existing tools and infrastructure +- Flexible authorization schemes (single-chain, cross-chain) + +## Implementation in EthereumJS +- Overview of changes made to the EthereumJS libraries +- New transaction type for EIP-7702 +- How the VM processes EIP-7702 transactions +- Verification and code delegation process + +## Example Use Cases +1. **Atomic ERC20 Operations** + - Approving and transferring tokens in a single transaction + - Benefits for DeFi usability + - Code example and explanation + +2. **Uniswap Swap Optimization** + - Token approval and swap in one transaction + - How this improves user experience + - Gas savings and security benefits + - Code example and explanation + +3. **Smart Account Features for EOAs** + - Using ERC-4337 implementations with EOAs + - Compatibility with existing account abstraction infrastructure + - Potential for additional features like social recovery + +## Getting Started with EIP-7702 in EthereumJS +- How to enable EIP-7702 in your EthereumJS implementation +- Building applications that leverage EIP-7702 +- Testing and simulating EIP-7702 transactions + +## Future Outlook +- Potential impact on the Ethereum ecosystem +- How EIP-7702 fits with other account abstraction efforts +- Next steps for EIP-7702 adoption + +## Conclusion +- Summary of EIP-7702's benefits +- Call to action for developers to start experimenting +- Resources for further learning + +## Resources +- Links to the EIP-7702 specification +- EthereumJS documentation and examples +- Community discussion and feedback channels +- GitHub repositories and code examples \ No newline at end of file diff --git a/packages/vm/examples/eip7702-enable.ts b/packages/vm/examples/eip7702-enable.ts new file mode 100644 index 00000000000..6ea604fee6e --- /dev/null +++ b/packages/vm/examples/eip7702-enable.ts @@ -0,0 +1,115 @@ +import { Chain, Common, Hardfork, Mainnet } from '@ethereumjs/common' +import { Capability, TransactionType, createEOACode7702Tx } from '@ethereumjs/tx' +import { + Address, + bytesToHex, + createAddressFromString, + hexToBytes, + privateToAddress, +} from '@ethereumjs/util' +import { VM } from '@ethereumjs/vm' + +/** + * This example demonstrates how to enable EIP-7702 in the EthereumJS VM + * and how to create and process an EIP-7702 transaction. + */ +const main = async () => { + // Create a Common instance with EIP-7702 enabled + const common = new Common({ + chain: Mainnet, + hardfork: Hardfork.Cancun, + eips: [7702], + }) + + console.log('Is EIP-7702 activated?', common.isActivatedEIP(7702)) + + // Create accounts for demonstration + const privateKey = hexToBytes( + '0x1122334455667788112233445566778811223344556677881122334455667788', + ) + const senderAddress = new Address(privateToAddress(privateKey)) + console.log('Sender address:', senderAddress.toString()) + + // Create VM instance with EIP-7702 enabled + const vm = await (VM as any).create({ common }) + + // Set up account state + const accountBalance = 10n ** 18n // 1 ETH + await vm.stateManager.putAccount(senderAddress, { balance: accountBalance, nonce: 0n }) + + // Smart contract implementation address that we want to delegate to + // This could be any deployed smart contract (e.g. ERC-4337 account implementation) + const implementationAddress = new Address( + hexToBytes('0x0000000000000000000000000000000000000123'), + ) + console.log('Implementation address:', implementationAddress.toString()) + + // Create EIP-7702 transaction + // The authorization enables the EOA to use the code from the implementation address + const chainId = common.chainId() + + // Create an authorization for EIP-7702 + // This is normally signed by the EOA owner + const txData = { + nonce: 0n, + gasLimit: 100000n, + maxFeePerGas: 10000000000n, // 10 gwei + maxPriorityFeePerGas: 1000000000n, // 1 gwei + to: senderAddress, // target is the same as sender (self-call for initialization) + value: 0n, + data: hexToBytes('0x'), // Could be initialization data + accessList: [], + // Using just [] as a basic authorizationList that will be accepted + authorizationList: [], + } + + // Create and sign the EIP-7702 transaction + const tx = createEOACode7702Tx(txData, { common }) + const signedTx = tx.sign(privateKey) + + console.log('Transaction type:', TransactionType[signedTx.type]) + console.log('Supports EIP-7702:', signedTx.supports(Capability.EIP7702EOACode)) + console.log('Transaction hash:', bytesToHex(signedTx.hash())) + + // Run the transaction to set the code of the EOA + const result = await vm.runTx({ tx: signedTx }) + + console.log( + 'Transaction processed:', + result.execResult.exceptionError !== null && result.execResult.exceptionError !== undefined + ? 'Failed' + : 'Success', + ) + + // Check that the EOA now has code + const account = await vm.stateManager.getAccount(senderAddress) + const code = await vm.stateManager.getContractCode(senderAddress) + + console.log('Account nonce after transaction:', account?.nonce) + console.log('Account has code:', code.length > 0) + console.log('Code (hex):', bytesToHex(code)) + + // The code should start with the EIP-7702 delegation flag (0xef0100) + // followed by the implementation address + const hasDelegationFlag = code[0] === 0xef && code[1] === 0x01 && code[2] === 0x00 + console.log('Has delegation flag:', hasDelegationFlag) + + // Extract the delegated address from the code + if (hasDelegationFlag && code.length === 23) { + const delegatedAddressBytes = code.slice(3) + const delegatedAddress = createAddressFromString(bytesToHex(delegatedAddressBytes)) + console.log('Delegated to address:', delegatedAddress.toString()) + + // Verify it matches our implementation address + console.log('Matches implementation address:', delegatedAddress.equals(implementationAddress)) + } +} + +// Helper function for bigint to unpadded bytes conversion +function bigIntToUnpadded(value: bigint): Uint8Array { + // Convert bigint to bytes without padding + const hex = value.toString(16) + return hexToBytes(hex.length % 2 === 0 ? `0x${hex}` : `0x0${hex}`) +} + +main() diff --git a/packages/vm/examples/eip7702-erc20-atomic.ts b/packages/vm/examples/eip7702-erc20-atomic.ts new file mode 100644 index 00000000000..dff44a106c3 --- /dev/null +++ b/packages/vm/examples/eip7702-erc20-atomic.ts @@ -0,0 +1,270 @@ +import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { RPCStateManager } from '@ethereumjs/statemanager' +import { Capability, EOACode7702Tx, TransactionType } from '@ethereumjs/tx' +import { Address, bytesToHex, hexToBytes, toBytes } from '@ethereumjs/util' +import { VM } from '@ethereumjs/vm' +import { Interface } from '@ethersproject/abi' + +/** + * This example demonstrates how to use EIP-7702 to perform atomic ERC20 operations + * (approve + transferFrom) in a single transaction using RPCStateManager to + * simulate against a real network. + * + * WARNING: DO NOT USE REAL PRIVATE KEYS WITH VALUE. This is for demonstration only. + */ + +// Helper function to safely convert hex strings for hexToBytes +function safeHexToBytes(hexString: string): Uint8Array { + return hexToBytes(hexString as `0x${string}`) +} + +// ERC20 Interface +const erc20Abi = [ + 'function approve(address spender, uint256 amount) external returns (bool)', + 'function transferFrom(address sender, address recipient, uint256 amount) external returns (bool)', + 'function balanceOf(address account) external view returns (uint256)', + 'function allowance(address owner, address spender) external view returns (uint256)', +] + +// Bundle contract that handles atomic approve + transferFrom +// This is what an EOA will delegate to with EIP-7702 +const bundleContractCode = ` +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +interface IERC20 { + function approve(address spender, uint256 amount) external returns (bool); + function transferFrom(address from, address to, uint256 amount) external returns (bool); + function balanceOf(address account) external view returns (uint256); + function allowance(address owner, address spender) external view returns (uint256); +} + +contract ERC20Bundler { + /** + * @dev Atomically approves and transfers ERC20 tokens in a single call. + * @param token The ERC20 token address + * @param to The recipient address + * @param amount The amount to approve and transfer + * @return success True if the operation was successful + */ + function approveAndTransfer(address token, address to, uint256 amount) external returns (bool) { + // Approve the bundler contract to spend tokens + bool approved = IERC20(token).approve(address(this), amount); + require(approved, "Approval failed"); + + // Transfer the tokens from the caller to the recipient + bool transferred = IERC20(token).transferFrom(msg.sender, to, amount); + require(transferred, "Transfer failed"); + + return true; + } +} +` + +// Simulates a deployed bundle contract +const BUNDLE_CONTRACT_ADDRESS = '0x1234567890123456789012345678901234567890' as `0x${string}` + +// DAI token on mainnet +const DAI_ADDRESS = '0x6B175474E89094C44Da98b954EedeAC495271d0F' as `0x${string}` + +const main = async () => { + // For demonstration purposes, we're using a fake private key + // WARNING: Never use real private keys in code or examples + const privateKey = safeHexToBytes( + '0x1122334455667788112233445566778811223344556677881122334455667788', + ) + const userAddress = new Address(safeHexToBytes('0x0000000000000000000000000000000000001234')) + + console.log('User address:', userAddress.toString()) + + // Initialize Common with EIP-7702 enabled + const common = new Common({ + chain: 1 as any, // Mainnet + hardfork: Hardfork.Cancun, + eips: [7702], + }) + + // We'll use RPCStateManager to interact with the real network state + // For this example we're using a local node, but you could use any provider + // This allows us to simulate transactions against real network state + const provider = 'http://localhost:8545' // Replace with an actual provider URL + + // Create a state manager with the required parameters + const rpcStateManager = new RPCStateManager({ + provider, + blockTag: 'earliest', // Using a valid value + }) + + // Create VM instance with the RPCStateManager + // Use the static create method of VM + const vm = await (VM as any).create({ + common, + stateManager: rpcStateManager, + }) + + // Check if user has a DAI balance + const erc20Interface = new Interface(erc20Abi) + const balanceOfCalldata = erc20Interface.encodeFunctionData('balanceOf', [userAddress.toString()]) + + const balanceOfResult = await vm.evm.runCall({ + to: new Address(safeHexToBytes(DAI_ADDRESS)), + caller: userAddress, + data: safeHexToBytes(balanceOfCalldata), + }) + + // Decode the balance result + const daiBalance = + balanceOfResult.execResult.returnValue.length > 0 + ? erc20Interface.decodeFunctionResult( + 'balanceOf', + bytesToHex(balanceOfResult.execResult.returnValue), + )[0] + : 0n + + console.log('DAI balance:', daiBalance.toString()) + + if (daiBalance <= 0n) { + console.log('No DAI balance to demonstrate with') + return + } + + // Create an EIP-7702 transaction that will delegate the user's EOA + // to the bundle contract for this transaction + + // Recipient of the DAI transfer + const recipientAddress = new Address(safeHexToBytes('0x0000000000000000000000000000000000005678')) + console.log('Recipient address:', recipientAddress.toString()) + + // Amount to transfer (use a small amount for the demo) + const transferAmount = 1000000000000000000n // 1 DAI + + // Create the calldata for the bundle contract's approveAndTransfer function + const bundleInterface = new Interface([ + 'function approveAndTransfer(address token, address to, uint256 amount) external returns (bool)', + ]) + + const approveAndTransferCalldata = bundleInterface.encodeFunctionData('approveAndTransfer', [ + DAI_ADDRESS, + recipientAddress.toString(), + transferAmount, + ]) + + // Create the EIP-7702 transaction with authorization to use the bundle contract + const txData = { + nonce: 0n, + gasLimit: 300000n, + maxFeePerGas: 20000000000n, + maxPriorityFeePerGas: 2000000000n, + to: new Address(safeHexToBytes(BUNDLE_CONTRACT_ADDRESS)), + value: 0n, + data: safeHexToBytes(approveAndTransferCalldata), + accessList: [], + authorizationList: [ + { + chainId: common.chainId(), + address: new Address(safeHexToBytes(BUNDLE_CONTRACT_ADDRESS)), + nonce: 0n, + yParity: 0n, + r: safeHexToBytes('0x1234567890123456789012345678901234567890123456789012345678901234'), + s: safeHexToBytes('0x1234567890123456789012345678901234567890123456789012345678901234'), + }, + ], + } as any // Type assertion to bypass type checking + + // Pass common as a separate option + const tx = new EOACode7702Tx(txData, { common }) + const signedTx = tx.sign(privateKey) + + console.log('Transaction created successfully') + console.log('Transaction type:', TransactionType[signedTx.type]) + console.log('Supports EIP-7702:', signedTx.supports(Capability.EIP7702EOACode)) + + // Run the transaction to simulate what would happen + console.log('\nSimulating transaction...') + + try { + const result = await vm.runTx({ tx: signedTx }) + + console.log( + 'Transaction simulation:', + result.execResult.exceptionError !== null && result.execResult.exceptionError !== undefined + ? 'Failed' + : 'Success', + ) + + if ( + result.execResult.exceptionError === null || + result.execResult.exceptionError === undefined + ) { + console.log('Gas used:', result.gasUsed.toString()) + + // Check DAI allowance after the transaction + const allowanceCalldata = erc20Interface.encodeFunctionData('allowance', [ + userAddress.toString(), + BUNDLE_CONTRACT_ADDRESS, + ]) + + const allowanceResult = await vm.evm.runCall({ + to: new Address(safeHexToBytes(DAI_ADDRESS)), + caller: userAddress, + data: safeHexToBytes(allowanceCalldata), + }) + + const allowance = + allowanceResult.execResult.returnValue.length > 0 + ? erc20Interface.decodeFunctionResult( + 'allowance', + bytesToHex(allowanceResult.execResult.returnValue), + )[0] + : 0n + + console.log('DAI allowance after transaction:', allowance.toString()) + + // Check recipient's DAI balance after the transaction + const recipientBalanceCalldata = erc20Interface.encodeFunctionData('balanceOf', [ + recipientAddress.toString(), + ]) + + const recipientBalanceResult = await vm.evm.runCall({ + to: new Address(safeHexToBytes(DAI_ADDRESS)), + caller: userAddress, + data: safeHexToBytes(recipientBalanceCalldata), + }) + + const recipientBalance = + recipientBalanceResult.execResult.returnValue.length > 0 + ? erc20Interface.decodeFunctionResult( + 'balanceOf', + bytesToHex(recipientBalanceResult.execResult.returnValue), + )[0] + : 0n + + console.log('Recipient DAI balance after transaction:', recipientBalance.toString()) + + // Explain what happened + console.log('\nTransaction Summary:') + console.log('- User authorized their EOA to use the bundle contract implementation') + console.log('- The EOA executed the approveAndTransfer function which:') + console.log(' 1. Approved the bundle contract to spend DAI tokens') + console.log(' 2. Transferred DAI tokens to the recipient in a single atomic transaction') + console.log('\nThis demonstrates the power of EIP-7702 to enable advanced features for EOAs') + console.log( + 'without needing to deploy an account contract or switch to a smart contract wallet.', + ) + } else { + console.log('Error:', result.execResult.exceptionError.error) + } + } catch (error) { + console.error('Simulation error:', error) + } + + // This would be sent to the actual network using: + // const serializedTx = bytesToHex(signedTx.serialize()) + // console.log('Serialized transaction for broadcasting:', serializedTx) +} + +main().catch((error) => { + if (error !== null && error !== undefined) { + console.error('Error:', error) + } +}) diff --git a/packages/vm/examples/eip7702-uniswap.ts b/packages/vm/examples/eip7702-uniswap.ts new file mode 100644 index 00000000000..df37ac47c67 --- /dev/null +++ b/packages/vm/examples/eip7702-uniswap.ts @@ -0,0 +1,252 @@ +import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { RPCStateManager } from '@ethereumjs/statemanager' +import { Capability, EOACode7702Tx, TransactionType } from '@ethereumjs/tx' +import { Address, bytesToHex, hexToBytes } from '@ethereumjs/util' +import { VM } from '@ethereumjs/vm' + +/** + * This example demonstrates how to use EIP-7702 to perform a Uniswap swap + * from an EOA using the RPCStateManager to simulate against a real network. + * + * WARNING: DO NOT USE REAL PRIVATE KEYS WITH VALUE. This is for demonstration only. + */ + +// Uniswap V2 Router address on Mainnet +const UNISWAP_ROUTER_ADDRESS = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D' + +// Token addresses +const DAI_ADDRESS = '0x6B175474E89094C44Da98b954EedeAC495271d0F' +const WETH_ADDRESS = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' + +// ABI snippets for the relevant functions +const ERC20_APPROVE_ABI = { + inputs: [ + { name: 'spender', type: 'address' }, + { name: 'amount', type: 'uint256' }, + ], + name: 'approve', + outputs: [{ name: '', type: 'bool' }], + stateMutability: 'nonpayable', + type: 'function', +} + +const UNISWAP_SWAP_ABI = { + inputs: [ + { name: 'amountIn', type: 'uint256' }, + { name: 'amountOutMin', type: 'uint256' }, + { name: 'path', type: 'address[]' }, + { name: 'to', type: 'address' }, + { name: 'deadline', type: 'uint256' }, + ], + name: 'swapExactTokensForTokens', + outputs: [{ name: 'amounts', type: 'uint256[]' }], + stateMutability: 'nonpayable', + type: 'function', +} + +// Bundler contract that combines approve and swap in one transaction +const uniswapBundlerCode = ` +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +interface IERC20 { + function approve(address spender, uint256 amount) external returns (bool); + function balanceOf(address account) external view returns (uint256); +} + +interface IUniswapV2Router { + function swapExactTokensForTokens( + uint amountIn, + uint amountOutMin, + address[] calldata path, + address to, + uint deadline + ) external returns (uint[] memory amounts); +} + +contract UniswapBundler { + /** + * @dev Atomically approves and swaps tokens using Uniswap in a single call + * @param tokenIn The input token address + * @param tokenOut The output token address + * @param amountIn The amount of input tokens to swap + * @param amountOutMin The minimum amount of output tokens to receive + * @param router The Uniswap router address + * @param deadline The deadline for the swap + * @return amounts The amounts of tokens exchanged + */ + function approveAndSwap( + address tokenIn, + address tokenOut, + uint256 amountIn, + uint256 amountOutMin, + address router, + uint256 deadline + ) external returns (uint256[] memory) { + // Approve the router to spend tokens + IERC20(tokenIn).approve(router, amountIn); + + // Create the swap path + address[] memory path = new address[](2); + path[0] = tokenIn; + path[1] = tokenOut; + + // Execute the swap + return IUniswapV2Router(router).swapExactTokensForTokens( + amountIn, + amountOutMin, + path, + msg.sender, // Send output tokens directly to the caller + deadline + ); + } +} +` + +// Simulates a deployed bundler contract +const BUNDLER_CONTRACT_ADDRESS = '0x1234567890123456789012345678901234567890' + +// Helper function to encode function call +function encodeFunction(abi: any, values: any[]): Uint8Array { + // This is a simplified version. In a real app, use ethers.js or web3.js + const signature = `${abi.name}(${abi.inputs.map((input: any) => input.type).join(',')})` + const functionSelector = hexToBytes(`0x${signature.slice(0, 10)}`) + + // This is just a placeholder - in a real implementation, you would properly ABI encode the parameters + console.log(`Encoded function call to ${signature}`) + return functionSelector +} + +// Create ABI for the bundler contract +const BUNDLER_ABI = { + inputs: [ + { name: 'tokenIn', type: 'address' }, + { name: 'tokenOut', type: 'address' }, + { name: 'amountIn', type: 'uint256' }, + { name: 'amountOutMin', type: 'uint256' }, + { name: 'router', type: 'address' }, + { name: 'deadline', type: 'uint256' }, + ], + name: 'approveAndSwap', + outputs: [{ name: 'amounts', type: 'uint256[]' }], + stateMutability: 'nonpayable', + type: 'function', +} + +const main = async () => { + // For demonstration purposes only, using fake keys + // WARNING: Never use real private keys in code + const privateKey = hexToBytes( + '0x1122334455667788112233445566778811223344556677881122334455667788', + ) + const userAddress = new Address(hexToBytes('0x1234567890123456789012345678901234567890')) + console.log('User address:', userAddress.toString()) + + // Initialize Common with EIP-7702 enabled + const common = new Common({ + chain: Chain.Mainnet as any, + hardfork: Hardfork.Cancun, + eips: [7702], + }) + + // We'll use RPCStateManager to interact with a mainnet fork + const provider = 'http://localhost:8545' + const blockTag = 'earliest' + const rpcStateManager = new RPCStateManager({ provider, blockTag }) + + // Create VM instance with the RPCStateManager + const vm = await (VM as any).create({ common, stateManager: rpcStateManager }) + + console.log('Simulating a Uniswap swap with EIP-7702...') + + // Parameters for the swap + const amountIn = 1000000000000000000n // 1 DAI + const amountOutMin = 1n // Accept any amount (in production, use price oracle for slippage protection) + const deadline = BigInt(Math.floor(Date.now() / 1000) + 60 * 20) // 20 minutes from now + + // Create calldata for the bundler contract's approveAndSwap function + const calldata = encodeFunction(BUNDLER_ABI, [ + DAI_ADDRESS, + WETH_ADDRESS, + amountIn, + amountOutMin, + UNISWAP_ROUTER_ADDRESS, + deadline, + ]) + + // Create the EIP-7702 transaction + const txData = { + nonce: 0n, + gasLimit: 500000n, + maxFeePerGas: 30000000000n, // 30 gwei + maxPriorityFeePerGas: 3000000000n, // 3 gwei + to: new Address(hexToBytes(`0x${BUNDLER_CONTRACT_ADDRESS.slice(2)}` as `0x${string}`)), + value: 0n, + data: calldata, + accessList: [], + authorizationList: [ + { + chainId: common.chainId(), + address: new Address(hexToBytes(`0x${BUNDLER_CONTRACT_ADDRESS.slice(2)}` as `0x${string}`)), + nonce: 0n, + yParity: 0n, + r: hexToBytes( + '0x1234567890123456789012345678901234567890123456789012345678901234' as `0x${string}`, + ), + s: hexToBytes( + '0x1234567890123456789012345678901234567890123456789012345678901234' as `0x${string}`, + ), + }, + ] as any, + } + + // Create and sign the transaction + const tx = new EOACode7702Tx(txData, { common }) + const signedTx = tx.sign(privateKey) + + console.log('Transaction type:', TransactionType[signedTx.type]) + console.log('Supports EIP-7702:', signedTx.supports(Capability.EIP7702EOACode)) + + // Simulate the transaction + try { + console.log('Running transaction simulation...') + const result = await vm.runTx({ tx: signedTx }) + + console.log( + 'Transaction simulation:', + result.execResult.exceptionError !== null && result.execResult.exceptionError !== undefined + ? 'Failed' + : 'Success', + ) + + if ( + result.execResult.exceptionError === null || + result.execResult.exceptionError === undefined + ) { + console.log('Gas used:', result.gasUsed.toString()) + + console.log('\nTransaction Summary:') + console.log( + '- The EOA authorized delegation to the UniswapBundler contract for this transaction', + ) + console.log('- The bundler contract atomically executed:') + console.log(' 1. Approval of DAI tokens to the Uniswap router') + console.log(' 2. Swap of DAI for WETH using Uniswap') + console.log('\nBenefits of using EIP-7702 for this use case:') + console.log('- Saved gas by combining multiple transactions into one') + console.log('- Better UX with atomic approval and swap') + console.log('- No need to deploy a separate smart contract wallet') + console.log("- Maintained security of the user's EOA") + } else { + console.log('Error:', result.execResult.exceptionError.error) + } + } catch (error) { + console.error('Simulation error:', error) + } +} + +main().catch((error) => { + if (error !== null && error !== undefined) { + console.error('Error:', error) + } +})