After days of failures and countless searches, i ask for help to understand why my flash swap contract returns the "TransferHelper::transferFrom: transferFrom failed" error, the error occurs in uniswapV2Call -> swapExactTokensForTokens -> TransferHelper.safeTransferFrom when i try to sell the borrowed tokens from Uniswap to Sushiswap. This is the contract:
// SPDX-License-Identifier: MIT
pragma solidity =0.6.6;
import "@uniswap/v2-periphery/contracts/libraries/UniswapV2Library.sol";
import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Callee.sol";
import "@uniswap/v2-core/contracts/interfaces/IERC20.sol";
import "@uniswap/lib/contracts/libraries/TransferHelper.sol";
// flash swap contract
contract FlashSwap is IUniswapV2Callee {
address public owner;
constructor() public {
owner = msg.sender;
}
using SafeMath for uint256;
address private constant UniswapV2Factory = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
address public constant SUSHISWAP_V2_ROUTER = 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506;
IUniswapV2Router02 private sushi_router = IUniswapV2Router02(SUSHISWAP_V2_ROUTER);
function executeTrade(address token0, address token1, uint amount0, uint amount1) public {
address pairAddress = IUniswapV2Factory(UniswapV2Factory).getPair(token0, token1);
require(pairAddress != address(0), "Could not find pool on uniswap");
bytes memory data = abi.encode(pairAddress);
IUniswapV2Pair(pairAddress).swap(amount0, amount1, address(this), data);
}
function uniswapV2Call(address _sender, uint _amount0, uint _amount1, bytes calldata _data) override external {
address[] memory pathA = new address[](2);
address[] memory pathB = new address[](2);
uint amountTokenBorrowed = _amount0 == 0 ? _amount1 : _amount0;
address token0 = IUniswapV2Pair(msg.sender).token0();
address token1 = IUniswapV2Pair(msg.sender).token1();
require(msg.sender == UniswapV2Library.pairFor(UniswapV2Factory, token0, token1), "Invalid Request");
require(_sender == address(this), "!sender");
require(_amount0 == 0 || _amount1 == 0);
// create and populate path array for sushiswap swapExactTokensForTokens function.
pathA[0] = _amount0 == 0 ? token1 : token0;
pathA[1] = _amount0 == 0 ? token0 : token1;
// create and populate path array for uniswap getAmountsIn function.
pathB[0] = _amount0 == 0 ? token0 : token1;
pathB[1] = _amount0 == 0 ? token1 : token0;
IERC20 token = IERC20(_amount0 == 0 ? token1 : token0);
token.approve(address(sushi_router), amountTokenBorrowed);
// calculate the amount of tokens we need to reimburse uniswap for the flashloan
uint amountRequired = UniswapV2Library.getAmountsIn(UniswapV2Factory, amountTokenBorrowed, pathB)[0];
// sell borrowed tokens on sushiswap
uint amountReceived = sushi_router.swapExactTokensForTokens(amountTokenBorrowed, amountRequired, pathA, address(this), block.timestamp + 60)[1];
require(amountReceived > amountRequired, "amountReceived <= amountRequired!");
IERC20 outputToken = IERC20(_amount0 == 0 ? token0 : token1);
// amount to payback flashloan
// amountRequired is the amount we need to payback
outputToken.transfer(msg.sender, amountRequired);
// send profit (remaining tokens)
outputToken.transfer(owner, amountReceived - amountRequired);
}
}
I checked with Tenderly the input values to swapExactTokensForTokens and they are correct:
{
"amountIn": "100000000000000000000",
"amountOutMin": "89506386512945284445",
"path": [
"0xTokenA_borrowed", //just hiding the real addr
"0xTokenB_to_receive" //just hiding the real addr
],
"to": "0xFlashSwap", //just hiding the real addr
"deadline": "1676458833"
}
While getAmountsOut(inside swapExactTokensForTokens) returns 93238998313501573286 which is enough to cover the amountRequired.
I also gave several approvals(approve) before, just to be sure to cover any need, here the from -> to:
TokenA -> FlashSwap
TokenB -> FlashSwap
TokenA -> sushi router
TokenB -> sushi router
TokenA -> uni router
TokenB -> uni router
TokenA -> uni pair
TokenB -> uni pair
TokenA -> sushi pair
TokenB -> sushi pair
uni pair -> FlashSwap
sushi pair -> FlashSwap
These are the TransferHelper.safeTransferFrom(inside swapExactTokensForTokens) input values:
{
"token": "0xTokenA_borrowed",
"from": "Uniswap Pair", //just hiding the real addr
"to": "Sushiswap Pair", //just hiding the real addr
"value": "100000000000000000000"
}
Than output is "execution reverted".
What could be the problem?