智能合约功能调用的 ValidationError 无明显原因(web3py)?
ValidationError on smart contract function call for no apparent reason(web3py)?
我正在尝试调用 Uniswap 的 Router 函数 swapExactTokensForETHSupportingFeeOnTransferTokens()。当我在 etherscan 上手动输入值时,它会通过。但是,当我通过 python 代码执行此操作时,它会给我一个验证错误。错误如下所示:
web3.exceptions.ValidationError: Could not identify the intended function with name swapExactTokensForETHSupportingFeeOnTransferTokens, positional argument(s) of type (<class int>, <class int>, <class list>, <class str>, <class float>) and keyword argument(s) of type {}. Found 1 function(s) with the name swapExactTokensForETHSupportingFeeOnTransferTokens: [swapExactTokensForETHSupportingFeeOnTransferTokens(uint256,uint256,address[],address,uint256)] Function invocation failed due to no matching argument types.
这是我使用的代码:
swap = uniswap_router_contract.functions.swapExactTokensForETHSupportingFeeOnTransferTokens(uint amount, 0, list_of_two_token_addresses, my_address_string, unix_time_stamp_deadline).buildTransaction({'nonce': some_nonce})
gas_amount = web3.eth.estimateGas(swap)
print(gas amount)
我是否应该以某种方式将我的整数转换为 python 中的无符号整数?我试过了,但没有解决。我正在使用 web3py 库。有人可以指导我解决问题或调用所述函数的现有代码吗?
谢谢。
编辑:
我将时间戳转换为 int,并使用 web3.toChecksum 方法确保我的地址字符串是校验和。
swap = uniswap_router_contract.functions.swapExactTokensForETHSupportingFeeOnTransferTokens(uint amount, 0, list_of_two_token_addresses, my_address_string, int(unix_time_stamp_deadline)).buildTransaction({'nonce': some_nonce})
gas = web3.eth.estimateGas(swap)
print(gas)
当我运行这个时,它给我这个错误:
raise SolidityError(response['error']['message'])
web3.exceptions.SolidityError: execution reverted: TransferHelper:
TRANSFER_FROM_FAILED
您传递的参数类型与函数的预期参数类型不匹配。
你通过了:
int, int, list, str, float
但函数需要:
uint256, uint256, address[], address, uint256
我猜是最后一个参数 unix_time_stamp_deadline
导致了不匹配。它是一个浮点数,但该函数需要一个整数。您可以在将其传递给函数时将其转换为 int,如下所示:
int(unix_time_stamp_deadline)
我在下面的文章中报告了这个问题:
https://medium.com/@italo.honorato/how-to-resolve-transferhelper-error-transfer-from-failed-fb4c8bf6488c
里面我放了案例的解决方法
我遇到了这个问题。该错误仅在使用上述功能通过智能互换合约出售代币时发生。如果您直接通过 Etherscan 上的 Uniswap V2 路由器合约进行交互,您将没有问题,因为 msg.sender 是您的外部帐户。
由于某些原因,该错误似乎不会发生在 fee-free 合约中的 swapExactTokensForTokens
等其他函数中。
请记住首先为使用 WETH 代币和您的代币 2 的交易协议提供批准。这必须在执行交易之前完成。
兑换功能代码如下:
contract ContractSwapOnUniswap{
function swap(address _tokenIn, address _tokenOut, uint256 _amountIn, uint256 _amountOutMin, address _to) external {
IERC20(_tokenIn).transferFrom(msg.sender, address(this), _amountIn);
//next we need to allow the uniswapv2 router to spend the token we just sent to this contract
//by calling IERC20 approve you allow the uniswap contract to spend the tokens in this contract
IERC20(_tokenIn).approve(UNISWAP_V2_ROUTER, _amountIn);
address[] memory path = new address[](2);
path[0] = _tokenIn;
path[1] = _tokenOut;
IUniswapV2Router(UNISWAP_V2_ROUTER).swapExactTokensForTokens(_amountIn, _amountOutMin, path, _to, block.timestamp);
}
}
阅读上面合约approve
功能的评论。
下面是ERC20标准的approve
函数:
function approve(address spender, uint256 amount) public override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
错误发生是因为无法批准 UNISWAP_V2_ROUTER
从 ContractSwapOnUniswap
地址花费代币的权限,为什么默认情况下 ERC20 批准功能无法识别 ContractSwapOnUniswap
为 msg.sender
。也就是说,发生 TransferHelper: TRANSFER_FROM_FAILED
错误是因为 UNISWAP_V2_ROUTER
在代币合约中没有花费 ContractSwapOnUniswap
.
余额的必要权限
ContractSwapOnUniswap
不可能是 msg.sender
。 msg.sender
始终是调用合约函数并发送交易的账户。
我断定这是ERC20标准本身的设计错误,适用于互换其他账户的合约。如果我错了,请指正。
也就是说,swapExactTokensForTokensSupportingFeeOnTransferTokens
如果以这种方式用于买卖显然效果不佳。
这里的问题是在相同的功能逻辑中将 transferFrom
与 approve 混合在一起。
我找到的解决方案是下面的代码:
function buy(address _tokenIn, address _tokenOut, uint256 _amountIn, uint256 _amountOutMin) public {
IERC20(_tokenIn).transferFrom(msg.sender, address(this), _amountIn);
IERC20(_tokenIn).approve(UNISWAP_V2_ROUTER, _amountIn);
address[] memory path = new address[](2);
path[0] = _tokenIn;
path[1] = _tokenOut;
IUniswapV2Router02(UNISWAP_V2_ROUTER).swapExactTokensForTokensSupportingFeeOnTransferTokens(_amountIn, _amountOutMin, path, address(this), block.timestamp);
}
function sell(address _tokenIn, address _tokenOut, uint256 _amountIn, uint256 _amountOutMin) public {
//No `transferFrom` statement needed here
//Sell only works if this line below exists
//approve below is giving permission for UNISWAP_V2_ROUTER to spend msg.sender tokens
//But who has the tokens to be spent is addres(this)
//Looks like the EVM still needs `msg.sender` to be approved
//Precisely because the transaction involves the manipulation of the token, even if it is not spent by `msg.sender`
IERC20(_tokenIn).approve(UNISWAP_V2_ROUTER, _amountIn);
address[] memory path = new address[](2);
path[0] = _tokenIn;
path[1] = _tokenOut;
IUniswapV2Router02(UNISWAP_V2_ROUTER).swapExactTokensForTokensSupportingFeeOnTransferTokens(_amountIn, _amountOutMin, path, msg.sender, block.timestamp);
}
请记住,建议插入一个函数以从您的合约中提取 ERC20 或 ETH,因为如果没有这个功能,就有可能在合约中永远丢失它们。
强文本
我正在尝试调用 Uniswap 的 Router 函数 swapExactTokensForETHSupportingFeeOnTransferTokens()。当我在 etherscan 上手动输入值时,它会通过。但是,当我通过 python 代码执行此操作时,它会给我一个验证错误。错误如下所示:
web3.exceptions.ValidationError: Could not identify the intended function with name swapExactTokensForETHSupportingFeeOnTransferTokens, positional argument(s) of type (<class int>, <class int>, <class list>, <class str>, <class float>) and keyword argument(s) of type {}. Found 1 function(s) with the name swapExactTokensForETHSupportingFeeOnTransferTokens: [swapExactTokensForETHSupportingFeeOnTransferTokens(uint256,uint256,address[],address,uint256)] Function invocation failed due to no matching argument types.
这是我使用的代码:
swap = uniswap_router_contract.functions.swapExactTokensForETHSupportingFeeOnTransferTokens(uint amount, 0, list_of_two_token_addresses, my_address_string, unix_time_stamp_deadline).buildTransaction({'nonce': some_nonce})
gas_amount = web3.eth.estimateGas(swap)
print(gas amount)
我是否应该以某种方式将我的整数转换为 python 中的无符号整数?我试过了,但没有解决。我正在使用 web3py 库。有人可以指导我解决问题或调用所述函数的现有代码吗?
谢谢。
编辑:
我将时间戳转换为 int,并使用 web3.toChecksum 方法确保我的地址字符串是校验和。
swap = uniswap_router_contract.functions.swapExactTokensForETHSupportingFeeOnTransferTokens(uint amount, 0, list_of_two_token_addresses, my_address_string, int(unix_time_stamp_deadline)).buildTransaction({'nonce': some_nonce})
gas = web3.eth.estimateGas(swap)
print(gas)
当我运行这个时,它给我这个错误:
raise SolidityError(response['error']['message']) web3.exceptions.SolidityError: execution reverted: TransferHelper: TRANSFER_FROM_FAILED
您传递的参数类型与函数的预期参数类型不匹配。
你通过了:
int, int, list, str, float
但函数需要:
uint256, uint256, address[], address, uint256
我猜是最后一个参数 unix_time_stamp_deadline
导致了不匹配。它是一个浮点数,但该函数需要一个整数。您可以在将其传递给函数时将其转换为 int,如下所示:
int(unix_time_stamp_deadline)
我在下面的文章中报告了这个问题: https://medium.com/@italo.honorato/how-to-resolve-transferhelper-error-transfer-from-failed-fb4c8bf6488c
里面我放了案例的解决方法
我遇到了这个问题。该错误仅在使用上述功能通过智能互换合约出售代币时发生。如果您直接通过 Etherscan 上的 Uniswap V2 路由器合约进行交互,您将没有问题,因为 msg.sender 是您的外部帐户。
由于某些原因,该错误似乎不会发生在 fee-free 合约中的 swapExactTokensForTokens
等其他函数中。
请记住首先为使用 WETH 代币和您的代币 2 的交易协议提供批准。这必须在执行交易之前完成。
兑换功能代码如下:
contract ContractSwapOnUniswap{
function swap(address _tokenIn, address _tokenOut, uint256 _amountIn, uint256 _amountOutMin, address _to) external {
IERC20(_tokenIn).transferFrom(msg.sender, address(this), _amountIn);
//next we need to allow the uniswapv2 router to spend the token we just sent to this contract
//by calling IERC20 approve you allow the uniswap contract to spend the tokens in this contract
IERC20(_tokenIn).approve(UNISWAP_V2_ROUTER, _amountIn);
address[] memory path = new address[](2);
path[0] = _tokenIn;
path[1] = _tokenOut;
IUniswapV2Router(UNISWAP_V2_ROUTER).swapExactTokensForTokens(_amountIn, _amountOutMin, path, _to, block.timestamp);
}
}
阅读上面合约approve
功能的评论。
下面是ERC20标准的approve
函数:
function approve(address spender, uint256 amount) public override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
错误发生是因为无法批准 UNISWAP_V2_ROUTER
从 ContractSwapOnUniswap
地址花费代币的权限,为什么默认情况下 ERC20 批准功能无法识别 ContractSwapOnUniswap
为 msg.sender
。也就是说,发生 TransferHelper: TRANSFER_FROM_FAILED
错误是因为 UNISWAP_V2_ROUTER
在代币合约中没有花费 ContractSwapOnUniswap
.
ContractSwapOnUniswap
不可能是 msg.sender
。 msg.sender
始终是调用合约函数并发送交易的账户。
我断定这是ERC20标准本身的设计错误,适用于互换其他账户的合约。如果我错了,请指正。
也就是说,swapExactTokensForTokensSupportingFeeOnTransferTokens
如果以这种方式用于买卖显然效果不佳。
这里的问题是在相同的功能逻辑中将 transferFrom
与 approve 混合在一起。
我找到的解决方案是下面的代码:
function buy(address _tokenIn, address _tokenOut, uint256 _amountIn, uint256 _amountOutMin) public {
IERC20(_tokenIn).transferFrom(msg.sender, address(this), _amountIn);
IERC20(_tokenIn).approve(UNISWAP_V2_ROUTER, _amountIn);
address[] memory path = new address[](2);
path[0] = _tokenIn;
path[1] = _tokenOut;
IUniswapV2Router02(UNISWAP_V2_ROUTER).swapExactTokensForTokensSupportingFeeOnTransferTokens(_amountIn, _amountOutMin, path, address(this), block.timestamp);
}
function sell(address _tokenIn, address _tokenOut, uint256 _amountIn, uint256 _amountOutMin) public {
//No `transferFrom` statement needed here
//Sell only works if this line below exists
//approve below is giving permission for UNISWAP_V2_ROUTER to spend msg.sender tokens
//But who has the tokens to be spent is addres(this)
//Looks like the EVM still needs `msg.sender` to be approved
//Precisely because the transaction involves the manipulation of the token, even if it is not spent by `msg.sender`
IERC20(_tokenIn).approve(UNISWAP_V2_ROUTER, _amountIn);
address[] memory path = new address[](2);
path[0] = _tokenIn;
path[1] = _tokenOut;
IUniswapV2Router02(UNISWAP_V2_ROUTER).swapExactTokensForTokensSupportingFeeOnTransferTokens(_amountIn, _amountOutMin, path, msg.sender, block.timestamp);
}
请记住,建议插入一个函数以从您的合约中提取 ERC20 或 ETH,因为如果没有这个功能,就有可能在合约中永远丢失它们。
强文本