I've been working on a smart contract and I'm trying to perform a swap on uniswap using polygon mainnet but I'm struggling to get it to work,
This is the solidity code I'm using:
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.7.6;
pragma abicoder v2;
import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
//import "https://github.com/Uniswap/v2-periphery/blob/master/contracts/interfaces/IUniswapV2Router02.sol";
import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
//import "https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/TransferHelper.sol";
import '@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';
//import "https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/ISwapRouter.sol";
contract FlashLoan {
address public constant LINK = 0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39;
address public constant USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174;
IERC20 public usdcToken = IERC20(USDC);
IERC20 public linkToken = IERC20(LINK);
IUniswapV2Router02 public quickswapRouter = IUniswapV2Router02(0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff);
address public constant uniswapRouter = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
ISwapRouter public immutable routerUniswapInterficie = ISwapRouter(uniswapRouter);
uint24 public constant poolFee = 3000;
address payable owner;
constructor(){
owner = payable(msg.sender);
}
function firstSwap(uint256 amountIn) external returns (uint256 amountOut){
usdcToken.approve(uniswapRouter, amountIn); //aquest es uniswap!
ISwapRouter.ExactInputSingleParams memory params =
ISwapRouter.ExactInputSingleParams({
tokenIn: USDC,
tokenOut: LINK,
fee: poolFee,
recipient: address(this),//qui rep els tokens
deadline: block.timestamp,
amountIn: amountIn,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
});
// The call to `exactInputSingle` executes the swap.
return routerUniswapInterficie.exactInputSingle(params);
}
function getBalance(address _tokenAddress) external view returns (uint256) {
return IERC20(_tokenAddress).balanceOf(address(this));
}
function withdrawVell(address _tokenAddress) external onlyOwner {
IERC20 token = IERC20(_tokenAddress);
token.transfer(msg.sender, token.balanceOf(address(this)));
}
modifier onlyOwner() {
require(
msg.sender == owner,
"Only the contract owner can call this function"
);
_;
}
receive() external payable {}
}
I've tried this code on remix with all the token address and router address hardcoded and it works perfectly, i can get the balance of a certain token available in my contract and i can swap the USDC i have on it to LINK and im using the approve function to allow the router to spend my USDC but the twist comes when i try to call the firstSwap() function from my javascript file so i can perform a swap with a call of a script. This is the javascript code im trying to run:
const { ethers, network } = require("hardhat");
const fs = require("fs");
require("dotenv").config();
let config,arb,owner,inTrade,balance;
config = require('./../config/polygon.json');
const contract = require("../artifacts/contracts/FlashLoan.sol/FlashLoan.json");
const infuraProvider = new ethers.providers.InfuraProvider("matic", process.env.PROJECT_ID);
const signer = new ethers.Wallet(process.env.PRIVATE_KEY,infuraProvider);
const FlashLoanContract = new ethers.Contract(config.arbContract, contract.abi, signer);
const main = async() =>{
//await setup();
//await getBalance();
//await withdraw(baseToken);
await performFirstSwap();
}
/*
const getBalance = async () => {
balance= await arb.getBalance(config.bases[0].address);
console.log(`Balance is: ${balance}`);
}
const setup = async () => {
[owner] = await ethers.getSigners();
console.log(`Owner: ${owner.address}`);
const IArb = await ethers.getContractFactory('FlashLoan');
arb = await IArb.attach(config.arbContract);//aqui tinc el meu contracte
}
async function withdraw(baseTokenw){
try{
const provider = new ethers.providers.InfuraProvider("matic", process.env.PROJECT_ID);
const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
// Conecta el contrato con el signer
const connectedContract = FlashLoanContract.connect(signer);
const gasPrice = ethers.utils.parseUnits("100", "gwei");
// Llama a la función firstSwap
const tx = await connectedContract.withdrawVell(baseTokenw, {gasLimit: 150000, gasPrice});
await tx.wait();
}
catch(error){
console.log(error);
}
}*/
async function performFirstSwap() {
console.log("Starting the swap");
try {
const gasPrice = ethers.utils.parseUnits("100", "gwei");
const tx = await FlashLoanContract.firstSwap(400000, {gasLimit: 150000, gasPrice});
await tx.wait();
console.log("Swap transaction successful:");
} catch (error) {
console.error("Error in the transaction:", error);
}
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
i've tried with all the functions and they all work like a charm, (withdraw and getBalance) so i know the right contract is connected but when im trying to use the firstSwap call it fails and gives me the following error:
Error in the transaction: Error: transaction failed [ See: https://links.ethers.org/v5-errors-CALL_EXCEPTION ] (transactionHash="0xc5f2e8393d04a254d243330f869bde0c054ef736994cd49382b62ed13d8a3995", transaction={"type":2,"chainId":137,"nonce":135,"maxPriorityFeePerGas":{"type":"BigNumber","hex":"0x174876e800"},"maxFeePerGas":{"type":"BigNumber","hex":"0x174876e800"},"gasPrice":null,"gasLimit":{"type":"BigNumber","hex":"0x0249f0"},"to":"0x7A292CEF5D1b0c55177427719602027afAeBfE04","value":{"type":"BigNumber","hex":"0x00"},"data":"0xfc57fea80000000000000000000000000000000000000000000000000000000000061a80","accessList":[],"hash":"0xc5f2e8393d04a254d243330f869bde0c054ef736994cd49382b62ed13d8a3995","v":1,"r":"0xcca4b3d0a68acc7fda29acc589df48522c71aaad499cd08c433817586c35aace","s":"0x328cbcda1a9676709627e43898faa58170cc738c9340c87120b380ff302097a0","from":"0x223c02bf20B580f234dE37745d1C7a81AC2E5D84","confirmations":0}, receipt={"to":"0x7A292CEF5D1b0c55177427719602027afAeBfE04","from":"0x223c02bf20B580f234dE37745d1C7a81AC2E5D84","contractAddress":null,"transactionIndex":44,"gasUsed":{"type":"BigNumber","hex":"0x023ff9"},"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000800000000000000000000100000000000000000000000000000000002000000000000000000100000080000000000000000000004000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000020000000004000000000000000000001000000000000000000000000000000100000000000000000000000800000000000000000000000080000000000000000000000100000","blockHash":"0x48aa29ee072ff33f9f4b8fb5b40ca889354cfa4420fa939b1b3193ee048b5a1f","transactionHash":"0xc5f2e8393d04a254d243330f869bde0c054ef736994cd49382b62ed13d8a3995","logs":[{"transactionIndex":44,"blockNumber":47188057,"transactionHash":"0xc5f2e8393d04a254d243330f869bde0c054ef736994cd49382b62ed13d8a3995","address":"0x0000000000000000000000000000000000001010","topics":["0x4dfe1bbbcf077ddc3e01291eea2d5c70c2b422b415d95645b9adcfd678cb1d63","0x0000000000000000000000000000000000000000000000000000000000001010","0x000000000000000000000000223c02bf20b580f234de37745d1c7a81ac2e5d84","0x000000000000000000000000eedba2484aaf940f37cd3cd21a5d7c4a7dafbfc0"],"data":"0x0000000000000000000000000000000000000000000000000000330f0aa31b5a00000000000000000000000000000000000000000000000064dd45e1e93d59ba000000000000000000000000000000000000000000009a5ec15ac7b2c066dee600000000000000000000000000000000000000000000000064dd12d2de9a3e60000000000000000000000000000000000000000000009a5ec15afac1cb09fa40","logIndex":2251,"blockHash":"0x48aa29ee072ff33f9f4b8fb5b40ca889354cfa4420fa939b1b3193ee048b5a1f"}],"blockNumber":47188057,"confirmations":1,"cumulativeGasUsed":{"type":"BigNumber","hex":"0x01941cab"},"effectiveGasPrice":{"type":"BigNumber","hex":"0x174876e800"},"status":0,"type":2,"byzantium":true}, code=CALL_EXCEPTION, version=providers/5.7.2)
at Logger.makeError (/home/paucontracts/smart_contracts/hardhat_test/node_modules/@ethersproject/logger/src.ts/index.ts:269:28)
at Logger.throwError (/home/paucontracts/smart_contracts/hardhat_test/node_modules/@ethersproject/logger/src.ts/index.ts:281:20)
at InfuraProvider.<anonymous> (/home/paucontracts/smart_contracts/hardhat_test/node_modules/@ethersproject/providers/src.ts/base-provider.ts:1549:24)
at step (/home/paucontracts/smart_contracts/hardhat_test/node_modules/@ethersproject/providers/lib/base-provider.js:48:23)
at Object.next (/home/paucontracts/smart_contracts/hardhat_test/node_modules/@ethersproject/providers/lib/base-provider.js:29:53)
at fulfilled (/home/paucontracts/smart_contracts/hardhat_test/node_modules/@ethersproject/providers/lib/base-provider.js:20:58) {
reason: 'transaction failed',
code: 'CALL_EXCEPTION',
transactionHash: '0xc5f2e8393d04a254d243330f869bde0c054ef736994cd49382b62ed13d8a3995',
transaction: {
type: 2,
chainId: 137,
nonce: 135,
maxPriorityFeePerGas: BigNumber { value: "100000000000" },
maxFeePerGas: BigNumber { value: "100000000000" },
gasPrice: null,
gasLimit: BigNumber { value: "150000" },
to: '0x7A292CEF5D1b0c55177427719602027afAeBfE04',
value: BigNumber { value: "0" },
data: '0xfc57fea80000000000000000000000000000000000000000000000000000000000061a80',
accessList: [],
hash: '0xc5f2e8393d04a254d243330f869bde0c054ef736994cd49382b62ed13d8a3995',
v: 1,
r: '0xcca4b3d0a68acc7fda29acc589df48522c71aaad499cd08c433817586c35aace',
s: '0x328cbcda1a9676709627e43898faa58170cc738c9340c87120b380ff302097a0',
from: '0x223c02bf20B580f234dE37745d1C7a81AC2E5D84',
confirmations: 0,
wait: [Function (anonymous)]
},
receipt: {
to: '0x7A292CEF5D1b0c55177427719602027afAeBfE04',
from: '0x223c02bf20B580f234dE37745d1C7a81AC2E5D84',
contractAddress: null,
transactionIndex: 44,
gasUsed: BigNumber { value: "147449" },
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000800000000000000000000100000000000000000000000000000000002000000000000000000100000080000000000000000000004000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000020000000004000000000000000000001000000000000000000000000000000100000000000000000000000800000000000000000000000080000000000000000000000100000',
blockHash: '0x48aa29ee072ff33f9f4b8fb5b40ca889354cfa4420fa939b1b3193ee048b5a1f',
transactionHash: '0xc5f2e8393d04a254d243330f869bde0c054ef736994cd49382b62ed13d8a3995',
logs: [ [Object] ],
blockNumber: 47188057,
confirmations: 1,
cumulativeGasUsed: BigNumber { value: "26483883" },
effectiveGasPrice: BigNumber { value: "100000000000" },
status: 0,
type: 2,
byzantium: true
}
}
I guess the problem comes from the permisions im giving to the contract to spend my USDC but don't see what is wrong as in Remix works perfectly like that.
I've cheched:
const Quant = ethers.utils.parseEther("10");
// Uni = new ethers.Contract("0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45", addresses.univ2abi, provider);
// console.log(await Uni);
await hre.network.provider.request({
method: "hardhat_impersonateAccount",
params: [addresses.WMATIC_WHALE],
});
const signer = ethers.provider.getSigner(addresses.WMATIC_WHALE);
const amountIn = ethers.utils.parseUnits("25", 18);
await DAI
.connect(signer)
.transfer(Flashloan.address, amountIn);
await WMATIC
.connect(signer)
.transfer(Flashloan.address, amountIn);
const tx = await Flashloan.executeFlashLoan([
addresses.erc20Address.DAI], [Quant],
"0x");
await tx.wait();
look my code try to use ethers to bigNumbers or some library. maybe can help u