From d17a82c828f942d3361742e5a3f0e082d1ae8bf7 Mon Sep 17 00:00:00 2001 From: mark Date: Thu, 14 Aug 2025 18:00:45 +0800 Subject: [PATCH 1/2] feat(script): add on chain --- .gitignore | 3 +- hardhat-version/README.md | 96 ++++ hardhat-version/deployments/contract.json | 3 + hardhat-version/package-lock.json | 255 +++++++++- hardhat-version/package.json | 3 +- .../scripts/1-7702-mint-transfer.js | 458 ++++++++++++++++++ 6 files changed, 813 insertions(+), 5 deletions(-) create mode 100644 hardhat-version/deployments/contract.json create mode 100644 hardhat-version/scripts/1-7702-mint-transfer.js diff --git a/.gitignore b/.gitignore index 256a18b..d2bf45f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ docs/ # Dotenv file .env hardhat-version/node_modules/ -hardhat-version/artifacts \ No newline at end of file +hardhat-version/artifacts +.idea \ No newline at end of file diff --git a/hardhat-version/README.md b/hardhat-version/README.md index 1eef2df..86006cb 100644 --- a/hardhat-version/README.md +++ b/hardhat-version/README.md @@ -176,6 +176,102 @@ networks: { } ``` +## 链上交互 + +### 转账运行 +```angular2html + ts-node scripts/1-7702-mint-transfer.js +(node:93249) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///Users/mark/Dev/self/eip-7702/hardhat-version/scripts/1-7702-mint-transfer.js is not specified and it doesn't parse as CommonJS. +Reparsing as ES module because module syntax was detected. This incurs a performance overhead. +To eliminate this warning, add "type": "module" to /Users/mark/Dev/self/eip-7702/hardhat-version/package.json. +(Use `node --trace-warnings ...` to show where the warning was created) +🚀 Alice EIP-7702 助手 - BSC主网 +💡 核心特性: Alice地址变成智能账户,一笔交易完成mint+transfer +⛽ Gas价格: 0.11 gwei (提升版) +✨ 简化版: 无需permit/approve,直接mint+transfer +🔄 智能处理: 自动解决EIP-7702授权冲突 +🔁 完整流程: 取消冲突授权→重新授权→执行业务→取消授权 + +👥 参与者: +Alice: 0x28984635f3adaf50C9cAD16bb3e444ceECC57dEA +Relayer: 0x8E4F1a52C38A9A26Fa78e5Eb5127c098cFc3e2db +BatchContract: 0x01CFCFd8FB0C4BF9abB4Fd8a449DdF48c94e86D4 + +📊 初始状态: +Alice代币: 0 +Relayer代币: 1002000 +Alice BNB: 0 +Alice代码长度: 0 +Alice代码: 0x +Batch代码长度: 3936 +期望的EIP-7702代码: 0xef010001cfcfd8fb0c4bf9abb4fd8a449ddf48c94e86d4 +Alice实际代码: 0x +代码匹配: false +Alice是智能账户: undefined + +🔐 步骤1: Alice首次EIP-7702授权 + +🔐 步骤1.3: Alice签名新的EIP-7702授权 (离线) +✅ Alice已签名EIP-7702授权 + 目标合约地址: 0x01CFCFd8FB0C4BF9abB4Fd8a449DdF48c94e86D4 + +✅ 跳过ERC20 Permit步骤 (智能账户直接transfer,无需授权) + +🔨 步骤2: 构建批量调用 +✅ 批量调用已构建 (2个操作): + 1. Mint 1000 代币给Alice + 2. Transfer 500代币给Relayer (无需授权,直接转账) + +⏳ 步骤3: 准备批量操作签名 (稍后进行) + +📤 步骤4: Relayer发送EIP-7702授权交易 +💡 这将使Alice地址变成BatchCallAndSponsor智能账户 +✅ EIP-7702授权交易已发送: 0x51dbbce2e4362565d04244c4ce85c594ac37985468948ffda3ad7c26892e3008 +✅ Alice地址现在是BatchCallAndSponsor智能账户! + +✍️ 步骤5: Alice签名批量操作 (现在Alice是正确的智能账户) + 使用初始nonce: 0 (新的EIP-7702智能账户) +✅ Alice已签名批量操作 (使用正确的nonce) + +⚡ 步骤6: Relayer执行批量操作 +💫 一笔交易完成: mint 1000 + transfer 500 +finalAliceCode: 0xef010001cfcfd8fb0c4bf9abb4fd8a449ddf48c94e86d4 +✅ 批量操作已发送: 0x91b0375c64bd765a4e7d8dee1c042c3be949f7fe769b0083cfe2166ddfc03865 +🎉 批量操作完成! Gas使用: 110172 + +📊 最终结果: +Alice代币: 500 +Relayer代币: 1002500 + +🎯 变化: +Alice净获得: 500 代币 +Relayer获得: 500 代币 + +🎉🎉🎉 成功! EIP-7702演示完成! +✨ Alice获得500代币,Relayer获得500代币 +✨ Alice全程只签名,无需发送交易 +✨ 一笔交易完成mint+transfer + +🔍 查看交易: https://bscscan.com/tx/0x91b0375c64bd765a4e7d8dee1c042c3be949f7fe769b0083cfe2166ddfc03865 + +🔄 步骤7: 可选的取消授权 (将Alice恢复为普通EOA) +💡 这样Alice地址可以被重新用于其他EIP-7702授权 + +🚫 步骤7.1: Alice签名取消授权 (离线) +✅ Alice已签名取消授权 + +📤 步骤7.2: Relayer发送取消授权交易 +✅ 最终取消授权交易已发送: 0xd2be58649292841e607a3f4cc84d88de11507235e07ea2dd0628cacf0b56aef0 + +📊 最终授权状态: +Alice代码: 0x +Alice是普通EOA: true +✅ Alice已成功恢复为普通EOA,可用于下次EIP-7702授权 + +🔍 查看取消授权交易: https://bscscan.com/tx/0xd2be58649292841e607a3f4cc84d88de11507235e07ea2dd0628cacf0b56aef0 + +``` + ## 📚 相关资源 - [EIP-7702规范](https://eips.ethereum.org/EIPS/eip-7702) diff --git a/hardhat-version/deployments/contract.json b/hardhat-version/deployments/contract.json new file mode 100644 index 0000000..55a07ff --- /dev/null +++ b/hardhat-version/deployments/contract.json @@ -0,0 +1,3 @@ +{ + "BatchCallAndSponsor": "0x01CFCFd8FB0C4BF9abB4Fd8a449DdF48c94e86D4" +} \ No newline at end of file diff --git a/hardhat-version/package-lock.json b/hardhat-version/package-lock.json index 34caf75..e7a6952 100644 --- a/hardhat-version/package-lock.json +++ b/hardhat-version/package-lock.json @@ -9,7 +9,8 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "ethers": "^6.8.0" + "ethers": "^6.8.0", + "viem": "^2.25.1" }, "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^4.0.0", @@ -905,6 +906,18 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@noble/curves": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/@noble/curves/-/curves-1.2.0.tgz", @@ -1277,7 +1290,6 @@ "version": "1.2.6", "resolved": "https://registry.npmmirror.com/@scure/base/-/base-1.2.6.tgz", "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", - "dev": true, "license": "MIT", "funding": { "url": "https://paulmillr.com/funding/" @@ -1731,6 +1743,27 @@ "license": "ISC", "peer": true }, + "node_modules/abitype": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz", + "integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", @@ -3498,6 +3531,12 @@ "license": "MIT", "peer": true }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, "node_modules/evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmmirror.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", @@ -4723,6 +4762,21 @@ "license": "ISC", "peer": true }, + "node_modules/isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, "node_modules/js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmmirror.com/js-sha3/-/js-sha3-0.8.0.tgz", @@ -5410,6 +5464,96 @@ "node": ">=0.10.0" } }, + "node_modules/ox": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.8.6.tgz", + "integrity": "sha512-eiKcgiVVEGDtEpEdFi1EGoVVI48j6icXHce9nFwCNM7CKG3uoCXKdr4TPhS00Iy1TR2aWSF1ltPD0x/YgqIL9w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "^1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.0.8", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ox/node_modules/@adraffy/ens-normalize": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz", + "integrity": "sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==", + "license": "MIT" + }, + "node_modules/ox/node_modules/@noble/curves": { + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.6.tgz", + "integrity": "sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ox/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ox/node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ox/node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", @@ -7314,7 +7458,7 @@ "version": "5.9.2", "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.2.tgz", "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "peer": true, "bin": { @@ -7425,6 +7569,111 @@ "license": "MIT", "peer": true }, + "node_modules/viem": { + "version": "2.33.3", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.33.3.tgz", + "integrity": "sha512-aWDr6i6r3OfNCs0h9IieHFhn7xQJJ8YsuA49+9T5JRyGGAkWhLgcbLq2YMecgwM7HdUZpx1vPugZjsShqNi7Gw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.9.2", + "@noble/hashes": "1.8.0", + "@scure/bip32": "1.7.0", + "@scure/bip39": "1.6.0", + "abitype": "1.0.8", + "isows": "1.0.7", + "ox": "0.8.6", + "ws": "8.18.2" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/viem/node_modules/@noble/curves": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.2.tgz", + "integrity": "sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/ws": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/web3-utils": { "version": "1.10.4", "resolved": "https://registry.npmmirror.com/web3-utils/-/web3-utils-1.10.4.tgz", diff --git a/hardhat-version/package.json b/hardhat-version/package.json index dfd5751..0632457 100644 --- a/hardhat-version/package.json +++ b/hardhat-version/package.json @@ -20,6 +20,7 @@ "hardhat-gas-reporter": "^1.0.9" }, "dependencies": { - "ethers": "^6.8.0" + "ethers": "^6.8.0", + "viem": "^2.25.1" } } diff --git a/hardhat-version/scripts/1-7702-mint-transfer.js b/hardhat-version/scripts/1-7702-mint-transfer.js new file mode 100644 index 0000000..af1bd2f --- /dev/null +++ b/hardhat-version/scripts/1-7702-mint-transfer.js @@ -0,0 +1,458 @@ +import { + createWalletClient, + createPublicClient, + http, + parseEther, + formatEther, + encodeFunctionData, + encodePacked, + keccak256, + parseGwei, + toHex, + zeroAddress +} from 'viem'; +import { privateKeyToAccount } from 'viem/accounts'; +import { bsc } from 'viem/chains'; + +// 配置 - BSC主网 +const ALICE_PRIVATE_KEY = "0x3bb30d1aa3c71712875c73d5ec90206d798b81e55379ae2f0792600308277d22"; +const RELAYER_PRIVATE_KEY = ""; +const BATCH_CONTRACT = "0x01CFCFd8FB0C4BF9abB4Fd8a449DdF48c94e86D4"; +const ERC20_CONTRACT = "0xD246EbaF9B5d3B28914AA3b20699150CFBE96Bca"; + +const MINT_AMOUNT = parseEther("1000"); +const TRANSFER_AMOUNT = parseEther("500"); +const GAS_PRICE = parseGwei('0.11'); // 0.11 gwei + +// ERC20 ABI (基础功能) +const ERC20_ABI = [ + { + name: 'balanceOf', + type: 'function', + inputs: [{ name: 'owner', type: 'address' }], + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view' + }, + { + name: 'mint', + type: 'function', + inputs: [ + { name: 'to', type: 'address' }, + { name: 'amount', type: 'uint256' } + ], + outputs: [], + stateMutability: 'nonpayable' + }, + { + name: 'approve', + type: 'function', + inputs: [ + { name: 'spender', type: 'address' }, + { name: 'amount', type: 'uint256' } + ], + outputs: [{ name: '', type: 'bool' }], + stateMutability: 'nonpayable' + }, + { + name: 'transfer', + type: 'function', + inputs: [ + { name: 'to', type: 'address' }, + { name: 'amount', type: 'uint256' } + ], + outputs: [{ name: '', type: 'bool' }], + stateMutability: 'nonpayable' + }, + { + name: 'transferFrom', + type: 'function', + inputs: [ + { name: 'from', type: 'address' }, + { name: 'to', type: 'address' }, + { name: 'amount', type: 'uint256' } + ], + outputs: [{ name: '', type: 'bool' }], + stateMutability: 'nonpayable' + } +]; + +const BATCH_ABI = [ + { + name: 'execute', + type: 'function', + inputs: [ + { + name: 'calls', + type: 'tuple[]', + components: [ + { name: 'to', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'data', type: 'bytes' } + ] + }, + { name: 'signature', type: 'bytes' } + ], + outputs: [], + stateMutability: 'payable' + }, + { + name: 'nonce', + type: 'function', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view' + } +]; + +async function main() { + console.log("🚀 Alice EIP-7702 助手 - BSC主网"); + console.log("💡 核心特性: Alice地址变成智能账户,一笔交易完成mint+transfer"); + console.log("⛽ Gas价格: 0.11 gwei (提升版)"); + console.log("✨ 简化版: 无需permit/approve,直接mint+transfer"); + console.log("🔄 智能处理: 自动解决EIP-7702授权冲突"); + console.log("🔁 完整流程: 取消冲突授权→重新授权→执行业务→取消授权"); + + // 创建客户端 + const publicClient = createPublicClient({ + chain: bsc, + transport: http() + }); + + const alice = privateKeyToAccount(ALICE_PRIVATE_KEY); + const relayer = privateKeyToAccount(RELAYER_PRIVATE_KEY); + + const aliceClient = createWalletClient({ + account: alice, + chain: bsc, + transport: http() + }); + + const relayerClient = createWalletClient({ + account: relayer, + chain: bsc, + transport: http() + }); + + console.log("\n👥 参与者:"); + console.log("Alice:", alice.address); + console.log("Relayer:", relayer.address); + console.log("BatchContract:", BATCH_CONTRACT); + + try { + // 检查初始状态 + console.log("\n📊 初始状态:"); + const [aliceTokens, RelayerTokens, aliceBnb] = await Promise.all([ + publicClient.readContract({ + address: ERC20_CONTRACT, + abi: ERC20_ABI, + functionName: 'balanceOf', + args: [alice.address] + }), + publicClient.readContract({ + address: ERC20_CONTRACT, + abi: ERC20_ABI, + functionName: 'balanceOf', + args: [relayer.address] + }), + publicClient.getBalance({ address: alice.address }) + ]); + + console.log("Alice代币:", formatEther(aliceTokens)); + console.log("Relayer代币:", formatEther(RelayerTokens)); + console.log("Alice BNB:", formatEther(aliceBnb)); + + // 检查Alice是否已经是智能账户 + const aliceCode = await publicClient.getCode({ address: alice.address }); + console.log("Alice代码长度:", aliceCode ? aliceCode.length : 0); + console.log("Alice代码:", aliceCode || '0x'); + const isSmartAccount = aliceCode && aliceCode !== '0x'; + + // 获取BatchCallAndSponsor合约的代码 + const batchCode = await publicClient.getCode({ address: BATCH_CONTRACT }); + console.log("Batch代码长度:", batchCode ? batchCode.length : 0); + + // 检查Alice的代码是否包含BatchCallAndSponsor合约地址(全小写比较) + // EIP-7702格式: 0xef01 + 00 + 合约地址 (去掉0x前缀) + const expectedCode = ('0xef0100' + BATCH_CONTRACT.slice(2)).toLowerCase(); + const actualCode = (aliceCode || '0x').toLowerCase(); + const isCorrectContract = actualCode === expectedCode; + + console.log("期望的EIP-7702代码:", expectedCode); + console.log("Alice实际代码:", actualCode); + console.log("代码匹配:", isCorrectContract); + + console.log("Alice是智能账户:", isSmartAccount); + if (isSmartAccount) { + console.log("Alice代码是BatchCallAndSponsor:", isCorrectContract); + if (!isCorrectContract) { + console.log("⚠️ Alice当前代码不是BatchCallAndSponsor,需要重新授权"); + } + } + + // 步骤1: 处理EIP-7702授权冲突和重新授权 + let authorization; + if (!isCorrectContract) { + if (isSmartAccount) { + console.log("\n🔄 步骤1: Alice被授权给其他合约,需要先取消再重新授权"); + console.log(" 当前授权:", actualCode); + console.log(" 期望授权:", expectedCode); + + // 子步骤1.1: Alice签名取消授权 + console.log("\n🚫 步骤1.1: Alice签名取消当前授权 (离线)"); + const cancelAuthorization = await aliceClient.signAuthorization({ + contractAddress: zeroAddress // 授权到零地址 = 取消授权 + }); + console.log("✅ Alice已签名取消授权"); + + // 子步骤1.2: Relayer发送取消授权交易 + console.log("\n📤 步骤1.2: Relayer发送取消授权交易"); + const cancelTx = await relayerClient.sendTransaction({ + account: relayer, + to: alice.address, + value: 0n, + data: '0x', + authorizationList: [cancelAuthorization], + gas: 100000n, + maxFeePerGas: GAS_PRICE, + maxPriorityFeePerGas: GAS_PRICE + }); + console.log("✅ 取消授权交易已发送:", cancelTx); + + // 等待取消授权交易确认 + await publicClient.waitForTransactionReceipt({ hash: cancelTx }); + console.log("✅ Alice授权已取消,恢复为普通EOA"); + } else { + console.log("\n🔐 步骤1: Alice首次EIP-7702授权"); + } + + // 重新授权到BatchCallAndSponsor + console.log("\n🔐 步骤1.3: Alice签名新的EIP-7702授权 (离线)"); + authorization = await aliceClient.signAuthorization({ + contractAddress: BATCH_CONTRACT + }); + console.log("✅ Alice已签名EIP-7702授权"); + console.log(" 目标合约地址:", BATCH_CONTRACT); + } else { + console.log("\n✅ Alice已经是正确的智能账户,跳过授权步骤"); + } + + console.log("\n✅ 跳过ERC20 Permit步骤 (智能账户直接transfer,无需授权)"); + + // 步骤2: 构建批量调用 (mint + transfer) - 不需要approve! + console.log("\n🔨 步骤2: 构建批量调用"); + const calls = [ + // 1. Mint代币给Alice + { + to: ERC20_CONTRACT, + value: 0n, + data: encodeFunctionData({ + abi: ERC20_ABI, + functionName: 'mint', + args: [alice.address, MINT_AMOUNT] + }) + }, + // 2. 直接Transfer转账给Relayer (无需approve,因为是自己转给别人) + { + to: ERC20_CONTRACT, + value: 0n, + data: encodeFunctionData({ + abi: ERC20_ABI, + functionName: 'transfer', + args: [relayer.address, TRANSFER_AMOUNT] + }) + } + ]; + + console.log("✅ 批量调用已构建 (2个操作):"); + console.log(" 1. Mint 1000 代币给Alice"); + console.log(" 2. Transfer 500代币给Relayer (无需授权,直接转账)"); + + console.log("\n⏳ 步骤3: 准备批量操作签名 (稍后进行)"); + + // 步骤4: Relayer执行EIP-7702授权交易 (如果需要) + if (!isCorrectContract) { + console.log("\n📤 步骤4: Relayer发送EIP-7702授权交易"); + console.log("💡 这将使Alice地址变成BatchCallAndSponsor智能账户"); + + const authTx = await relayerClient.sendTransaction({ + account: relayer, + to: alice.address, + value: 0n, + data: '0x', + authorizationList: [authorization], + gas: 100000n, + maxFeePerGas: GAS_PRICE, + maxPriorityFeePerGas: GAS_PRICE + }); + + console.log("✅ EIP-7702授权交易已发送:", authTx); + + // 等待交易确认 + const authReceipt = await publicClient.waitForTransactionReceipt({ + hash: authTx + }); + console.log("✅ Alice地址现在是BatchCallAndSponsor智能账户!"); + } else { + console.log("\n✅ Alice已经是正确的智能账户,跳过EIP-7702授权交易"); + } + + // 步骤5: Alice签名批量操作 (在确保Alice是正确智能账户后) + console.log("\n✍️ 步骤5: Alice签名批量操作 (现在Alice是正确的智能账户)"); + + // 对于EIP-7702新授权的地址,nonce应该从0开始 + let batchNonce = 0n; + console.log(" 使用初始nonce: 0 (新的EIP-7702智能账户)"); + + // 构建批量操作的digest + let encodedCalls = '0x'; + for (const call of calls) { + const packed = encodePacked( + ['address', 'uint256', 'bytes'], + [call.to, call.value, call.data] + ); + encodedCalls += packed.slice(2); + } + + const digest = keccak256( + encodePacked(['uint256', 'bytes'], [batchNonce, encodedCalls]) + ); + + const batchSignature = await aliceClient.signMessage({ + message: { raw: digest } + }); + + console.log("✅ Alice已签名批量操作 (使用正确的nonce)"); + + // 步骤6: Relayer执行批量操作 (一笔交易完成mint+transfer) + console.log("\n⚡ 步骤6: Relayer执行批量操作"); + console.log("💫 一笔交易完成: mint 1000 + transfer 500"); + + // 最终检查Alice是否是正确的智能账户(使用一致的小写比较) + const finalAliceCode = await publicClient.getCode({ address: alice.address }); + console.log("finalAliceCode:", finalAliceCode); + const finalExpectedCode = expectedCode; // 重用之前计算的expectedCode + const finalActualCode = (finalAliceCode || '0x').toLowerCase(); + const finalIsCorrectContract = finalActualCode === finalExpectedCode; + + + + const { request } = await publicClient.simulateContract({ + address: alice.address, // 现在Alice地址是合约地址 + abi: BATCH_ABI, + functionName: 'execute', + args: [calls, batchSignature], + account: relayer + }); + + const batchTx = await relayerClient.writeContract({ + ...request, + maxFeePerGas: GAS_PRICE, // 0.11 gwei max fee + maxPriorityFeePerGas: GAS_PRICE // 0.11 gwei priority fee + }); + + console.log("✅ 批量操作已发送:", batchTx); + + // 等待交易确认 + const batchReceipt = await publicClient.waitForTransactionReceipt({ + hash: batchTx + }); + + console.log("🎉 批量操作完成! Gas使用:", batchReceipt.gasUsed.toString()); + + // 验证最终结果 + console.log("\n📊 最终结果:"); + const [finalAliceTokens, finalRelayerTokens] = await Promise.all([ + publicClient.readContract({ + address: ERC20_CONTRACT, + abi: ERC20_ABI, + functionName: 'balanceOf', + args: [alice.address] + }), + publicClient.readContract({ + address: ERC20_CONTRACT, + abi: ERC20_ABI, + functionName: 'balanceOf', + args: [relayer.address] + }) + ]); + + console.log("Alice代币:", formatEther(finalAliceTokens)); + console.log("Relayer代币:", formatEther(finalRelayerTokens)); + + const aliceGain = finalAliceTokens - aliceTokens; + const RelayerGain = finalRelayerTokens - RelayerTokens; + + console.log("\n🎯 变化:"); + console.log("Alice净获得:", formatEther(aliceGain), "代币"); + console.log("Relayer获得:", formatEther(RelayerGain), "代币"); + + if (aliceGain === (MINT_AMOUNT - TRANSFER_AMOUNT) && RelayerGain === TRANSFER_AMOUNT) { + console.log("\n🎉🎉🎉 成功! EIP-7702演示完成!"); + console.log("✨ Alice获得500代币,Relayer获得500代币"); + console.log("✨ Alice全程只签名,无需发送交易"); + console.log("✨ 一笔交易完成mint+transfer"); + } + + console.log(`\n🔍 查看交易: https://bscscan.com/tx/${batchTx}`); + + // 步骤7: 可选的取消授权 (恢复Alice为普通EOA) + console.log("\n🔄 步骤7: 可选的取消授权 (将Alice恢复为普通EOA)"); + console.log("💡 这样Alice地址可以被重新用于其他EIP-7702授权"); + + // Alice签名取消授权 + console.log("\n🚫 步骤7.1: Alice签名取消授权 (离线)"); + const finalCancelAuthorization = await aliceClient.signAuthorization({ + contractAddress: zeroAddress // 授权到零地址 = 取消授权 + }); + console.log("✅ Alice已签名取消授权"); + + // Relayer发送取消授权交易 + console.log("\n📤 步骤7.2: Relayer发送取消授权交易"); + const finalCancelTx = await relayerClient.sendTransaction({ + account: relayer, + to: alice.address, + value: 0n, + data: '0x', + authorizationList: [finalCancelAuthorization], + gas: 100000n, + maxFeePerGas: GAS_PRICE, + maxPriorityFeePerGas: GAS_PRICE + }); + console.log("✅ 最终取消授权交易已发送:", finalCancelTx); + + // 等待取消授权交易确认 + await publicClient.waitForTransactionReceipt({ hash: finalCancelTx }); + + // 验证Alice恢复为普通EOA + const veryFinalCode = await publicClient.getCode({ address: alice.address }); + const isNowEOA = !veryFinalCode || veryFinalCode === '0x'; + + console.log("\n📊 最终授权状态:"); + console.log("Alice代码:", veryFinalCode || '0x'); + console.log("Alice是普通EOA:", isNowEOA); + + if (isNowEOA) { + console.log("✅ Alice已成功恢复为普通EOA,可用于下次EIP-7702授权"); + } else { + console.log("⚠️ Alice仍是智能账户,取消授权可能失败"); + } + + console.log(`\n🔍 查看取消授权交易: https://bscscan.com/tx/${finalCancelTx}`); + + } catch (error) { + console.error("\n❌ 操作失败:", error.message); + + if (error.message.includes('authorization')) { + console.log("💡 可能原因: BSC主网可能还不完全支持EIP-7702"); + console.log("💡 建议: 等待BSC正式支持EIP-7702后再试"); + } + + if (error.message.includes('insufficient funds')) { + console.log("💡 建议: 确保账户有足够的BNB支付gas费"); + } + } +} + +// Alice助手完成! + +main().catch(console.error); From dd381b8759b14765225f928ae266ee840772ccf1 Mon Sep 17 00:00:00 2001 From: mark Date: Thu, 14 Aug 2025 18:11:27 +0800 Subject: [PATCH 2/2] feat(script): add stake token --- hardhat-version/README.md | 104 +++- hardhat-version/scripts/2-7702-stake-token.js | 566 ++++++++++++++++++ 2 files changed, 669 insertions(+), 1 deletion(-) create mode 100644 hardhat-version/scripts/2-7702-stake-token.js diff --git a/hardhat-version/README.md b/hardhat-version/README.md index 86006cb..149f391 100644 --- a/hardhat-version/README.md +++ b/hardhat-version/README.md @@ -178,7 +178,7 @@ networks: { ## 链上交互 -### 转账运行 +### 帮助Alice mint token 和分发 ```angular2html ts-node scripts/1-7702-mint-transfer.js (node:93249) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///Users/mark/Dev/self/eip-7702/hardhat-version/scripts/1-7702-mint-transfer.js is not specified and it doesn't parse as CommonJS. @@ -272,6 +272,108 @@ Alice是普通EOA: true ``` +### 帮助 Alice stake token + +```angular2html + ts-node scripts/2-7702-stake-token.js +(node:95297) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///Users/mark/Dev/self/eip-7702/hardhat-version/scripts/2-7702-stake-token.js is not specified and it doesn't parse as CommonJS. +Reparsing as ES module because module syntax was detected. This incurs a performance overhead. +To eliminate this warning, add "type": "module" to /Users/mark/Dev/self/eip-7702/hardhat-version/package.json. +(Use `node --trace-warnings ...` to show where the warning was created) +🚀 Alice EIP-7702 助手 - BSC主网 (TierStake质押版) +💡 核心特性: Alice通过EIP-7702进行gasless质押操作 +⛽ Gas价格: 0.11 gwei +🏦 质押场景: approve授权 + stake操作,Alice零gas体验 +🔄 智能处理: 自动解决EIP-7702授权冲突 +🔁 完整流程: 取消冲突授权→重新授权→执行质押→取消授权 + +👥 参与者: +Alice: 0x81e206235Db517630183Ff9aB1A1E8a927310f01 +Relayer: 0x8E4F1a52C38A9A26Fa78e5Eb5127c098cFc3e2db +BatchContract: 0x01CFCFd8FB0C4BF9abB4Fd8a449DdF48c94e86D4 +StakingToken: 0xfdffb411c4a70aa7c95d5c981a6fb4da867e1111 +TierStake: 0x8AF99B0092650d77EBf470A2d7e935dC1093073d + +📊 初始状态: +Alice持有Token: 0.0001 +Alice已质押: 0 +Alice待提取: 0 +Alice BNB: 0 +合约总质押: 1.2334561 +Alice代码长度: 0 +Alice代码: 0x +Batch代码长度: 3936 +期望的EIP-7702代码: 0xef010001cfcfd8fb0c4bf9abb4fd8a449ddf48c94e86d4 +Alice实际代码: 0x +代码匹配: false +Alice是智能账户: undefined + +🔐 步骤1: Alice首次EIP-7702授权 + +🔐 步骤1.3: Alice签名新的EIP-7702授权 (离线) +✅ Alice已签名EIP-7702授权 + 目标合约地址: 0x01CFCFd8FB0C4BF9abB4Fd8a449DdF48c94e86D4 + +✅ 步骤2: 跳过ERC20 Permit +💡 Alice现在是智能账户,可以直接approve,无需permit签名 + +🔨 步骤3: 构建批量调用 +✅ 批量调用已构建 (2个操作): + 1. Approve授权TierStake使用 0.0000001 代币 + 2. Stake质押 0.0000001 代币到TierStake合约 + +⏳ 步骤4: 准备批量操作签名 (稍后进行) + +📤 步骤5: Relayer发送EIP-7702授权交易 +💡 这将使Alice地址变成BatchCallAndSponsor智能账户 +✅ EIP-7702授权交易已发送: 0x788c172d38057ac84827d4d141715356c295264d1a8a30d8ee63f81c88ffaac5 +✅ Alice地址现在是BatchCallAndSponsor智能账户! + +✍️ 步骤6: Alice签名批量操作 (现在Alice是正确的智能账户) + 使用初始nonce: 0 (新的EIP-7702智能账户) +✅ Alice已签名批量操作 (使用正确的nonce) + +⚡ 步骤7: Relayer执行批量操作 +💫 一笔交易完成: approve授权 + stake质押 0.0000001 代币 +finalAliceCode: 0xef010001cfcfd8fb0c4bf9abb4fd8a449ddf48c94e86d4 +✅ 批量操作已发送: 0x669d0955715eb49dbc94a06d4750dc368071e3facd6d811bdbb78445e023efb2 +🎉 批量操作完成! Gas使用: 138204 + +📊 最终结果: +Alice剩余Token: 0.0000999 +Alice已质押: 0.0000001 +Alice待提取: 0 +合约总质押: 1.2334562 + +🎯 变化: +Alice代币变化: -0.0000001 +Alice质押增加: 0.0000001 + +🎉🎉🎉 成功! EIP-7702质押演示完成! +✨ Alice成功质押 0.0000001 代币 +✨ Alice全程只签名,无需发送交易 +✨ 一笔交易完成approve+stake + +🔍 查看交易: https://bscscan.com/tx/0x669d0955715eb49dbc94a06d4750dc368071e3facd6d811bdbb78445e023efb2 + +🔄 步骤8: 可选的取消授权 (将Alice恢复为普通EOA) +💡 这样Alice地址可以被重新用于其他EIP-7702授权 + +🚫 步骤8.1: Alice签名取消授权 (离线) +✅ Alice已签名取消授权 + +📤 步骤8.2: Relayer发送取消授权交易 +✅ 最终取消授权交易已发送: 0xe7883e9546f69d4d7bfd364d057f6f0da381e1bb9cda77ed442797873e85d443 + +📊 最终授权状态: +Alice代码: 0x +Alice是普通EOA: true +✅ Alice已成功恢复为普通EOA,可用于下次EIP-7702授权 + +🔍 查看取消授权交易: https://bscscan.com/tx/0xe7883e9546f69d4d7bfd364d057f6f0da381e1bb9cda77ed442797873e85d443 +(base) ➜ hardhat-version git:(main +``` + ## 📚 相关资源 - [EIP-7702规范](https://eips.ethereum.org/EIPS/eip-7702) diff --git a/hardhat-version/scripts/2-7702-stake-token.js b/hardhat-version/scripts/2-7702-stake-token.js new file mode 100644 index 0000000..13603a7 --- /dev/null +++ b/hardhat-version/scripts/2-7702-stake-token.js @@ -0,0 +1,566 @@ +import { + createWalletClient, + createPublicClient, + http, + parseEther, + formatEther, + encodeFunctionData, + encodePacked, + keccak256, + parseGwei, + toHex, + zeroAddress +} from 'viem'; +import { privateKeyToAccount } from 'viem/accounts'; +import { bsc } from 'viem/chains'; + +// 配置 - BSC主网 (TierStake质押版本) +const ALICE_PRIVATE_KEY = "0xaf8d3571cefcdeb7aeb829ca8110cc3d758b386d54ad37660174dc428d7bef31"; // 请填入Alice的私钥 +const RELAYER_PRIVATE_KEY = ""; // 请填入Relayer的私钥 +const BATCH_CONTRACT = "0x01CFCFd8FB0C4BF9abB4Fd8a449DdF48c94e86D4"; +const STAKING_TOKEN_CONTRACT = "0xfdffb411c4a70aa7c95d5c981a6fb4da867e1111"; // stakingToken (支持ERC20Permit) +const TIERSTAKE_CONTRACT = "0x8AF99B0092650d77EBf470A2d7e935dC1093073d"; // TierStake合约地址 (需要填入已部署的地址) + +const STAKE_AMOUNT = parseEther("0.0000001"); // Alice质押数量 +const WITHDRAW_AMOUNT = parseEther("0.0000001"); // Alice提取数量 +const GAS_PRICE = parseGwei('0.11'); // 0.11 gwei + +// ERC20 ABI (包含Permit功能) +const ERC20_ABI = [ + { + name: 'balanceOf', + type: 'function', + inputs: [{ name: 'owner', type: 'address' }], + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view' + }, + { + name: 'approve', + type: 'function', + inputs: [ + { name: 'spender', type: 'address' }, + { name: 'amount', type: 'uint256' } + ], + outputs: [{ name: '', type: 'bool' }], + stateMutability: 'nonpayable' + }, + { + name: 'transferFrom', + type: 'function', + inputs: [ + { name: 'from', type: 'address' }, + { name: 'to', type: 'address' }, + { name: 'amount', type: 'uint256' } + ], + outputs: [{ name: '', type: 'bool' }], + stateMutability: 'nonpayable' + }, + { + name: 'permit', + type: 'function', + inputs: [ + { name: 'owner', type: 'address' }, + { name: 'spender', type: 'address' }, + { name: 'amount', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + { name: 'v', type: 'uint8' }, + { name: 'r', type: 'bytes32' }, + { name: 's', type: 'bytes32' } + ], + outputs: [], + stateMutability: 'nonpayable' + }, + { + name: 'nonces', + type: 'function', + inputs: [{ name: 'owner', type: 'address' }], + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view' + }, + { + name: 'DOMAIN_SEPARATOR', + type: 'function', + inputs: [], + outputs: [{ name: '', type: 'bytes32' }], + stateMutability: 'view' + } +]; + +// TierStake ABI (质押合约功能) +const TIERSTAKE_ABI = [ + { + name: 'stake', + type: 'function', + inputs: [{ name: 'amount', type: 'uint256' }], + outputs: [], + stateMutability: 'nonpayable' + }, + { + name: 'withdraw', + type: 'function', + inputs: [{ name: 'amount', type: 'uint256' }], + outputs: [], + stateMutability: 'nonpayable' + }, + { + name: 'claim', + type: 'function', + inputs: [], + outputs: [], + stateMutability: 'nonpayable' + }, + { + name: 'getUserInfo', + type: 'function', + inputs: [{ name: 'user', type: 'address' }], + outputs: [{ + name: '', + type: 'tuple', + components: [ + { name: 'stakedAmount', type: 'uint256' }, + { name: 'claimedAmount', type: 'uint256' }, + { name: 'pendingWithdrawAmount', type: 'uint256' }, + { name: 'nextClaimTime', type: 'uint256' } + ] + }], + stateMutability: 'view' + }, + { + name: 'canClaim', + type: 'function', + inputs: [{ name: 'user', type: 'address' }], + outputs: [{ name: '', type: 'bool' }], + stateMutability: 'view' + }, + { + name: 'totalStaked', + type: 'function', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view' + }, + { + name: 'stakingToken', + type: 'function', + inputs: [], + outputs: [{ name: '', type: 'address' }], + stateMutability: 'view' + } +]; + +const BATCH_ABI = [ + { + name: 'execute', + type: 'function', + inputs: [ + { + name: 'calls', + type: 'tuple[]', + components: [ + { name: 'to', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'data', type: 'bytes' } + ] + }, + { name: 'signature', type: 'bytes' } + ], + outputs: [], + stateMutability: 'payable' + }, + { + name: 'nonce', + type: 'function', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view' + } +]; + +async function main() { + console.log("🚀 Alice EIP-7702 助手 - BSC主网 (TierStake质押版)"); + console.log("💡 核心特性: Alice通过EIP-7702进行gasless质押操作"); + console.log("⛽ Gas价格: 0.11 gwei"); + console.log("🏦 质押场景: approve授权 + stake操作,Alice零gas体验"); + console.log("🔄 智能处理: 自动解决EIP-7702授权冲突"); + console.log("🔁 完整流程: 取消冲突授权→重新授权→执行质押→取消授权"); + + // 检查私钥配置 + if (!ALICE_PRIVATE_KEY || !RELAYER_PRIVATE_KEY) { + console.error("\n❌ 错误: 请在脚本中填入私钥"); + console.error("请设置:"); + console.error("- ALICE_PRIVATE_KEY: Alice的私钥"); + console.error("- RELAYER_PRIVATE_KEY: Relayer的私钥"); + console.error("- TIERSTAKE_CONTRACT: TierStake合约地址"); + process.exit(1); + } + + if (!TIERSTAKE_CONTRACT) { + console.error("\n❌ 错误: 请填入TierStake合约地址"); + console.error("请设置 TIERSTAKE_CONTRACT 变量"); + process.exit(1); + } + + // 创建客户端 + const publicClient = createPublicClient({ + chain: bsc, + transport: http() + }); + + const alice = privateKeyToAccount(ALICE_PRIVATE_KEY); + const relayer = privateKeyToAccount(RELAYER_PRIVATE_KEY); + + const aliceClient = createWalletClient({ + account: alice, + chain: bsc, + transport: http() + }); + + const relayerClient = createWalletClient({ + account: relayer, + chain: bsc, + transport: http() + }); + + console.log("\n👥 参与者:"); + console.log("Alice:", alice.address); + console.log("Relayer:", relayer.address); + console.log("BatchContract:", BATCH_CONTRACT); + console.log("StakingToken:", STAKING_TOKEN_CONTRACT); + console.log("TierStake:", TIERSTAKE_CONTRACT); + + try { + // 检查初始状态 + console.log("\n📊 初始状态:"); + const [aliceTokens, aliceBnb, aliceStakeInfo, totalStaked] = await Promise.all([ + publicClient.readContract({ + address: STAKING_TOKEN_CONTRACT, + abi: ERC20_ABI, + functionName: 'balanceOf', + args: [alice.address] + }), + publicClient.getBalance({ address: alice.address }), + publicClient.readContract({ + address: TIERSTAKE_CONTRACT, + abi: TIERSTAKE_ABI, + functionName: 'getUserInfo', + args: [alice.address] + }), + publicClient.readContract({ + address: TIERSTAKE_CONTRACT, + abi: TIERSTAKE_ABI, + functionName: 'totalStaked', + args: [] + }) + ]); + + console.log("Alice持有Token:", formatEther(aliceTokens)); + console.log("Alice已质押:", formatEther(aliceStakeInfo.stakedAmount)); + console.log("Alice待提取:", formatEther(aliceStakeInfo.pendingWithdrawAmount)); + console.log("Alice BNB:", formatEther(aliceBnb)); + console.log("合约总质押:", formatEther(totalStaked)); + + // 检查Alice是否已经是智能账户 + const aliceCode = await publicClient.getCode({ address: alice.address }); + console.log("Alice代码长度:", aliceCode ? aliceCode.length : 0); + console.log("Alice代码:", aliceCode || '0x'); + const isSmartAccount = aliceCode && aliceCode !== '0x'; + + // 获取BatchCallAndSponsor合约的代码 + const batchCode = await publicClient.getCode({ address: BATCH_CONTRACT }); + console.log("Batch代码长度:", batchCode ? batchCode.length : 0); + + // 检查Alice的代码是否包含BatchCallAndSponsor合约地址(全小写比较) + // EIP-7702格式: 0xef01 + 00 + 合约地址 (去掉0x前缀) + const expectedCode = ('0xef0100' + BATCH_CONTRACT.slice(2)).toLowerCase(); + const actualCode = (aliceCode || '0x').toLowerCase(); + const isCorrectContract = actualCode === expectedCode; + + console.log("期望的EIP-7702代码:", expectedCode); + console.log("Alice实际代码:", actualCode); + console.log("代码匹配:", isCorrectContract); + + console.log("Alice是智能账户:", isSmartAccount); + if (isSmartAccount) { + console.log("Alice代码是BatchCallAndSponsor:", isCorrectContract); + if (!isCorrectContract) { + console.log("⚠️ Alice当前代码不是BatchCallAndSponsor,需要重新授权"); + } + } + + // 步骤1: 处理EIP-7702授权冲突和重新授权 + let authorization; + if (!isCorrectContract) { + if (isSmartAccount) { + console.log("\n🔄 步骤1: Alice被授权给其他合约,需要先取消再重新授权"); + console.log(" 当前授权:", actualCode); + console.log(" 期望授权:", expectedCode); + + // 子步骤1.1: Alice签名取消授权 + console.log("\n🚫 步骤1.1: Alice签名取消当前授权 (离线)"); + const cancelAuthorization = await aliceClient.signAuthorization({ + contractAddress: zeroAddress // 授权到零地址 = 取消授权 + }); + console.log("✅ Alice已签名取消授权"); + + // 子步骤1.2: Relayer发送取消授权交易 + console.log("\n📤 步骤1.2: Relayer发送取消授权交易"); + const cancelTx = await relayerClient.sendTransaction({ + account: relayer, + to: alice.address, + value: 0n, + data: '0x', + authorizationList: [cancelAuthorization], + gas: 100000n, + maxFeePerGas: GAS_PRICE, + maxPriorityFeePerGas: GAS_PRICE + }); + console.log("✅ 取消授权交易已发送:", cancelTx); + + // 等待取消授权交易确认 + await publicClient.waitForTransactionReceipt({ hash: cancelTx }); + console.log("✅ Alice授权已取消,恢复为普通EOA"); + } else { + console.log("\n🔐 步骤1: Alice首次EIP-7702授权"); + } + + // 重新授权到BatchCallAndSponsor + console.log("\n🔐 步骤1.3: Alice签名新的EIP-7702授权 (离线)"); + authorization = await aliceClient.signAuthorization({ + contractAddress: BATCH_CONTRACT + }); + console.log("✅ Alice已签名EIP-7702授权"); + console.log(" 目标合约地址:", BATCH_CONTRACT); + } else { + console.log("\n✅ Alice已经是正确的智能账户,跳过授权步骤"); + } + + // 步骤2: 跳过Permit,智能账户直接approve + console.log("\n✅ 步骤2: 跳过ERC20 Permit"); + console.log("💡 Alice现在是智能账户,可以直接approve,无需permit签名"); + + // 步骤3: 构建批量调用 (approve + stake) + console.log("\n🔨 步骤3: 构建批量调用"); + const calls = [ + // 1. 直接Approve授权TierStake合约使用Alice的代币 + { + to: STAKING_TOKEN_CONTRACT, + value: 0n, + data: encodeFunctionData({ + abi: ERC20_ABI, + functionName: 'approve', + args: [TIERSTAKE_CONTRACT, STAKE_AMOUNT] + }) + }, + // 2. 质押代币到TierStake合约 + { + to: TIERSTAKE_CONTRACT, + value: 0n, + data: encodeFunctionData({ + abi: TIERSTAKE_ABI, + functionName: 'stake', + args: [STAKE_AMOUNT] + }) + } + ]; + + console.log("✅ 批量调用已构建 (2个操作):"); + console.log(" 1. Approve授权TierStake使用", formatEther(STAKE_AMOUNT), "代币"); + console.log(" 2. Stake质押", formatEther(STAKE_AMOUNT), "代币到TierStake合约"); + + console.log("\n⏳ 步骤4: 准备批量操作签名 (稍后进行)"); + + // 步骤5: Relayer执行EIP-7702授权交易 (如果需要) + if (!isCorrectContract) { + console.log("\n📤 步骤5: Relayer发送EIP-7702授权交易"); + console.log("💡 这将使Alice地址变成BatchCallAndSponsor智能账户"); + + const authTx = await relayerClient.sendTransaction({ + account: relayer, + to: alice.address, + value: 0n, + data: '0x', + authorizationList: [authorization], + gas: 100000n, + maxFeePerGas: GAS_PRICE, + maxPriorityFeePerGas: GAS_PRICE + }); + + console.log("✅ EIP-7702授权交易已发送:", authTx); + + // 等待交易确认 + const authReceipt = await publicClient.waitForTransactionReceipt({ + hash: authTx + }); + console.log("✅ Alice地址现在是BatchCallAndSponsor智能账户!"); + } else { + console.log("\n✅ Alice已经是正确的智能账户,跳过EIP-7702授权交易"); + } + + // 步骤6: Alice签名批量操作 (在确保Alice是正确智能账户后) + console.log("\n✍️ 步骤6: Alice签名批量操作 (现在Alice是正确的智能账户)"); + + // 对于EIP-7702新授权的地址,nonce应该从0开始 + let batchNonce = 0n; + console.log(" 使用初始nonce: 0 (新的EIP-7702智能账户)"); + + // 构建批量操作的digest + let encodedCalls = '0x'; + for (const call of calls) { + const packed = encodePacked( + ['address', 'uint256', 'bytes'], + [call.to, call.value, call.data] + ); + encodedCalls += packed.slice(2); + } + + const digest = keccak256( + encodePacked(['uint256', 'bytes'], [batchNonce, encodedCalls]) + ); + + const batchSignature = await aliceClient.signMessage({ + message: { raw: digest } + }); + + console.log("✅ Alice已签名批量操作 (使用正确的nonce)"); + + // 步骤7: Relayer执行批量操作 (一笔交易完成approve+stake) + console.log("\n⚡ 步骤7: Relayer执行批量操作"); + console.log("💫 一笔交易完成: approve授权 + stake质押", formatEther(STAKE_AMOUNT), "代币"); + + // 最终检查Alice是否是正确的智能账户(使用一致的小写比较) + const finalAliceCode = await publicClient.getCode({ address: alice.address }); + console.log("finalAliceCode:", finalAliceCode); + const finalExpectedCode = expectedCode; // 重用之前计算的expectedCode + const finalActualCode = (finalAliceCode || '0x').toLowerCase(); + const finalIsCorrectContract = finalActualCode === finalExpectedCode; + + + + const { request } = await publicClient.simulateContract({ + address: alice.address, // 现在Alice地址是合约地址 + abi: BATCH_ABI, + functionName: 'execute', + args: [calls, batchSignature], + account: relayer + }); + + const batchTx = await relayerClient.writeContract({ + ...request, + maxFeePerGas: GAS_PRICE, // 0.11 gwei max fee + maxPriorityFeePerGas: GAS_PRICE // 0.11 gwei priority fee + }); + + console.log("✅ 批量操作已发送:", batchTx); + + // 等待交易确认 + const batchReceipt = await publicClient.waitForTransactionReceipt({ + hash: batchTx + }); + + console.log("🎉 批量操作完成! Gas使用:", batchReceipt.gasUsed.toString()); + + // 验证最终结果 + console.log("\n📊 最终结果:"); + const [finalAliceTokens, finalAliceStakeInfo, finalTotalStaked] = await Promise.all([ + publicClient.readContract({ + address: STAKING_TOKEN_CONTRACT, + abi: ERC20_ABI, + functionName: 'balanceOf', + args: [alice.address] + }), + publicClient.readContract({ + address: TIERSTAKE_CONTRACT, + abi: TIERSTAKE_ABI, + functionName: 'getUserInfo', + args: [alice.address] + }), + publicClient.readContract({ + address: TIERSTAKE_CONTRACT, + abi: TIERSTAKE_ABI, + functionName: 'totalStaked', + args: [] + }) + ]); + + console.log("Alice剩余Token:", formatEther(finalAliceTokens)); + console.log("Alice已质押:", formatEther(finalAliceStakeInfo.stakedAmount)); + console.log("Alice待提取:", formatEther(finalAliceStakeInfo.pendingWithdrawAmount)); + console.log("合约总质押:", formatEther(finalTotalStaked)); + + const aliceTokenChange = finalAliceTokens - aliceTokens; + const aliceStakeChange = finalAliceStakeInfo.stakedAmount - aliceStakeInfo.stakedAmount; + + console.log("\n🎯 变化:"); + console.log("Alice代币变化:", formatEther(aliceTokenChange)); + console.log("Alice质押增加:", formatEther(aliceStakeChange)); + + if (aliceStakeChange === STAKE_AMOUNT && aliceTokenChange === -STAKE_AMOUNT) { + console.log("\n🎉🎉🎉 成功! EIP-7702质押演示完成!"); + console.log("✨ Alice成功质押", formatEther(STAKE_AMOUNT), "代币"); + console.log("✨ Alice全程只签名,无需发送交易"); + console.log("✨ 一笔交易完成approve+stake"); + } + + console.log(`\n🔍 查看交易: https://bscscan.com/tx/${batchTx}`); + + // 步骤8: 可选的取消授权 (恢复Alice为普通EOA) + console.log("\n🔄 步骤8: 可选的取消授权 (将Alice恢复为普通EOA)"); + console.log("💡 这样Alice地址可以被重新用于其他EIP-7702授权"); + + // Alice签名取消授权 + console.log("\n🚫 步骤8.1: Alice签名取消授权 (离线)"); + const finalCancelAuthorization = await aliceClient.signAuthorization({ + contractAddress: zeroAddress // 授权到零地址 = 取消授权 + }); + console.log("✅ Alice已签名取消授权"); + + // Relayer发送取消授权交易 + console.log("\n📤 步骤8.2: Relayer发送取消授权交易"); + const finalCancelTx = await relayerClient.sendTransaction({ + account: relayer, + to: alice.address, + value: 0n, + data: '0x', + authorizationList: [finalCancelAuthorization], + gas: 100000n, + maxFeePerGas: GAS_PRICE, + maxPriorityFeePerGas: GAS_PRICE + }); + console.log("✅ 最终取消授权交易已发送:", finalCancelTx); + + // 等待取消授权交易确认 + await publicClient.waitForTransactionReceipt({ hash: finalCancelTx }); + + // 验证Alice恢复为普通EOA + const veryFinalCode = await publicClient.getCode({ address: alice.address }); + const isNowEOA = !veryFinalCode || veryFinalCode === '0x'; + + console.log("\n📊 最终授权状态:"); + console.log("Alice代码:", veryFinalCode || '0x'); + console.log("Alice是普通EOA:", isNowEOA); + + if (isNowEOA) { + console.log("✅ Alice已成功恢复为普通EOA,可用于下次EIP-7702授权"); + } else { + console.log("⚠️ Alice仍是智能账户,取消授权可能失败"); + } + + console.log(`\n🔍 查看取消授权交易: https://bscscan.com/tx/${finalCancelTx}`); + + } catch (error) { + console.error("\n❌ 操作失败:", error.message); + + if (error.message.includes('authorization')) { + console.log("💡 可能原因: BSC主网可能还不完全支持EIP-7702"); + console.log("💡 建议: 等待BSC正式支持EIP-7702后再试"); + } + + if (error.message.includes('insufficient funds')) { + console.log("💡 建议: 确保账户有足够的BNB支付gas费"); + } + } +} + +// Alice助手完成! + +main().catch(console.error);