Hello all,
I wrote a smart contract, that can borrow a flashloan on balancer, make trades and repay the loan. If I run it on hardhat it works just fine, but if I deploy to the mainnet, I cannot even estimate gas for calling the function. It fails every time. So this is my smart contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IFlashLoanRecipient.sol";
import "./IBalancerVault.sol";
import "./interfaces/IUniswapV2Router02.sol";
contract FlashLoanArbitrage {
address payable private owner;
address private vault;
address private sushiRouter;
address private quickRouter;
address private apeRouter;
address public matic = 0x0000000000000000000000000000000000001010;
constructor(
address _vault,
address _sushiRouter,
address _quickRouter,
address _apeRouter
) {
owner = payable(msg.sender);
sushiRouter = _sushiRouter;
quickRouter = _quickRouter;
apeRouter = _apeRouter;
vault = _vault;
}
event FlashLoanResult(
address indexed token,
uint256 amountBorrowed,
uint256 amountProfit,
uint256 timestamp
);
modifier onlyOwner() {
require(msg.sender == owner, "Only Owner can call this function!");
_;
}
receive() external payable {}
function withdraw(IERC20 token) private {
//If contract needs to be funded, then compare here, that everytime there is enough matic here
uint256 amount = 0;
require(address(token) != address(0), "Invalid token address");
if (matic == address(token)) {
amount = address(this).balance;
require(amount > 0, "Not enough MATIC token to Withdraw");
owner.transfer(amount);
} else {
amount = token.balanceOf(address(this));
require(amount > 0, "Not enough token to Withdraw");
token.transfer(owner, amount);
}
}
function withdrawOnlyOwner(address token) external payable onlyOwner {
uint256 amount = 0;
require(address(token) != address(0), "Invalid token address");
if (matic == token) {
amount = address(this).balance;
require(amount > 0, "Not enough MATIC token to WithdrawOnlyOwner");
payable(msg.sender).transfer(amount);
} else {
IERC20 token_transfer = IERC20(token);
amount = token_transfer.balanceOf(address(this));
require(amount > 0, "Not enough token to WithdrawOnlyOwner");
token_transfer.transfer(payable(msg.sender), amount);
}
}
function changeAddresses(
address _quickRouter,
address _sushiRouter,
address _apeRouter,
address _vault
) external onlyOwner {
sushiRouter = _sushiRouter;
quickRouter = _quickRouter;
apeRouter = _apeRouter;
vault = _vault;
}
function balance(address token_address) public view returns (uint256) {
uint256 Balance = IERC20(token_address).balanceOf(address(this));
return Balance;
}
function swapper(
address dex_router,
uint256 _amountIn,
uint256 _amountOutMin,
address _tokenIn,
address _tokenOut,
uint256 _deadline
) private returns (uint256) {
//Zum testen muss das womöglich auf public gesetzt werden
require(
IERC20(_tokenIn).approve(dex_router, _amountIn),
"Approve for Swapping failed."
);
require(
_amountIn <= balance(_tokenIn),
"Error: Actual amount received is less than expected"
); // check if actual amount is less than expected
address[] memory path;
path = new address[](2);
path[0] = _tokenIn;
path[1] = _tokenOut;
uint256 deadline = block.timestamp + _deadline;
uint256[] memory amounts = IUniswapV2Router02(dex_router)
.swapExactTokensForTokens(
_amountIn,
_amountOutMin,
path,
address(this),
deadline
);
require(amounts[1] > 0, "Output amount should be greater than 0");
return amounts[1];
}
function receiveFlashLoan(
IERC20[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytes memory data
) external {
address dexA;
address dexB;
address[] memory trade_tokens;
uint24 fees;
uint256[] memory outputs;
(dexA, dexB, trade_tokens, fees, outputs) = abi.decode(
data,
(address, address, address[], uint24, uint256[])
);
for (uint256 i = 0; i < tokens.length; ++i) {
IERC20 token = tokens[i];
uint256 amount = amounts[i] + feeAmounts[i];
uint256 amountOut0 = 0;
uint256 amountOut1 = 0;
amountOut0 = swapper(
dexA,
outputs[0],
outputs[1],
trade_tokens[0],
trade_tokens[1],
3000
);
amountOut1 = swapper(
dexB,
balance(trade_tokens[1]),
0,
trade_tokens[1],
trade_tokens[0],
3000
);
emit FlashLoanResult(
trade_tokens[0],
amounts[i],
balance(trade_tokens[1]),
(block.timestamp + 3000)
);
token.transfer(vault, amount);
withdraw(tokens[i]);
}
}
function flashLoan(
IERC20[] memory tokens,
uint256[] memory amounts,
bytes memory userData
) external onlyOwner {
IBalancerVault(vault).flashLoan(
IFlashLoanRecipient(address(this)),
tokens,
amounts,
userData
);
}
}
and that is how I am estimating the gas I need:
require("dotenv").config();
const { ethers } = require("ethers");
const flashloanABI = require("./abis/flashloan_abi.json");
const abiCoder = ethers.utils.defaultAbiCoder;
const FLASHLOAN = process.env.CONTRACT_ADDRESS;
const POLYGON_MAINNET_QUICKNODE= process.env.POLYGON_MAINNET_QUICKNODE;
const provider = new ethers.providers.JsonRpcProvider(
POLYGON_MAINNET_QUICKNODE
);
const flash_contract = new ethers.Contract(FLASHLOAN, flashloanABI, provider);
async function estimateGas(amountIn_wei, tokenIn_address, data) {
console.log("We are calculating");
try {
const functionData = flash_contract.interface.encodeFunctionData(
"flashLoan",
[[tokenIn_address], [amountIn_wei], data]
);
const gasAmount = await provider.estimateGas({
from: "0x5C42F93937389e73A7317e2108cbcFdECf658002",
to: FLASHLOAN,
data: functionData,
});
console.log(`Estimated gas cost: ${gasAmount}`);
} catch (e) {
console.log(e);
}
console.log("Estimated gas limit:", gasEstimate.toString());
return gasLimit;
}
async function testingGas() {
const tokenIn = "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619";
const amountIn = ethers.utils.parseUnits("1", 18);
const data = abiCoder.encode(
["address", "address", "address[]", "uint24", "uint256[]"],
[
router_addr_quickswap,
router_addr_sushiswap,
[tokenIn, "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270"],
0,
[amountIn, 0, 0, 0, 0],
]
);
const gas = await estimateGas(amountIn, tokenIn, data);
}
testingGas();
The error is this:
Error: cannot estimate gas; transaction may fail or may require manual gas limit [ See: https://links.ethers.org/v5-errors-UNPREDICTABLE_GAS_LIMIT ] (reason="execution reverted", method="estimateGas", transaction={"from":"0x55A535B67Baa225101fbb859f1609dFD3970af68","to":"0xd4FbfF04437e6FdE782796401bbEd8c4b20FCA72","data":"0x788fb484000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000055a535b67baa225101fbb859f1609dfd3970af68","accessList":null}, error={"reason":"processing response error","code":"SERVER_ERROR","body":"{\"jsonrpc\":\"2.0\",\"id\":67,\"error\":{\"code\":-32000,\"message\":\"execution reverted\"}}\n","error":{"code":-32000},"requestBody":"{\"method\":\"eth_estimateGas\",\"params\":[{\"from\":\"0x55a535b67baa225101fbb859f1609dfd3970af68\",\"to\":\"0xd4fbff04437e6fde782796401bbed8c4b20fca72\",\"data\":\"0x788fb484000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000055a535b67baa225101fbb859f1609dfd3970af68\"}],\"id\":67,\"jsonrpc\":\"2.0\"}","requestMethod":"POST","url":"https://skilled-sly-dream.matic.discover.quiknode.pro/.../"}, code=UNPREDICTABLE_GAS_LIMIT, version=providers/5.7.1)
at Logger.makeError (c:\Users\danis\arb_gpt\node_modules\@ethersproject\logger\lib\index.js:238:21)
at Logger.throwError (c:\Users\danis\arb_gpt\node_modules\@ethersproject\logger\lib\index.js:247:20)
at checkError (c:\Users\danis\arb_gpt\node_modules\@ethersproject\providers\lib\json-rpc-provider.js:122:20)
at JsonRpcProvider.<anonymous> (c:\Users\danis\arb_gpt\node_modules\@ethersproject\providers\lib\json-rpc-provider.js:751:47)
at step (c:\Users\danis\arb_gpt\node_modules\@ethersproject\providers\lib\json-rpc-provider.js:48:23)
at Object.throw (c:\Users\danis\arb_gpt\node_modules\@ethersproject\providers\lib\json-rpc-provider.js:29:53)
at rejected (c:\Users\danis\arb_gpt\node_modules\@ethersproject\providers\lib\json-rpc-provider.js:21:65)
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
reason: 'execution reverted',
code: 'UNPREDICTABLE_GAS_LIMIT',
method: 'estimateGas',
transaction: {
from: '0x55A535B67Baa225101fbb859f1609dFD3970af68',
to: '0xd4FbfF04437e6FdE782796401bbEd8c4b20FCA72',
data: '0x788fb484000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000055a535b67baa225101fbb859f1609dfd3970af68',
accessList: null
},
error: Error: processing response error (body="{\"jsonrpc\":\"2.0\",\"id\":67,\"error\":{\"code\":-32000,\"message\":\"execution reverted\"}}\n", error={"code":-32000}, requestBody="{\"method\":\"eth_estimateGas\",\"params\":[{\"from\":\"0x55a535b67baa225101fbb859f1609dfd3970af68\",\"to\":\"0xd4fbff04437e6fde782796401bbed8c4b20fca72\",\"data\":\"0x788fb484000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000055a535b67baa225101fbb859f1609dfd3970af68\"}],\"id\":67,\"jsonrpc\":\"2.0\"}", requestMethod="POST", url="https://skilled-sly-dream.matic.discover.quiknode.pro/.../", code=SERVER_ERROR, version=web/5.7.1)
at Logger.makeError (c:\Users\danis\arb_gpt\node_modules\@ethersproject\logger\lib\index.js:238:21)
at Logger.throwError (c:\Users\danis\arb_gpt\node_modules\@ethersproject\logger\lib\index.js:247:20)
at c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:313:32
at step (c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:33:23)
at Object.next (c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:14:53)
at fulfilled (c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:5:58)
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
reason: 'processing response error',
code: 'SERVER_ERROR',
body: '{"jsonrpc":"2.0","id":67,"error":{"code":-32000,"message":"execution reverted"}}\n',
error: Error: execution reverted
at getResult (c:\Users\danis\arb_gpt\node_modules\@ethersproject\providers\lib\json-rpc-provider.js:191:21)
at processJsonFunc (c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:356:22)
at c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:288:46
at step (c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:33:23)
at Object.next (c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:14:53)
at fulfilled (c:\Users\danis\arb_gpt\node_modules\@ethersproject\web\lib\index.js:5:58)
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
code: -32000,
data: undefined
},
requestBody: '{"method":"eth_estimateGas","params":[{"from":"0x55a535b67baa225101fbb859f1609dfd3970af68","to":"0xd4fbff04437e6fde782796401bbed8c4b20fca72","data":"0x788fb484000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000055a535b67baa225101fbb859f1609dfd3970af68"}],"id":67,"jsonrpc":"2.0"}',
requestMethod: 'POST',
url: 'https://skilled-sly-dream.matic.discover.quiknode.pro/.../'
}
}
The strange thing is, when I fork polygon mainnet and do tests with hardhat, everything is working fine, but on mainnet it is not working...
I hope someone can help me. Thank you!