智能合约功能调用的 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_ROUTERContractSwapOnUniswap 地址花费代币的权限,为什么默认情况下 ERC20 批准功能无法识别 ContractSwapOnUniswapmsg.sender。也就是说,发生 TransferHelper: TRANSFER_FROM_FAILED 错误是因为 UNISWAP_V2_ROUTER 在代币合约中没有花费 ContractSwapOnUniswap.

余额的必要权限

ContractSwapOnUniswap 不可能是 msg.sendermsg.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,因为如果没有这个功能,就有可能在合约中永远丢失它们。

强文本