I created the contract and used Remix to deploy it for arbitrage purposes. Almost all functions—executeFlashSwap, swapOnSushiSwap, and swapOnUniswap—succeed when I call them independently. However, when I combine them into a single code, it fails and raises the following error:
"Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending? Cannot read properties of undefined (reading 'includes')"
I hope you can help me resolve this issue. Thank you so much!
Here is my code.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Callee.sol";
import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
// import "@sushiswap/core/contracts/uniswapv2/interfaces/IUniswapV2Router02.sol";
contract ArbitrageBot is IUniswapV2Callee {
IUniswapV2Factory private factory;
IUniswapV2Router02 private sushiSwapRouter;
IUniswapV2Router02 private uniswapRouter;
IERC20 private tokenA;
IERC20 private tokenB;
address private pair;
uint256 public amountToRepay;
constructor(address _factory, address _sushiSwapRouter, address _uniswapRouter, address _tokenA, address _tokenB) {
factory = IUniswapV2Factory(_factory);
sushiSwapRouter = IUniswapV2Router02(_sushiSwapRouter);
uniswapRouter = IUniswapV2Router02(_uniswapRouter);
tokenA = IERC20(_tokenA);
tokenB = IERC20(_tokenB);
pair = factory.getPair(address(tokenA), address(tokenB));
}
// Initiate the flashswap
function executeFlashSwap(uint256 amountTokenA) external {
require(pair != address(0), "Pair doesn't exist");
bytes memory data = abi.encode(tokenA, msg.sender);
IUniswapV2Pair(pair).swap(0, amountTokenA, address(this), data);
}
// This is the callback function triggered by the flashswap
function uniswapV2Call(
address sender,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external override {
require(msg.sender == pair, "Invalid pair caller");
require(sender == address(this), "Invalid sender");
(address tokenBorrow, address initiator) = abi.decode(data, (address, address));
uint256 amountBorrowed = amount1; // Assuming tokenA is the borrowed token
uint256 fee = (amountBorrowed * 3) / 997 + 1; // Flash swap fee
amountToRepay = amountBorrowed + fee;
// Step 1: Swap token A (borrowed) to token B on SushiSwap
uint256 amountTokenB = swapOnSushiSwap(tokenA, tokenB, amountBorrowed);
// Step 2: Swap token B back to token A on Uniswap
uint256 amountTokenAReceived = swapOnUniswap(tokenB, tokenA, amountTokenB);
// Step 3: Check for profit and repay
require(amountTokenAReceived > amountToRepay, "No arbitrage profit");
tokenA.transfer(pair, amountToRepay); // Repay the flashloan
tokenA.transfer(initiator, amountTokenAReceived - amountToRepay); // Profit
}
// Helper function to swap tokens on SushiSwap
function swapOnSushiSwap(
IERC20 _tokenIn,
IERC20 _tokenOut,
uint256 amountIn
) internal returns (uint256) {
_tokenIn.approve(address(sushiSwapRouter), amountIn);
address[] memory path = new address[](2);
path[0] = address(_tokenIn);
path[1] = address(_tokenOut);
uint256[] memory amountsOut = sushiSwapRouter.swapExactTokensForTokens(
amountIn,
1, // minimum amount out (can be adjusted)
path,
address(this),
block.timestamp + 1200 // 20 minute deadline
);
return amountsOut[1]; // amount of token B received
}
// Helper function to swap tokens on Uniswap
function swapOnUniswap(
IERC20 _tokenIn,
IERC20 _tokenOut,
uint256 amountIn
) internal returns (uint256) {
_tokenIn.approve(address(uniswapRouter), amountIn);
address[] memory path = new address[](2);
path[0] = address(_tokenIn);
path[1] = address(_tokenOut);
uint256[] memory amountsOut = uniswapRouter.swapExactTokensForTokens(
amountIn,
1, // minimum amount out (can be adjusted)
path,
address(this),
block.timestamp + 1200 // 20 minute deadline
);
return amountsOut[1]; // amount of token A received
}
}