【问题标题】:Error: VM Exception while processing transaction: reverted with reason string 'TransferHelper: TRANSFER_FROM_FAILED'错误:处理事务时出现 VM 异常:使用原因字符串 \'TransferHelper: TRANSFER_FROM_FAILED\' 还原
【发布时间】:2022-08-08 10:13:37
【问题描述】:

非常感谢您的时间和帮助

我想在 uniswap 和 sushiswap 之间运行 flashloan。但是,尽管尝试了类似问题的答案中提到的几种潜在解决方案,但我得到了这个错误并且它仍然存在。我是通过 vscode 用安全帽编写的。

这是错误:错误:处理事务时出现 VM 异常:使用原因字符串 \'TransferHelper: TRANSFER_FROM_FAILED\' 还原

这是坚固性代码:

// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import \"hardhat/console.sol\";

// Uniswap interface and library imports
import \"./libraries/UniswapV2Library.sol\";
import \"./libraries/SafeERC20.sol\";
import \"./interfaces/IUniswapV2Router02.sol\";
import \"./interfaces/IUniswapV2Pair.sol\";
import \"./interfaces/IUniswapV2Factory.sol\";
import \"./interfaces/IERC20.sol\";

contract FlashloanSwap {
    using SafeERC20 for IERC20;

    // Define the factory and router addresses of the DEXs
    address private constant UNISWAP_FACTORY =
        0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
    address private constant UNISWAP_ROUTER =
        0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
    address private constant SUSHISWAP_FACTORY =
        0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac;
    address private constant SUSHISWAP_ROUTER =
        0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F;

// Trade variables
uint256 private deadline = block.timestamp + 1 days;

// Get the contract balance of token
function getBalanceOfToken(address _address) public view returns (uint256) {
    return IERC20(_address).balanceOf(address(this));
}


function checkProfitability(uint256 _input, uint256 _outPut)
    private
    pure
    returns (bool)
{
    return _outPut > _input;
}

// There must be a function to receive ETH
receive() external payable {}
fallback() external payable {}

// Eth balance needs to be checked sometimes
function getBalance() public view returns (uint256) {
    return address(this).balance;
}

// Define a function to trade a given token in exchange for another token on a given DEX
function executeTrade(
    address _fromToken,
    address _toToken,
    uint256 _amountIn,
    address factory,
    address router
) private returns (uint256) {
    address pair = IUniswapV2Factory(factory).getPair(_fromToken, _toToken);
    require(pair != address(0), \"Pool does not exist\");

    // Calculate amount out
    address[] memory path = new address[](2);
    path[0] = _fromToken;
    path[1] = _toToken;

    uint256 amountRequired = IUniswapV2Router01(router).getAmountsOut(
        _amountIn,
        path
    )[1];

    console.log(\"Amount Required: \", amountRequired);

    // Perform arbitrage - Swap for another token
    IUniswapV2Router02(router)
        .swapExactTokensForTokensSupportingFeeOnTransferTokens(
            _amountIn,
            amountRequired,
            path,
            address(this),
            deadline
        );

    uint256 balanceA = getBalanceOfToken(_fromToken);
    uint256 balanceB = getBalanceOfToken(_toToken);

    uint256 amountReceived = balanceA == 0 ? balanceB : balanceA;

    console.log(\"amountReceived: \", amountReceived);

    require(amountReceived > 0, \"Aborted Transaction\");
    return amountReceived;
}

/* 
I) It will be run whenever an arbitrage opportunity is detected 
*/
function runTheArbitrage(
    address tokenA,
    address tokenB,
    uint256 amountA,
    uint256 amountB
) external {
    // Get the pair address on uniswap
    address pairAddress = IUniswapV2Factory(UNISWAP_FACTORY).getPair(
        tokenA,
        tokenB
    );

    
    // Check whether the pair exists
    require(
        pairAddress != address(0),
        \"The pair does not exist on uniswap\"
    );

           
    // Save the borrowed token\'s specifications in _data to be passed to uniswapV2Call
    address borrowedTokenAddress = amountA == 0 ? tokenB : tokenA;
    uint256 borrowedTokenAmount = amountA == 0 ? amountB : amountA;
    bytes memory data = abi.encode(
        borrowedTokenAddress,
        borrowedTokenAmount
    );

    // Create the flashloan with the swap function
    IUniswapV2Pair(pairAddress).swap(amountA, amountB, address(this), data);

}

/* 
II) With executing the previous function, uniswap will call this function in order to complete the flashloan cycle
*/
function uniswapV2Call(
    address _sender,
    uint256 _amountA,
    uint256 _amountB,
    bytes calldata _data
) external {
    
    // get the specifications of the borrowed token
    address token0 = IUniswapV2Pair(msg.sender).token0();
    address token1 = IUniswapV2Pair(msg.sender).token1();
    (address borrowedTokenAddress, uint256 borrowedTokenAmount) = abi
        .decode(_data, (address, uint256));
    token0 = token0 == borrowedTokenAddress ? token0 : token1;
    token1 = token0 == borrowedTokenAddress ? token1 : token0;


    // Check whether this function is called only by the pair contracts of uniswap
    require(
        msg.sender ==
            UniswapV2Library.pairFor(UNISWAP_FACTORY, token0, token1),
        \"Only requests from uniswap pair contracts are accepted\"
    );


    // Check whether this contract is the sender
    require(_sender == address(this), \"Sender should match this contract\");

    // Check one of the amounts to be zero
    require(
        _amountA == 0 || _amountB == 0,
        \"One of the amounts must be zero\"
    );


    // Execute the first swap on source DEX
    IERC20(token0).safeIncreaseAllowance(
        UNISWAP_ROUTER,
        borrowedTokenAmount
    );
    uint256 firstAmountOut = executeTrade(
        token0,
        token1,
        borrowedTokenAmount,
        UNISWAP_FACTORY,
        UNISWAP_ROUTER
    );

    // Aprove the second DEX to spend the swapped token, then execute the trade on it
    IERC20(token1).safeIncreaseAllowance(SUSHISWAP_ROUTER, firstAmountOut);
    uint256 secondAmountOut = executeTrade(
        token1,
        token0,
        firstAmountOut,
        SUSHISWAP_FACTORY,
        SUSHISWAP_ROUTER
    );


    uint256 fee = ((borrowedTokenAmount * 3) / 997) + 1;
    uint256 amountToBePaidBack = borrowedTokenAmount + fee;

    
    // Check profitability
    bool profCheck = checkProfitability(
        amountToBePaidBack,
        secondAmountOut
    );
    require(profCheck, \"Arbitrage not profitable\");

    
    // Pay back the loan
    bool success1 = IERC20(token0).transfer(msg.sender, amountToBePaidBack);


    // Send the profit to the initiator of the transaction
    bool success2 = IERC20(token0).transfer(
        tx.origin,
        secondAmountOut - amountToBePaidBack
    );

    console.log(secondAmountOut - amountToBePaidBack, success2);
}
}

这里还有用于安全帽测试的 hardhat.config.js 文件

const { version } = require(\"chai\");

require(\"@nomiclabs/hardhat-waffle\");
require(\'dotenv\').config();



module.exports = {
  solidity: {
    compilers: [
      {version: \'0.5.5\'}, 
      {version: \'0.6.6\'}, 
      {version: \'0.8.8\'},
    ],
  },
  networks: {
    hardhat: {
      forking: {
        url: process.env.alchemy_mainnet_key,
      },
    },
    testnet: {
      url: process.env.alchemy_renkiby_api,
      chainId: 97,
      accounts: [
        process.enc.test_private_key
      ],
    },
    mainnet: {
      url: process.env.alchemy_mainnet_key,
      chainId: 56,
      accounts: [
        process.env.private_key
      ],
    },
  },
};

最后,这是我的测试代码

const { expect } = require(\"chai\");
const { ethers, waffle } = require(\"hardhat\");
const { deployContract } = require(\"ethereum-waffle\");
const provider = waffle.provider;
const { abi } = require(\'../artifacts/contracts/interfaces/IERC20.sol/IERC20.json\');

describe(\"Checking the whole arbitrage process\", function () {

  // Get the factory and router addresses
  const UNISWAP_FACTORY = \"0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f\";
  const UNISWAP_ROUTER = \"0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D\";
  const SUSHI_FACTORY = \"0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac\";
  const SUSHI_ROUTER = \"0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F\";

  // Token addresses
  const tokenA = \"0x6B175474E89094C44Da98b954EedeAC495271d0F\";
  const tokenB = \"0x0F5D2fB29fb7d3CFeE444a200298f468908cC942\";

  // Get the decimals
  const decimals = 18;

  beforeEach(async function () {

    // Get owner as signer
    [owner] = await ethers.getSigners();

    // Deploy smart contract
    const FlashloanSwap = await ethers.getContractFactory(\"FlashloanSwap\");
    flashloanSwap = await FlashloanSwap.deploy();
    await flashloanSwap.deployed();

    console.log(\'\\n\', \"Contract is deployed by: \", owner.address);
    console.log(\"contract is deployed to: \", flashloanSwap.address, \'\\n\');

    const transactionHash = await owner.sendTransaction({
      to: flashloanSwap.address,
      value: ethers.utils.parseEther(\"1.0\"),
    });
    console.log(\"transactionHash : \", transactionHash);

    balanceOfEth = await provider.getBalance(flashloanSwap.address)
    balanceOfEth = ethers.utils.formatUnits(balanceOfEth, 18);
    console.log(\'\\n\', \"Balance of ETH before transaction : \", balanceOfEth.toString(), \'\\n\');
  });

  
  it(\"Check Whether Swap Occurs or Not\", async () => {
    await flashloanSwap.runTheArbitrage(tokenA, tokenB, 0, 100);
    
    balanceOfEth = await provider.getBalance(flashloanSwap.address)
    balanceOfEth = ethers.utils.formatEther(balanceOfEth, 18);
    
    console.log(\'\\n\', \"Balance of ETH after transaction : \", balanceOfEth, \'\\n\');
  });

    标签: solidity hardhat


    【解决方案1】:

    当您试图偿还贷款 + 费用时,您将每个资产的金额从您的 FlashloanSwap 转移到 Uniswap。是否会因为您必须允许 Uniswap 从您的帐户中提取贷款 + 费用而引发异常,即您在 uniswap 上调用批准,而 uniswap 在您的帐户上调用 transferFrom?

    尝试更换:

    IERC20(token0).transfer(msg.sender, amountToBePaidBack);`
    

    和:

    IERC20(token0).approve(msg.sender, amountToBePaidBack);
    

    【讨论】:

    • 感谢您的回答。事实上,当你在 uniswap 上调用流动资金池进行闪电贷时,它会在你的智能合约上调用一个名为 uniswapV2Call 的函数,该函数仅在一个区块和一次交易中完成所有套利。我尝试了您的建议,但不幸的是它不起作用并且错误仍然存​​在。
    【解决方案2】:

    100% 确定这是一个配额问题,您没有批准以下代币:

    
     IERC20(WETH).approve(routerA, amount);
    
            IUniswapV2Router02(routerA).swapExactTokensForTokensSupportingFeeOnTransferTokens(
                amount, 
                0, 
                path2, 
                cttAddress, 
                block.timestamp + 1200
            );      
    
    

    【讨论】:

      猜你喜欢
      • 2021-07-10
      • 2021-11-23
      • 2022-06-17
      • 2022-08-22
      • 1970-01-01
      • 2022-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多