使用Ethereum State Override模拟修改ERC20代币余额失败求助
问题诊断与解决方案
你的代码存在三个核心问题,导致余额始终返回0:
1. State Override 目标账户错误
ERC20的balances映射存储在代币合约的账户状态中,而非用户地址的状态里。你当前覆盖的是目标用户地址的状态,完全没触及USDT合约的存储,所以读取的还是原合约的真实余额(0)。
2. 存储槽计算方式错误
Solidity中mapping(address => uint256)类型的存储槽计算规则是:keccak256(abi.encodePacked(用户地址, 映射变量的存储槽索引))
USDT合约中balances是第一个状态变量,存储槽索引为0。你用字符串拼接的方式计算哈希,完全不符合Solidity的存储布局规则,导致找不到正确的存储位置。
3. 不必要的假合约代码
你注入的fakeUsdtCode是一个没有实现balanceOf方法的空合约,即使覆盖存储,调用该方法也会返回默认值0。实际上不需要替换合约代码,直接覆盖原合约的存储即可。
修正后的代码
(async () => { const Web3 = require('web3'); const web3 = new Web3('http://192.168.50.159:7546'); const targetAddress = '0xb7BF3e961645b8ebB75A622A07983335B61cf5c0'; const usdtContractAddress = '0xdac17f958d2ee523a2206206994597c13d831ec7'; const fakeUsdtBalance = '0x00000000000000000000000000000000000000000000000000000000000000ff'; // 255 // 正确计算balances映射的存储槽 const storageKey = web3.utils.soliditySha3( { type: 'address', value: targetAddress }, { type: 'uint256', value: 0 } // balances的存储槽索引为0 ); const result = await web3.eth.call({ to: usdtContractAddress, data: '0x70a08231000000000000000000000000' + targetAddress.substring(2), stateOverride: { // 覆盖USDT合约的状态,而非用户地址 [usdtContractAddress]: { storage: { [storageKey]: fakeUsdtBalance } } } }); console.log(`${targetAddress}的USDT余额: ${web3.utils.hexToNumberString(result)}`); })();
其他实现方法
如果State Override的方式不符合需求,还可以:
- 部署一个自定义的ERC20测试合约,在测试环境中直接调用
_mint方法生成余额 - 使用Hardhat的
impersonateAccount功能,模拟持有大额余额的账户(适合测试交互场景)
内容的提问来源于stack exchange,提问作者Moon soon
相关产品推荐
相关产品推荐

