Brownie主网分叉存入WETH到Aave报SafeMath减法溢出错误
根因分析
这个报错本质是合约执行SafeMath安全减法时,被减数小于减数触发的链上回滚,结合代码逻辑和环境配置,共有两个核心问题:
- 操作账户不匹配:代码中主网分叉分支的
get_weth()调用未指定发送账户,默认使用Brownie加载的索引为0的测试账户兑换了0.1 WETH,但后续授权、存款操作使用的是get_account(1)拿到的索引为1的账户,该账户实际WETH余额为0。注意:ERC20的
approve授权操作不会校验调用者的代币余额,哪怕持有0枚代币也可以成功给其他地址授权任意额度,因此授权步骤不会报错;只有借贷池调用transferFrom实际转走WETH时,WETH合约会执行用户余额 = 用户余额 - 转账金额的逻辑,0减0.1ETH对应的数值就会触发SafeMath减法溢出。 - 分叉环境硬分叉版本配置错误:启动ganache-cli时指定了
--hardfork istanbul,该硬分叉是2019年上线的旧版本,主网Aave V2协议2020年底才部署,依赖更高版本的EVM特性,旧硬分叉环境下执行新合约逻辑会出现异常。
Kovan测试网脚本可正常运行,是因为测试网逻辑中get_weth()传入了正确的操作账户,且测试网节点的硬分叉版本和合约版本匹配。
修复方案
1. 修正ganache分叉启动命令
去掉过时的istanbul硬分叉指定,让ganache自动匹配fork区块对应的EVM版本,修改后的启动命令如下:
ganache-cli.cmd --accounts 10 --fork https://eth-mainnet.alchemyapi.io/v2/_Z-H35Cw4-kIaLun7L14IU92c1AcDNYx --mnemonic brownie --port 8545
如果需要手动指定硬分叉版本,使用--hardfork london即可兼容当前主网Aave合约逻辑。
2. 修正业务代码逻辑
核心是保证兑换WETH、授权、存款三个操作使用同一个账户,同时补上原代码缺失的依赖导入、增加余额校验提前发现问题,修正后的完整代码如下:
from scripts.helpful_scripts import get_account from scripts.get_weth import get_weth from brownie import network, config, interface from web3 import Web3 AMOUNT = Web3.toWei(0.1, "ether") def main(): account = get_account(1) erc20_address = config["networks"][network.show_active()]["weth_token"] if network.show_active() in ["mainnet-fork"]: # 修复:指定用当前操作账户兑换WETH,保证WETH到账到正确地址 get_weth(account=account) lending_pool = get_lending_pool() # 增加余额前置校验,提前排查配置问题 weth_contract = interface.IERC20(erc20_address) balance = weth_contract.balanceOf(account.address) print(f"当前账户WETH余额: {Web3.fromWei(balance, 'ether')} WETH") assert balance >= AMOUNT, "账户WETH余额不足,无法执行存款" approve_erc20_tokens(AMOUNT, lending_pool.address, erc20_address, account) print("Depositing !!!") tx = lending_pool.deposit(erc20_address, AMOUNT, account.address, 0, {"from": account}) tx.wait(1) print("Deposited !!!") def approve_erc20_tokens(amount, spender, erc20_address, account): print("Approving ERC20 token !!!") erc20 = interface.IERC20(erc20_address) tx = erc20.approve(spender, amount, {"from": account}) tx.wait(1) print("Approved !!!") return tx def get_lending_pool(): lending_pool_addresses_provider = interface.ILendingPoolAddressesProvider(config["networks"][network.show_active()]["lending_pool_addresses_provider"]) lending_pool_address = lending_pool_addresses_provider.getLendingPool() lending_pool = interface.ILendingPool(lending_pool_address) return lending_pool
注意:需要确认你的get_weth函数支持接收account参数,如果原函数没有该入参,给函数加上即可,核心逻辑是调用WETH合约的deposit方法发送0.1ETH时,{"from": account}指定为当前操作账户。
3. 验证修复
重新启动ganache分叉环境,执行脚本即可正常完成WETH存入Aave借贷池的全流程,不会再触发SafeMath溢出报错。
内容的提问来源于stack exchange,提问作者aman pal
相关产品推荐
相关产品推荐

