Solidity and Truffle – UniSwap Solidity Contract: swapExactTokensForTokens Failed in Custom Smart Contract

ganache-clisoliditysushiswaptruffleuniswap

I am using sushiswap to transfer weth to dai. Everything goes fine when I use web3.js to call swapExactTokensForTokens directory.
However, thing goes wrong when I try to include the swapExactTokensForTokens in IUniswapV2Router02.sol. This is the code I'm running on Remix:

pragma solidity ^0.6.6;

import "https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/interfaces/IUniswapV2Router02.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/docs-v3.x/contracts/token/ERC20/IERC20.sol";

contract Arbitrage {
    
    IUniswapV2Router02 public sushiRouter = IUniswapV2Router02(0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506);
    
    event test (uint timestamp, uint amountIn, uint amountOut, address[] path, uint allowance, address sender);
    
    function swapper(address token1, address token2) public  {
        address[] memory path = new address[](2);
        path[0] = token1;
        path[1] = token2;
        uint amountOut = 1 ether;
        uint amountIn = sushiRouter.getAmountsIn(
            amountOut,
            path
        )[0];
        
        
        
        IERC20(token1).approve(address(sushiRouter), amountIn);
        
        uint allowed = IERC20(token1).allowance(msg.sender, address(sushiRouter));
        
        emit test(now+90, amountIn, amountOut, path, allowed, msg.sender);

        sushiRouter.swapExactTokensForTokens(
            amountIn, 
            amountOut,
            path, 
            msg.sender, 
            now + 60
        );
    }
}

The error message shown on Remix is:

Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
The execution failed due to an exception. Reverted

error in Remix

I also use truffle + ganache-cli (forked from mainnet). The error shown on truffle console is:

Uncaught:
Error: Returned error: VM Exception while processing transaction: revert TransferHelper: TRANSFER_FROM_FAILED -- Reason given: TransferHelper: TRANSFER_FROM_FAILED.

error on truffle

I believe things go wrong inside "sushiRouter.swapExactTokensForTokens()" because the code goes through and emits the event successfully once I comment it out.

It will be very appreciated if anybody can let me know in which part I messed it up.

—— Some more information below —–

After removing the swapping section like below:

...
contract Arbitrage {
    
    IUniswapV2Router02 public sushiRouter = IUniswapV2Router02(0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506);
    
    event test (uint timestamp, uint amountIn, uint amountOut, address[] path, uint allowance, address sender);
    
    function swapper(address token1, address token2) public  {
        address[] memory path = new address[](2);
        path[0] = token1;
        path[1] = token2;
        uint amountOut = 1 ether;
        uint amountIn = sushiRouter.getAmountsIn(
            amountOut,
            path
        )[0];
        
        IERC20(token1).approve(address(sushiRouter), amountIn);
        IERC20(token2).approve(address(sushiRouter), amountOut);
        
        uint allowed = IERC20(token1).allowance(msg.sender, address(sushiRouter));
        
        
        emit test(now+90, amountIn, amountOut, path, allowed, msg.sender);
    }
}

The transaction went through with the following tx details:

tx detail

—- 4/21 update —-

I might have found some clues when I am trying to research error on my ganache forked mainnet.

Google says the cause of "TRANSFER_FROM_FAILED" is that sushiswap router contract does not have enough allowance to transfer the token. So I check again the allowance field in "test event". Surprisingly, it is actually "0" even though I have already done the approve.

enter image description here

WHY?? The following line of code does not take effect?? Did I miss anything?

IERC20(token1).approve(address(sushiRouter), amountIn);

Apart from that, the swap still fails even if I manually approve 100 weth using truffle console:

truffle(development)> weth.approve(srouter.address, web3.utils.toWei("1000"))
truffle(development)> allowed = await weth.allowance(accounts[0], srouter.address)
truffle(development)> allowed.toString()
'1000000000000000000000'
truffle(development)> tx = await sw.swapper(weth.address, dai.address)
Uncaught:
Error: Returned error: VM Exception while processing transaction: revert TransferHelper: TRANSFER_FROM_FAILED -- Reason given: TransferHelper: TRANSFER_FROM_FAILED.

I am quite sure that my environment setting should not be a problem because I do similar processes manually with problem:

process below:

  1. fork the mainnet by ganache-cli
ganache-cli --fork https://mainnet.infura.io/v3/<projectID> --unlock 0x47ac0fb4f2d84898e4d9e7b4dab3c24507a6d503 -p 7545
  1. truffle console and migrate

  2. implement swaptoken on truffle console without any problem

truffle(development)> dai = await Dai.at("0x6b175474e89094c44da98b954eedeac495271d0f")
truffle(development)> weth = await Weth.at("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2")
truffle(development)> sw = await SwapTokens.deployed()

truffle(development)> srouter =await Router.at("0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F")

truffle(development)> web3.eth.sendTransaction({from:accounts[0], to: "0x47ac0fb4f2d84898e4d9e7b4dab3c24507a6d503", value:web3.utils.toWei("20")})
truffle(development)> dai.transfer(accounts[0], web3.utils.toWei("5000"), {from: "0x47ac0fb4f2d84898e4d9e7b4dab3c24507a6d503"}
truffle(development)> weth.deposit({from:accounts[0], value:web3.utils.toWei("50")})
truffle(development)> amountIn = await srouter.getAmountsIn(web3.utils.toWei("1"), [weth.address, dai.address])
truffle(development)> amountIn = amountIn[0]
truffle(development)> weth.approve(srouter.address, amountIn)
truffle(development)> amountOut = web3.utils.toWei("1")
truffle(development)> time = Math.floor((Date.now()/1000)) + 60*300
srouter.swapExactTokensForTokens(amountIn, amountOut, [weth.address, dai.address], accounts[0], time)

Everything works without issue.

But it fails when I call it in a smart contract?

Please help , I have been stuck here for so long@@

Best Answer

IERC20(token1).approve(address(sushiRouter), amountIn);
IERC20(token2).approve(address(sushiRouter), amountOut);

basically when you call approve from inside your contract then inside the approve method the msg.sender is your contract. so replace uint allowed = IERC20(token1).allowance(msg.sender, address(sushiRouter)); with uint allowed = IERC20(token1).allowance(address(this), address(sushiRouter)); and you should see the allowance and check if your contract has the token balance you want to swap or you need to give your contract the allowance to spend.