Python Web3调用OneSplit合约Swap WETH转USDC报错:SafeERC20底层调用失败
OneSplit合约WETH兑换USDC报错「SafeERC20: low-level call failed」排查与解决
问题场景
调用OneSplit合约(地址0xC586BeF4a0992C495Cf22e1aeEE4E446CECDee0E)执行WETH→USDC兑换时,代码如下:
transaction = onesplit_abi_contract.functions.swap( '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', #WETH TOKEN '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', #USDC TOKEN 1000000000000000000, 0, [5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 0, 0, 0, 0], 0, ).buildTransaction({ 'from': wallet_address, 'nonce': w3.eth.get_transaction_count(wallet_address), 'gas': 190000, 'gasPrice': w3.eth.gas_price }) signed_trx = w3.eth.account.signTransaction(transaction, private_key) trx_hash = w3.eth.sendRawTransaction(signed_trx.rawTransaction)
始终收到错误:Fail with error 'SafeERC20: low-level call failed'
但将WETH替换为ETH地址0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,并在buildTransaction中添加'value': 1000000000000000000后,交易可正常执行:
transaction = onesplit_abi_contract.functions.swap( '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', #ETH TOKEN '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', #USDC TOKEN 1000000000000000000, 0, [100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, ).buildTransaction({ 'from': wallet_address, 'nonce': w3.eth.get_transaction_count(wallet_address), 'gas': 190000, 'gasPrice': w3.eth.gas_price, 'value': 1000000000000000000 }) signed_trx = w3.eth.account.signTransaction(transaction, private_key) trx_hash = w3.eth.sendRawTransaction(signed_trx.rawTransaction)
核心原因
ETH是以太坊原生代币,交易时直接通过value字段转入合约即可;而WETH是ERC20标准代币,合约无法直接划转用户钱包里的WETH,必须先由用户调用WETH合约的approve方法,授权OneSplit合约拥有划转指定额度WETH的权限。当前代码跳过了授权步骤,导致合约执行WETH划转时触发SafeERC20调用失败。
解决方案
1. 先执行WETH授权操作
调用WETH合约的approve方法,授权OneSplit合约可以划转你的WETH:
# WETH合约地址与简化ABI(仅包含approve方法) weth_address = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' weth_abi = [{ "constant": False, "inputs": [{"name": "_spender", "type": "address"}, {"name": "_value", "type": "uint256"}], "name": "approve", "outputs": [{"name": "", "type": "bool"}], "payable": False, "stateMutability": "nonpayable", "type": "function" }] weth_contract = w3.eth.contract(address=weth_address, abi=weth_abi) # 构建授权交易:授权OneSplit合约划转1WETH(可设为2**256-1实现无限授权,仅对可信合约使用) approve_tx = weth_contract.functions.approve( '0xC586BeF4a0992C495Cf22e1aeEE4E446CECDee0E', # OneSplit合约地址 1000000000000000000 # 授权金额与swap的金额一致 ).buildTransaction({ 'from': wallet_address, 'nonce': w3.eth.get_transaction_count(wallet_address), 'gas': 50000, # approve交易所需gas足够即可 'gasPrice': w3.eth.gas_price }) # 签名并发送授权交易 signed_approve_tx = w3.eth.account.signTransaction(approve_tx, private_key) approve_tx_hash = w3.eth.sendRawTransaction(signed_approve_tx.rawTransaction) # 等待授权交易上链,确保授权生效 w3.eth.wait_for_transaction_receipt(approve_tx_hash)
2. 执行原Swap交易
授权完成后,再执行你原来的Swap交易,注意更新nonce(会自动递增,无需手动计算),同时可适当调高gas值(WETH兑换的gas消耗可能高于ETH):
transaction = onesplit_abi_contract.functions.swap( '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', #WETH TOKEN '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', #USDC TOKEN 1000000000000000000, 0, [5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 0, 0, 0, 0], 0, ).buildTransaction({ 'from': wallet_address, 'nonce': w3.eth.get_transaction_count(wallet_address), 'gas': 250000, # 适当调高gas避免不足 'gasPrice': w3.eth.gas_price }) signed_trx = w3.eth.account.signTransaction(transaction, private_key) trx_hash = w3.eth.sendRawTransaction(signed_trx.rawTransaction)
额外检查点
- 确认钱包内WETH余额≥1e18(即1WETH)
- 若使用无限授权(
2**256 - 1),仅对可信合约操作,避免资产风险 - 若仍报错,可进一步调高gas值,或检查OneSplit合约是否支持当前链上的WETH-USDC兑换对
内容的提问来源于stack exchange,提问作者Edoardo Calderoni
相关产品推荐
相关产品推荐

