Error: VM Exception while processing transaction: reverted with reason string 'TransferHelper: TRANSFER_FROM_FAILED' - solidity

Many thanks in advance for your time and help
I want to run flashloan between uniswap and sushiswap. But, I get this error and it persists, despite trying several potential solutions mentioned by answers to similar problem.
I wrote it with hardhat via vscode.
here is the error:
Error: VM Exception while processing transaction: reverted with reason string 'TransferHelper: TRANSFER_FROM_FAILED'
Here is the solidity code:
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "hardhat/console.sol";
// Uniswap interface and library imports
import "./libraries/UniswapV2Library.sol";
import "./libraries/SafeERC20.sol";
import "./interfaces/IUniswapV2Router02.sol";
import "./interfaces/IUniswapV2Pair.sol";
import "./interfaces/IUniswapV2Factory.sol";
import "./interfaces/IERC20.sol";
contract FlashloanSwap {
using SafeERC20 for IERC20;
// Define the factory and router addresses of the DEXs
address private constant UNISWAP_FACTORY =
0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
address private constant UNISWAP_ROUTER =
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
address private constant SUSHISWAP_FACTORY =
0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac;
address private constant SUSHISWAP_ROUTER =
0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F;
// Trade variables
uint256 private deadline = block.timestamp + 1 days;
// Get the contract balance of token
function getBalanceOfToken(address _address) public view returns (uint256) {
return IERC20(_address).balanceOf(address(this));
}
function checkProfitability(uint256 _input, uint256 _outPut)
private
pure
returns (bool)
{
return _outPut > _input;
}
// There must be a function to receive ETH
receive() external payable {}
fallback() external payable {}
// Eth balance needs to be checked sometimes
function getBalance() public view returns (uint256) {
return address(this).balance;
}
// Define a function to trade a given token in exchange for another token on a given DEX
function executeTrade(
address _fromToken,
address _toToken,
uint256 _amountIn,
address factory,
address router
) private returns (uint256) {
address pair = IUniswapV2Factory(factory).getPair(_fromToken, _toToken);
require(pair != address(0), "Pool does not exist");
// Calculate amount out
address[] memory path = new address[](2);
path[0] = _fromToken;
path[1] = _toToken;
uint256 amountRequired = IUniswapV2Router01(router).getAmountsOut(
_amountIn,
path
)[1];
console.log("Amount Required: ", amountRequired);
// Perform arbitrage - Swap for another token
IUniswapV2Router02(router)
.swapExactTokensForTokensSupportingFeeOnTransferTokens(
_amountIn,
amountRequired,
path,
address(this),
deadline
);
uint256 balanceA = getBalanceOfToken(_fromToken);
uint256 balanceB = getBalanceOfToken(_toToken);
uint256 amountReceived = balanceA == 0 ? balanceB : balanceA;
console.log("amountReceived: ", amountReceived);
require(amountReceived > 0, "Aborted Transaction");
return amountReceived;
}
/*
I) It will be run whenever an arbitrage opportunity is detected
*/
function runTheArbitrage(
address tokenA,
address tokenB,
uint256 amountA,
uint256 amountB
) external {
// Get the pair address on uniswap
address pairAddress = IUniswapV2Factory(UNISWAP_FACTORY).getPair(
tokenA,
tokenB
);
// Check whether the pair exists
require(
pairAddress != address(0),
"The pair does not exist on uniswap"
);
// Save the borrowed token's specifications in _data to be passed to uniswapV2Call
address borrowedTokenAddress = amountA == 0 ? tokenB : tokenA;
uint256 borrowedTokenAmount = amountA == 0 ? amountB : amountA;
bytes memory data = abi.encode(
borrowedTokenAddress,
borrowedTokenAmount
);
// Create the flashloan with the swap function
IUniswapV2Pair(pairAddress).swap(amountA, amountB, address(this), data);
}
/*
II) With executing the previous function, uniswap will call this function in order to complete the flashloan cycle
*/
function uniswapV2Call(
address _sender,
uint256 _amountA,
uint256 _amountB,
bytes calldata _data
) external {
// get the specifications of the borrowed token
address token0 = IUniswapV2Pair(msg.sender).token0();
address token1 = IUniswapV2Pair(msg.sender).token1();
(address borrowedTokenAddress, uint256 borrowedTokenAmount) = abi
.decode(_data, (address, uint256));
token0 = token0 == borrowedTokenAddress ? token0 : token1;
token1 = token0 == borrowedTokenAddress ? token1 : token0;
// Check whether this function is called only by the pair contracts of uniswap
require(
msg.sender ==
UniswapV2Library.pairFor(UNISWAP_FACTORY, token0, token1),
"Only requests from uniswap pair contracts are accepted"
);
// Check whether this contract is the sender
require(_sender == address(this), "Sender should match this contract");
// Check one of the amounts to be zero
require(
_amountA == 0 || _amountB == 0,
"One of the amounts must be zero"
);
// Execute the first swap on source DEX
IERC20(token0).safeIncreaseAllowance(
UNISWAP_ROUTER,
borrowedTokenAmount
);
uint256 firstAmountOut = executeTrade(
token0,
token1,
borrowedTokenAmount,
UNISWAP_FACTORY,
UNISWAP_ROUTER
);
// Aprove the second DEX to spend the swapped token, then execute the trade on it
IERC20(token1).safeIncreaseAllowance(SUSHISWAP_ROUTER, firstAmountOut);
uint256 secondAmountOut = executeTrade(
token1,
token0,
firstAmountOut,
SUSHISWAP_FACTORY,
SUSHISWAP_ROUTER
);
uint256 fee = ((borrowedTokenAmount * 3) / 997) + 1;
uint256 amountToBePaidBack = borrowedTokenAmount + fee;
// Check profitability
bool profCheck = checkProfitability(
amountToBePaidBack,
secondAmountOut
);
require(profCheck, "Arbitrage not profitable");
// Pay back the loan
bool success1 = IERC20(token0).transfer(msg.sender, amountToBePaidBack);
// Send the profit to the initiator of the transaction
bool success2 = IERC20(token0).transfer(
tx.origin,
secondAmountOut - amountToBePaidBack
);
console.log(secondAmountOut - amountToBePaidBack, success2);
}
}
Also here is the hardhat.config.js file for the hardhat test
const { version } = require("chai");
require("#nomiclabs/hardhat-waffle");
require('dotenv').config();
module.exports = {
solidity: {
compilers: [
{version: '0.5.5'},
{version: '0.6.6'},
{version: '0.8.8'},
],
},
networks: {
hardhat: {
forking: {
url: process.env.alchemy_mainnet_key,
},
},
testnet: {
url: process.env.alchemy_renkiby_api,
chainId: 97,
accounts: [
process.enc.test_private_key
],
},
mainnet: {
url: process.env.alchemy_mainnet_key,
chainId: 56,
accounts: [
process.env.private_key
],
},
},
};
And finally, here is my test code
const { expect } = require("chai");
const { ethers, waffle } = require("hardhat");
const { deployContract } = require("ethereum-waffle");
const provider = waffle.provider;
const { abi } = require('../artifacts/contracts/interfaces/IERC20.sol/IERC20.json');
describe("Checking the whole arbitrage process", function () {
// Get the factory and router addresses
const UNISWAP_FACTORY = "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f";
const UNISWAP_ROUTER = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D";
const SUSHI_FACTORY = "0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac";
const SUSHI_ROUTER = "0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F";
// Token addresses
const tokenA = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
const tokenB = "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942";
// Get the decimals
const decimals = 18;
beforeEach(async function () {
// Get owner as signer
[owner] = await ethers.getSigners();
// Deploy smart contract
const FlashloanSwap = await ethers.getContractFactory("FlashloanSwap");
flashloanSwap = await FlashloanSwap.deploy();
await flashloanSwap.deployed();
console.log('\n', "Contract is deployed by: ", owner.address);
console.log("contract is deployed to: ", flashloanSwap.address, '\n');
const transactionHash = await owner.sendTransaction({
to: flashloanSwap.address,
value: ethers.utils.parseEther("1.0"),
});
console.log("transactionHash : ", transactionHash);
balanceOfEth = await provider.getBalance(flashloanSwap.address)
balanceOfEth = ethers.utils.formatUnits(balanceOfEth, 18);
console.log('\n', "Balance of ETH before transaction : ", balanceOfEth.toString(), '\n');
});
it("Check Whether Swap Occurs or Not", async () => {
await flashloanSwap.runTheArbitrage(tokenA, tokenB, 0, 100);
balanceOfEth = await provider.getBalance(flashloanSwap.address)
balanceOfEth = ethers.utils.formatEther(balanceOfEth, 18);
console.log('\n', "Balance of ETH after transaction : ", balanceOfEth, '\n');
});

When you're trying to pay back the loan + fees, you transfer the amount for each asset from your FlashloanSwap to Uniswap. Could the exception you're getting be thrown because you have to allow Uniswap to pull the loan + fees from your account, i.e. you call approve on uniswap and uniswap calls transferFrom on your account?
Try replacing:
IERC20(token0).transfer(msg.sender, amountToBePaidBack);`
with:
IERC20(token0).approve(msg.sender, amountToBePaidBack);

100% sure that it's an allowance issue, you didn't approved the tokens like:
IERC20(WETH).approve(routerA, amount);
IUniswapV2Router02(routerA).swapExactTokensForTokensSupportingFeeOnTransferTokens(
amount,
0,
path2,
cttAddress,
block.timestamp + 1200
);

Related

Error: cannot estimate gas; transaction may fail or may require manual gas limit

im trying to test a constructor method of a exchange developed in solidity but i given the following error:
Error: cannot estimate gas; transaction may fail or may require manual gas limit [ See: https://links.ethers.org/v5-errors-UNPREDICTABLE_GAS_LIMIT ] (reason="VM Exception while processing transaction: reverted with reason string 'Invalid address _tokenVault'", method="estimateGas", transaction={"from":"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266","data":"0x60806040523480156200001157600080fd5b5060405162001dba38038062001dba833981810160405281019062000037919062000239565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603620000a9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620000a090620002f6565b60405180910390fd5b33600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060058190555060126000819055506003600181905550600060068190555050505062000318565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620001c68262000199565b9050919050565b620001d881620001b9565b8114620001e457600080fd5b50565b600081519050620001f881620001cd565b92915050565b6000819050919050565b6200021381620001fe565b81146200021f57600080fd5b50565b600081519050620002338162000208565b92915050565b60008060006060848603121562000255576200025462000194565b5b60006200026586828701620001e7565b93505060206200027886828701620001e7565b92505060406200028b8682870162000222565b9150509250925092565b600082825260208201905092915050565b7f496e76616c69642061646472657373205f746f6b656e5661756c740000000000600082015250565b6000620002de601b8362000295565b9150620002eb82620002a6565b602082019050919050565b600060208201905081810360008301526200031181620002cf565b9050919050565b611a9280620003286000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c8063a8f6c91311610097578063d0e30db011610066578063d0e30db01461028c578063e4828367146102aa578063e6aa216c146102da578063f071db5a146102f8576100f5565b8063a8f6c913146101f0578063ae06c1b71461020e578063b03a9a051461023e578063bda6606c1461025c576100f5565b80636497a8a0116100d35780636497a8a0146101665780638da5cb5b146101965780639902084b146101b4578063a001ecdd146101d2576100f5565b80632d296bf1146100fa578063313ce5671461012a5780635bc789d914610148575b600080fd5b610114600480360381019061010f9190611254565b610316565b6040516101219190611290565b60405180910390f35b6101326105ec565b60405161013f9190611290565b60405180910390f35b6101506105f2565b60405161015d91906112ec565b60405180910390f35b610180600480360381019061017b9190611333565b610618565b60405161018d9190611290565b60405180910390f35b61019e61089a565b6040516101ab91906112ec565b60405180910390f35b6101bc6108c0565b6040516101c99190611290565b60405180910390f35b6101da6109f8565b6040516101e79190611290565b60405180910390f35b6101f86109fe565b60405161020591906112ec565b60405180910390f35b61022860048036038101906102239190611254565b610a24565b6040516102359190611290565b60405180910390f35b610246610b09565b6040516102539190611290565b60405180910390f35b61027660048036038101906102719190611254565b610b0f565b6040516102839190611290565b60405180910390f35b610294610b81565b6040516102a19190611290565b60405180910390f35b6102c460048036038101906102bf9190611254565b610e46565b6040516102d19190611290565b60405180910390f35b6102e26111d6565b6040516102ef9190611290565b60405180910390f35b61030061120b565b60405161030d9190611290565b60405180910390f35b600080821161035a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610351906113bd565b60405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff1663bda6606c836040518263ffffffff1660e01b81526004016103939190611290565b6020604051808303816000875af11580156103b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103d691906113f2565b6103de611211565b101561041f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104169061146b565b60405180910390fd5b600060646001548461043191906114ba565b61043b919061152b565b90506000818461044b919061155c565b9050816006600082825461045f9190611590565b9250508190555080600560008282546104789190611590565b92505081905550600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33836040518363ffffffff1660e01b81526004016104dc9291906115c4565b600060405180830381600087803b1580156104f657600080fd5b505af115801561050a573d6000803e3d6000fd5b505050503373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff1663bda6606c876040518263ffffffff1660e01b81526004016105619190611290565b6020604051808303816000875af1158015610580573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a491906113f2565b6105ac611211565b6105b6919061155c565b9081150290604051600060405180830381858888f193505050501580156105e1573d6000803e3d6000fd5b508092505050919050565b60005481565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610688576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161067f90611639565b60405180910390fd5b60008273ffffffffffffffffffffffffffffffffffffffff163b146106e2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106d9906116a5565b60405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610772576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161076990611711565b60405180910390fd5b6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8b2cb4f846040518263ffffffff1660e01b81526004016107cf91906112ec565b602060405180830381865afa1580156107ec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061081091906113f2565b11610850576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108479061177d565b60405180910390fd5b81600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060019050919050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610952576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161094990611711565b60405180910390fd5b6706f05b59d3b20000600654101561099f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610996906117e9565b60405180910390fd5b60006006819055503373ffffffffffffffffffffffffffffffffffffffff166108fc6006549081150290604051600060405180830381858888f193505050501580156109ef573d6000803e3d6000fd5b50600654905090565b60015481565b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000808211610a68576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a5f90611855565b60405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610af8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aef90611711565b60405180910390fd5b816001819055506001549050919050565b60055481565b6000808211610b53576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b4a906118c1565b60405180910390fd5b6000600554670de0b6b3a764000084610b6c91906114ba565b610b76919061152b565b905080915050919050565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610c13576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c0a90611711565b60405180910390fd5b6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8b2cb4f336040518263ffffffff1660e01b8152600401610c7091906112ec565b602060405180830381865afa158015610c8d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cb191906113f2565b03610cf1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ce89061192d565b60405180910390fd5b60003073ffffffffffffffffffffffffffffffffffffffff1663bda6606c610d17611211565b6040518263ffffffff1660e01b8152600401610d339190611290565b6020604051808303816000875af1158015610d52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7691906113f2565b90508060056000828254610d8a9190611590565b92505081905550600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b8152600401610e109291906115c4565b600060405180830381600087803b158015610e2a57600080fd5b505af1158015610e3e573d6000803e3d6000fd5b505050505090565b6000808211610e8a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e8190611999565b60405180910390fd5b81600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8b2cb4f336040518263ffffffff1660e01b8152600401610ee691906112ec565b602060405180830381865afa158015610f03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f2791906113f2565b1015610f68576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f5f90611a05565b60405180910390fd5b6000606460015484610f7a91906114ba565b610f84919061152b565b905060008184610f94919061155c565b9050600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b333836040518363ffffffff1660e01b8152600401610ff39291906115c4565b600060405180830381600087803b15801561100d57600080fd5b505af1158015611021573d6000803e3d6000fd5b50505050600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd33600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16846040518463ffffffff1660e01b81526004016110a693929190611a25565b600060405180830381600087803b1580156110c057600080fd5b505af11580156110d4573d6000803e3d6000fd5b5050505081600660008282546110ea9190611590565b925050819055508060056000828254611103919061155c565b925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff1663bda6606c846040518263ffffffff1660e01b815260040161115d9190611290565b6020604051808303816000875af115801561117c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111a091906113f2565b9081150290604051600060405180830381858888f193505050501580156111cb573d6000803e3d6000fd5b508092505050919050565b600080670de0b6b3a7640000600554670de0b6b3a76400006111f891906114ba565b611202919061152b565b90508091505090565b60065481565b600034905090565b600080fd5b6000819050919050565b6112318161121e565b811461123c57600080fd5b50565b60008135905061124e81611228565b92915050565b60006020828403121561126a57611269611219565b5b60006112788482850161123f565b91505092915050565b61128a8161121e565b82525050565b60006020820190506112a56000830184611281565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006112d6826112ab565b9050919050565b6112e6816112cb565b82525050565b600060208201905061130160008301846112dd565b92915050565b611310816112cb565b811461131b57600080fd5b50565b60008135905061132d81611307565b92915050565b60006020828403121561134957611348611219565b5b60006113578482850161131e565b91505092915050565b600082825260208201905092915050565b7f496e76616c6964205f616d6f756e74546f4275792076616c7565000000000000600082015250565b60006113a7601a83611360565b91506113b282611371565b602082019050919050565b600060208201905081810360008301526113d68161139a565b9050919050565b6000815190506113ec81611228565b92915050565b60006020828403121561140857611407611219565b5b6000611416848285016113dd565b91505092915050565b7f496e73756666696369656e742065746865727300000000000000000000000000600082015250565b6000611455601383611360565b91506114608261141f565b602082019050919050565b6000602082019050818103600083015261148481611448565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006114c58261121e565b91506114d08361121e565b92508282026114de8161121e565b915082820484148315176114f5576114f461148b565b5b5092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006115368261121e565b91506115418361121e565b925082611551576115506114fc565b5b828204905092915050565b60006115678261121e565b91506115728361121e565b925082820390508181111561158a5761158961148b565b5b92915050565b600061159b8261121e565b91506115a68361121e565b92508282019050808211156115be576115bd61148b565b5b92915050565b60006040820190506115d960008301856112dd565b6115e66020830184611281565b9392505050565b7f496e76616c69642061646472657373205f746f6b656e5661756c740000000000600082015250565b6000611623601b83611360565b915061162e826115ed565b602082019050919050565b6000602082019050818103600083015261165281611616565b9050919050565b7f5f746f6b656e5661756c742063616e6e6f74206265206120636f6e7472616374600082015250565b600061168f602083611360565b915061169a82611659565b602082019050919050565b600060208201905081810360008301526116be81611682565b9050919050565b7f4e6f74206175746f72697a656400000000000000000000000000000000000000600082015250565b60006116fb600d83611360565b9150611706826116c5565b602082019050919050565b6000602082019050818103600083015261172a816116ee565b9050919050565b7f5f746f6b656e5661756c7420686173206e6f2062616c616e6365000000000000600082015250565b6000611767601a83611360565b915061177282611731565b602082019050919050565b600060208201905081810360008301526117968161175a565b9050919050565b7f496e73756666696369656e7420616d6f756e74206f6620666565730000000000600082015250565b60006117d3601b83611360565b91506117de8261179d565b602082019050919050565b60006020820190508181036000830152611802816117c6565b9050919050565b7f496e76616c6964205f70657263656e746167652076616c756500000000000000600082015250565b600061183f601983611360565b915061184a82611809565b602082019050919050565b6000602082019050818103600083015261186e81611832565b9050919050565b7f496e76616c6964205f746f6b656e416d6f756e742076616c7565000000000000600082015250565b60006118ab601a83611360565b91506118b682611875565b602082019050919050565b600060208201905081810360008301526118da8161189e565b9050919050565b7f4e6f20657468657273206465706f736974656400000000000000000000000000600082015250565b6000611917601383611360565b9150611922826118e1565b602082019050919050565b600060208201905081810360008301526119468161190a565b9050919050565b7f496e76616c6964205f616d6f756e74546f457863686167652076616c75650000600082015250565b6000611983601e83611360565b915061198e8261194d565b602082019050919050565b600060208201905081810360008301526119b281611976565b9050919050565b7f496e73756666696369656e742062616c616e6365000000000000000000000000600082015250565b60006119ef601483611360565b91506119fa826119b9565b602082019050919050565b60006020820190508181036000830152611a1e816119e2565b9050919050565b6000606082019050611a3a60008301866112dd565b611a4760208301856112dd565b611a546040830184611281565b94935050505056fea2646970667358221220ffad1a9439d9834abe467db3f1382023c0dc5f0f1296ff9a6bd076af0f22edd464736f6c6343000811003300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a","accessList":null}, error={"stackTrace":[{"type":4,"sourceReference":{"function":"constructor","contract":"Exchange","sourceName":"contracts/Exchange.sol","sourceContent":"//SPDX-License-Identifier: MIT\npragma solidity 0.8.17;\nimport "./interface/IExchange.sol";\nimport "./interface/IERC20.sol";\n\ncontract Exchange is IExchange {\n\n uint256 public decimals;\n uint256 public feePercentage;\n address public owner;\n address public tokenVault;\n address public erc20Contract;\n uint256 public invariant;\n uint256 public feesCollected;\n\n/\n* #notice Constructor\n* #dev Throw if '_tokenVault' is zero address\n* #dev Throw if '_tokenVault' is a contract\n* #dev Throw if '_erc20Contract' is zero address\n* #dev Throw if '_erc20Contract' is not a contract\n* #dev Throw if '_tokenAmount' is zero\n* #dev Throw if '_tokenVault' doesn't have enough balance\n* #param _tokenVault Address of the token vault\n* #param _erc20Contract Address of the ERC20 contract\n* #param _tokenAmount Amount of tokens to be deposited in the vault\n */\nconstructor(address _tokenVault, address _erc20Contract,uint256 _tokenAmount ){\n require(_tokenVault != address(0), "Invalid address _tokenVault");\n // require(_tokenVault.code.length==0, "_tokenVault cannot be a contract");\n //require(_erc20Contract != address(0), "_erc20Contract cannot be zero address");\n // require(_erc20Contract.code.length>0, "_erc20Contract is not a contract");\n // require(_tokenAmount > 0, "Invalid _tokenAmount value");\n // require(IERC20(_erc20Contract).getBalance(_tokenVault) >= _tokenAmount, "Insufficient tokens in the vault");\n owner = msg.sender;\n tokenVault = _tokenVault;\n erc20Contract = _erc20Contract;\n invariant = _tokenAmount;\n decimals = 18;\n feePercentage = 3;\n feesCollected = 0;\n}\n\n\n //auxiliaries\n function getValue() internal view returns (uint256) {\n return msg.value;\n }\n\n \n /\n * #notice calculate Ether Amount \n * #dev Throw if '_tokenAmount' is zero\n * #param _tokenAmount Amount of tokens to be exchanged\n */\n function calculateEtherAmount(uint256 _tokenAmount) external override returns (uint256)\n { \n require(_tokenAmount > 0, "Invalid _tokenAmount value");\n uint256 etherAmount = (_tokenAmount * 1 ether) / invariant;\n return etherAmount; \n }\n\n /\n * #notice returns the quantity of tokens that can be obtained for an ether at the moment of the query\n */\n function getExchangeRate() external override returns (uint256) {\n uint256 tokenAmount = (1 ether * invariant) / 1 ether;\n return tokenAmount;\n }\n\n /\n * #notice Buy tokens\n * #dev Throw if '_amountToBuy' is zero \n * #dev Thorw if 'msg.value' is less than '_amountToBuy'\n * #param _amountToBuy Amount of tokens to be bought\n */\n function buyToken(uint256 _amountToBuy) external override returns (uint256)\n {\n require(_amountToBuy > 0, "Invalid _amountToBuy value");\n require(getValue()>=this.calculateEtherAmount(_amountToBuy),"Insufficient ethers");\n uint256 fee = (_amountToBuy * feePercentage) / 100;\n uint256 amountToTransfer = _amountToBuy - fee;\n feesCollected += fee;\n invariant += amountToTransfer;\n IERC20(erc20Contract).transfer(msg.sender, amountToTransfer);\n payable(msg.sender).transfer(getValue()-this.calculateEtherAmount(_amountToBuy));\n return amountToTransfer;\n }\n\n /\n * #notice Buy ethers\n * #dev Throw if '_amountToExchange' is zero\n * #param _amountToExchange Amount of tokens to be exchanged\n */\n function buyEther(uint256 _amountToExchange) external override returns (uint256)\n {\n require(_amountToExchange > 0, "Invalid _amountToExchage value");\n require(IERC20(erc20Contract).getBalance(msg.sender) >=_amountToExchange,"Insufficient balance");\n uint256 fee = (_amountToExchange * feePercentage) / 100;\n uint256 amountToTransfer = _amountToExchange - fee;\n IERC20(erc20Contract).approve(msg.sender, amountToTransfer);\n IERC20(erc20Contract).transferFrom(msg.sender,tokenVault,amountToTransfer);\n feesCollected += fee;\n invariant -= amountToTransfer;\n\n payable(msg.sender).transfer(this.calculateEtherAmount(amountToTransfer));\n return amountToTransfer;\n }\n\n /\n * #notice Set fee percentage\n * #dev Throw if '_percentage' is zero\n * #dev Throw if 'msg.sender' is not the owner\n * #param _percentage Percentage of fee\n */\n function setFeePercentage(uint256 _percentage) external override returns (uint256)\n {\n require(_percentage > 0, "Invalid _percentage value");\n require(msg.sender == owner, "Not autorized");\n feePercentage = _percentage;\n return feePercentage;\n }\n\n /\n * #notice Deposit ethers\n * #dev Throw if 'msg.value' is zero\n * #dev Throw if 'msg.sender' is not the owner\n */\n\n function deposit() external override returns (uint256) {\n require(msg.sender == owner, "Not autorized");\n require (IERC20(erc20Contract).getBalance(msg.sender)!=0, "No ethers deposited");\n uint256 amountToTransfer = this.calculateEtherAmount(getValue());\n invariant += amountToTransfer;\n IERC20(erc20Contract).transfer(tokenVault, amountToTransfer);\n }\n\n\n /\n * #notice Set token vault\n * #dev Throw if '_tokenVault' is zero\n * #dev Throw if '_tokenVault' is a contract\n * #dev Throw if 'msg.sender' is not the owner\n * #dev Throw if '_tokenVault' has no balance\n * #dev Throw if '_tokenVault' has no authorization\n * #param _tokenVault Address of the token vault\n */\n function setTokenVault(address _tokenVault) external override returns (uint256)\n {\n require(_tokenVault != address(0), "Invalid address _tokenVault");\n require (_tokenVault.code.length==0, "_tokenVault cannot be a contract");\n require(msg.sender == owner, "Not autorized");\n require(IERC20(erc20Contract).getBalance(_tokenVault) > 0, "_tokenVault has no balance");\n tokenVault = _tokenVault;\n return 1;\n }\n\n /**\n * #notice Withdraw fees\n * #dev Throw if 'msg.sender' is not the owner\n * #dev Throw if 'feesCollected' is less than 0.5 ethers\n */\n function withdrawFeesAmount() external override returns (uint256) {\n require(msg.sender == owner, "Not autorized");\n require(feesCollected >= 0.5 ether, "Insufficient amount of fees");\n feesCollected = 0;\n payable(msg.sender).transfer(feesCollected);\n return feesCollected;\n }\n}\n","line":29,"range":[950,1015]},"message":{"value":{"type":"Buffer","data":[8,195,121,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,73,110,118,97,108,105,100,32,97,100,100,114,101,115,115,32,95,116,111,107,101,110,86,97,117,108,116,0,0,0,0,0]},"_selector":"08c379a0"},"isInvalidOpcodeError":false}],"data":"0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001b496e76616c69642061646472657373205f746f6b656e5661756c740000000000"}, code=UNPREDICTABLE_GAS_LIMIT, version=providers/5.6.8)
at Logger.makeError (node_modules/#ethersproject/logger/src.ts/index.ts:261:28)
at Logger.throwError (node_modules/#ethersproject/logger/src.ts/index.ts:273:20)
at checkError (node_modules/#ethersproject/providers/src.ts/json-rpc-provider.ts:78:20)
at EthersProviderWrapper. (node_modules/#ethersproject/providers/src.ts/json-rpc-provider.ts:603:20)
at step (node_modules/#ethersproject/providers/lib/json-rpc-provider.js:48:23)
at Object.throw (node_modules/#ethersproject/providers/lib/json-rpc-provider.js:29:53)
at rejected (node_modules/#ethersproject/providers/lib/json-rpc-provider.js:21:65)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at runNextTicks (internal/process/task_queues.js:64:3)
at listOnTimeout (internal/timers.js:526:9)
the test code:
const { ethers } = require("hardhat");
const chai = require("chai");
const { solidity } = require( "ethereum-waffle");
const { ConstructorFragment } = require("ethers/lib/utils");
chai.use(solidity);
const { expect } = chai;
const EchangePath = "contracts/Exchange.sol:Exchange";
const confirmations_number = 1;
const zeroAddress = '0x0000000000000000000000000000000000000000';
let contractInstance;
// Constructor parameters
const decimals = 18 ;
const feePercentage = 1 ;
const owner = '0x0000000000000000000000000000000000000000';
const tokenVault = '0x0000000000000000000000000000000000000000';
const erc20Contract = '0x0000000000000000000000000000000000000000';
const invariant = 1 ;
const feesCollected = 0 ;
const tokenAmount = 10;
describe("Echange tests", () => {
beforeEach(async () => {
console.log("-----------------------------------------------------------------------------------");
console.log(" -- Exchange tests start");
console.log("-----------------------------------------------------------------------------------");
[signer, account1, account2, account3] = await ethers.getSigners();
provider = ethers.provider;
// Deploy Exchange contract
const Exchange = await ethers.getContractFactory(EchangePath, signer);
contractInstance = await Exchange.deploy(
tokenVault,
erc20Contract,
tokenAmount
);
});
describe("Constructor tests", () => {
it("Try send invalid tokenValue address", async () => {
const Exchange = await ethers.getContractFactory(EchangePath, signer);
await expect(
Exchange.deploy(
zeroAddress,
erc20Contract,
tokenAmount,
{ gas: 1000000, gasPrice: 1000000000,gasLimit:1000000 , from: owner}
)
).to.be.revertedWith("Invalid token address");
});
});
the exchange code:
//SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import "./interface/IExchange.sol";
import "./interface/IERC20.sol";
contract Exchange is IExchange {
uint256 public decimals;
uint256 public feePercentage;
address public owner;
address public tokenVault;
address public erc20Contract;
uint256 public invariant;
uint256 public feesCollected;
constructor(address _tokenVault, address _erc20Contract,uint256 _tokenAmount ){
require(_tokenVault != address(0), "Invalid address _tokenVault");
require(_tokenVault.code.length==0, "_tokenVault cannot be a contract");
require(_erc20Contract != address(0), "_erc20Contract cannot be zero address");
require(_erc20Contract.code.length>0, "_erc20Contract is not a contract");
require(_tokenAmount > 0, "Invalid _tokenAmount value");
require(IERC20(_erc20Contract).getBalance(_tokenVault) >= _tokenAmount, "Insufficient tokens in the vault");
owner = msg.sender;
tokenVault = _tokenVault;
erc20Contract = _erc20Contract;
invariant = _tokenAmount;
decimals = 18;
feePercentage = 3;
feesCollected = 0;
}
I tried commenting the requires and it did not return the error.Also i tried deploy without the line :
{ gas: 1000000, gasPrice: 1000000000,gasLimit:1000000 , from: owner}
If you look closely the error is more like "Invalid address _tokenVault".
If you look into your tests, the error is coming from the beforeEach() under the comment
// Deploy Exchange contract
There you are trying to invoke the deploy method which is outside the test. Here is the updated test method.
describe("Echange tests", () => {
beforeEach(async () => {
console.log("-----------------------------------------------------------------------------------");
console.log(" -- Exchange tests start");
console.log("-----------------------------------------------------------------------------------");
[signer, account1, account2, account3] = await ethers.getSigners();
provider = ethers.provider;
});
describe("Constructor tests", () => {
it("Try send invalid tokenValue address", async () => {
const Exchange = await ethers.getContractFactory(EchangePath, signer);
const transactionPromise = Exchange.deploy(
zeroAddress,
erc20Contract,
tokenAmount
)
await expect(
transactionPromise
).to.be.revertedWith("Invalid address _tokenVault");
});
})
});

Error: value out-of-bounds on callbackGasLimit variable

i have written a simple contract in solidity as seen below
/**
* #title Lottery
* #notice Enter lottery by paying some amount
* #notice pick a random number( verifiably random)
* #notice winner be selected every xtimes
*/
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "#chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "#chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "#chainlink/contracts/src/v0.8/interfaces/KeeperCompatibleInterface.sol";
// ============= Errors ====================
error Lottery__NotEnoughFunds();
error Lottery__TransferFailed();
error Lottery__NotOpen();
error Raffle__UpkeepNotNeeded(uint256 currentBalance, uint256 numPlayers, uint256 raffleState);
contract Lottery is VRFConsumerBaseV2, KeeperCompatibleInterface {
// ============= Type declaration ===========
enum LotteryState {
OPEN,
CALCULATING
}
// ======== state variables ============
uint256 private immutable i_entranceFee;
address payable[] private s_players;
bytes32 private immutable i_gasLane;
uint64 private immutable i_subscriptionId;
uint32 private immutable i_callbackGasLimit;
uint16 private constant REQUEST_CONFIRMATION = 3;
uint32 private constant NUM_WORDS = 1;
VRFCoordinatorV2Interface private immutable i_vrfCoordinator;
address private s_recentwinner;
LotteryState private s_lotteryState;
uint256 private s_lastTimeStamp;
uint256 private immutable i_interval;
// ========== Events ==================
event LotteryEnter(address indexed player);
event RandomwinnerRequest(uint256 indexed requestId);
event WinnerPicked(address indexed winner);
// ========= constructor ============
constructor(
address vrfCoordinatorV2,
uint256 entranceFee,
uint32 callbackGasLimit,
uint256 interval,
bytes32 gasLane,
uint64 subscriptionId
) VRFConsumerBaseV2(vrfCoordinatorV2) {
i_entranceFee = entranceFee;
i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorV2);
i_gasLane = gasLane;
i_subscriptionId = subscriptionId;
i_callbackGasLimit = callbackGasLimit;
s_lotteryState = LotteryState.OPEN;
i_interval = interval;
s_lastTimeStamp = block.timestamp;
}
// ========== functions ============
function enterLottery() public payable {
if (msg.value < i_entranceFee) {
revert Lottery__NotEnoughFunds();
}
if (s_lotteryState != LotteryState.OPEN ) {
revert Lottery__NotOpen();
}
s_players.push(payable(msg.sender));
emit LotteryEnter(msg.sender);
}
function checkUpkeep(
bytes memory /* checkData */
)
public
view
override
returns (
bool upkeepNeeded,
bytes memory /* performData */
)
{
bool isOpen = LotteryState.OPEN == s_lotteryState;
bool timePassed = ((block.timestamp - s_lastTimeStamp) > i_interval);
bool hasPlayers = s_players.length > 0;
bool hasBalance = address(this).balance > 0;
upkeepNeeded = (timePassed && isOpen && hasBalance && hasPlayers);
// return (upkeepNeeded, "0x0"); // can we comment this out?
}
function performUpkeep( bytes calldata /* performData */) external override {
( bool upKeepNeeded, ) = checkUpkeep("");
if(!upKeepNeeded){
revert Raffle__UpkeepNotNeeded(
address(this).balance,
s_players.length,
uint256(s_lotteryState)
);
}
// Request a random number
// Once we get it , do something with it
s_lotteryState = LotteryState.CALCULATING;
uint256 requestId = i_vrfCoordinator.requestRandomWords(
i_gasLane,
i_subscriptionId,
REQUEST_CONFIRMATION,
i_callbackGasLimit,
NUM_WORDS
);
emit RandomwinnerRequest(requestId);
}
function fulfillRandomWords(uint256 /*requestId*/, uint256[] memory randomWords) internal override {
// Modulo function
uint256 winnerIndex = randomWords[0] % s_players.length;
address payable recentWinner = s_players[winnerIndex];
s_recentwinner = recentWinner;
s_lotteryState = LotteryState.OPEN;
s_players = new address payable[](0);
s_lastTimeStamp = block.timestamp;
(bool success, ) = recentWinner.call{value: address(this).balance}("");
if (!success) {
revert Lottery__TransferFailed();
}
emit WinnerPicked(recentWinner);
}
// =============== View /Pure functions =============
function getEntraceFee() public view returns (uint256) {
return i_entranceFee;
}
function getPlayers(uint256 index) public view returns (address) {
return s_players[index];
}
function getRecentWinner() public view returns (address) {
return s_recentwinner;
}
function getRaffleState() public view returns (LotteryState) {
return s_lotteryState;
}
function getNumWords() public pure returns (uint256) {
return NUM_WORDS;
}
function getRequestConfirmations() public pure returns (uint256) {
return REQUEST_CONFIRMATION;
}
function getLastTimeStamp() public view returns (uint256) {
return s_lastTimeStamp;
}
function getInterval() public view returns (uint256) {
return i_interval;
}
function getNumberOfPlayers() public view returns (uint256) {
return s_players.length;
}
}
and it deploy script
const { network, ethers } = require("hardhat");
const {
developmentChains,
networkConfig,
VERIFICATION_BLOCK_CONFIRMATIONS,
} = require("../helper-hardhat-config");
const { verify } = require("../utils/verify");
const VR_FUND_AMOUNT = ethers.utils.parseEther("4");
module.exports = async function ({ getNamedAccounts, deployments }) {
const { deploy, log } = deployments;
const { deployer } = await getNamedAccounts();
const chainId = network.config.chainId;
let vrfCoordinatorV2Address, subscriptionId;
if (developmentChains.includes(network.name)) {
const vrfCoordinatorV2Mock = await ethers.getContract(
"VRFCoordinatorV2Mock"
);
vrfCoordinatorV2Address = vrfCoordinatorV2Mock.address;
const transactionResponse = await vrfCoordinatorV2Mock.createSubscription();
const transactionReceipt = await transactionResponse.wait(1);
subscriptionId = transactionReceipt.events[0].args.subId;
// fund the subscription
await vrfCoordinatorV2Mock.fundSubscription(subscriptionId, VR_FUND_AMOUNT);
} else {
vrfCoordinatorV2Address = networkConfig[chainId]["vrfCoordinatorV2"];
subscriptionId = networkConfig[chainId]["subscriptionId"];
}
const entranceFee = networkConfig[chainId]["entranceFee"];
const gasLane = networkConfig[chainId]["gasLane"];
const callbackGasLimit = networkConfig[chainId]["callbackGasLimit"];
const interval = networkConfig[chainId]["interval"];
const args = [
vrfCoordinatorV2Address,
entranceFee,
gasLane,
subscriptionId,
callbackGasLimit,
interval,
];
const lottery = await deploy("Lottery", {
from: deployer,
args: args,
log: true,
waitConfirmations: network.config.blockConfirmations || 1,
});
if (
!developmentChains.includes(network.name) &&
process.env.ETHERSCAN_API_KEY
) {
log("Verifying.....");
await verify(lottery.address, args);
}
log("-----------------------------------");
};
module.exports.tags = ["all", "lottery"];
mock deploy script
const { network } = require("hardhat")
const { developmentChains } = require("../helper-hardhat-config")
const BASE_FEE = ethers.utils.parseEther("0.25") // 0.25 is this the premium in LINK?
const GAS_PRICE_LINK = 1e9 // link per gas, is this the gas lane? // 0.000000001 LINK per gas
module.exports = async function ({ getNamedAccounts, deployments }) {
const { deploy, log } = deployments
const { deployer } = await getNamedAccounts()
const chainId = network.config.chainId
const args =[BASE_FEE, GAS_PRICE_LINK]
// If we are on a local development network, we need to deploy mocks!
if(developmentChains.includes(network.name)){
log("Local Network Detected! Deploying mocks...")
// deploy a mock vrfCoordinator
await deploy("VRFCoordinatorV2Mock",{
from:deployer,
log:true,
args:args,
})
log("Mocks Deployed!")
log("------------------------------------------")
}
}
module.exports.tags = ["all", "mocks"]
and helper configuration
const { ethers } = require("hardhat")
const networkConfig = {
5:{
name:"goerli",
vrfCoordinatorV2:"0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D",
entranceFee: ethers.utils.parseEther("0.01"),
gasLane:"0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15",
subscriptionId:"0",
callbackGasLimit:"500000",
interval:"30"
},
31337:{
name:"hardhat",
entranceFee: ethers.utils.parseEther("0.01"),
gasLane:"0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15",
callbackGasLimit:"500000",
interval:"30"
}
}
const developmentChains = ["hardhat", "localhost"]
module.exports = {
networkConfig,
developmentChains
}
when run yarn hardhat deploy, i encounter an error
as below
An unexpected error occurred:
Error: ERROR processing C:\Users\amoko\Desktop\blockchain\lottery\deploy\01-deploy-lottery.js:
Error: value out-of-bounds (argument="callbackGasLimit", value="0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15", code=INVALID_ARGUMENT, version=abi/5.7.0)
at Logger.makeError (C:\Users\amoko\Desktop\blockchain\lottery\node_modules\#ethersproject\logger\src.ts\index.ts:269:28)
at Logger.throwError (C:\Users\amoko\Desktop\blockchain\lottery\node_modules\#ethersproject\logger\src.ts\index.ts:281:20)
at Logger.throwArgumentError (C:\Users\amoko\Desktop\blockchain\lottery\node_modules\#ethersproject\logger\src.ts\index.ts:285:21)
at NumberCoder.Coder._throwError (C:\Users\amoko\Desktop\blockchain\lottery\node_modules\#ethersproject\abi\src.ts\coders\abstract-coder.ts:68:16)
at NumberCoder.encode (C:\Users\amoko\Desktop\blockchain\lottery\node_modules\#ethersproject\abi\src.ts\coders\number.ts:35:18)
at C:\Users\amoko\Desktop\blockchain\lottery\node_modules\#ethersproject\abi\src.ts\coders\array.ts:71:19
at Array.forEach (<anonymous>)
at pack (C:\Users\amoko\Desktop\blockchain\lottery\node_modules\#ethersproject\abi\src.ts\coders\array.ts:54:12)
at TupleCoder.encode (C:\Users\amoko\Desktop\blockchain\lottery\node_modules\#ethersproject\abi\src.ts\coders\tuple.ts:54:20)
at AbiCoder.encode (C:\Users\amoko\Desktop\blockchain\lottery\node_modules\#ethersproject\abi\src.ts\abi-coder.ts:111:15)
at DeploymentsManager.executeDeployScripts (C:\Users\amoko\Desktop\blockchain\lottery\node_modules\hardhat-deploy\src\DeploymentsManager.ts:1222:19)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at runNextTicks (node:internal/process/task_queues:65:3)
at listOnTimeout (node:internal/timers:528:9)
at processTimers (node:internal/timers:502:7)
at DeploymentsManager.runDeploy (C:\Users\amoko\Desktop\blockchain\lottery\node_modules\hardhat-deploy\src\DeploymentsManager.ts:1052:5)
at SimpleTaskDefinition.action (C:\Users\amoko\Desktop\blockchain\lottery\node_modules\hardhat-deploy\src\index.ts:438:5)
at Environment._runTaskDefinition (C:\Users\amoko\Desktop\blockchain\lottery\node_modules\hardhat\src\internal\core\runtime-environment.ts:308:14)
at Environment.run (C:\Users\amoko\Desktop\blockchain\lottery\node_modules\hardhat\src\internal\core\runtime-environment.ts:156:14)
at SimpleTaskDefinition.action (C:\Users\amoko\Desktop\blockchain\lottery\node_modules\hardhat-deploy\src\index.ts:584:32)
error Command failed with exit code 1.
i need help on figuring out why is it is propagting this error and how to fix it
I have tried changing the callbackgasLimit but it still brings the error. i expect the contract to get deployed on my localhost network when i run yarn hardhat deploy
i discovered that i had used a wrong variable(raffleState) in defining the event "Raffle__UpkeepNotNeeded" so it was leading to a significant increase in gas fees which were exceeding the max gas limit set for the testnet hence the error

Require statement reverting transaction everytime

something strange is going on. I'm testing some contracts on remix EVM. I have some pretty basic NFT staking contracts that works fine when comes to staking and transfering the token. However, if I try to execute the unstake function, the transaction gets reverted saying that the the require conditions are not passing. However, and more strange is that if I call the functions inside the require separately the condition is true!
I don't know whats going on at this point, so please any advice or help would be much appreciated. Currently I have three contracts (ERC20, ERC721, ERC721staking) with all functions working just right except for the unstake function.
These are my contracts:
Energy.sol (ERC20):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
/* Simple ERC20 token contract to issue rewards */
contract Energy is ERC20, Ownable {
mapping(address => bool) minters;
constructor() ERC20("ENERGY", "NRG") {
_mint(msg.sender, 100 * 10**decimals());
}
modifier isMinter() {
require(minters[msg.sender], "Caller is not authorized to mint!");
_;
}
function mintRewards(address to, uint256 amount) external isMinter {
_mint(to, amount * 10**decimals());
}
function addMinter(address account) public onlyOwner {
minters[account] = true;
}
}
Fuel.sol (ERC721)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
contract Fuel is ERC721, ERC721Burnable, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIdCounter;
constructor() ERC721("Fuel", "FUEL") {}
function safeMint(address to) public onlyOwner {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
}
}
Generator.sol (staking):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "#openzeppelin/contracts/security/ReentrancyGuard.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "./Energy.sol";
import "./Fuel.sol";
struct Loader {
uint256[] fuelIds;
mapping(uint256 => uint256) loadBlock;
}
contract Generator is Ownable, ReentrancyGuard, IERC721Receiver {
Fuel fuel;
Energy energy;
uint256 rewardsPerBlock = 5;
mapping(address => Loader) loaders;
// Enumeration of fuelIds staked indexes of a loader
mapping(address => mapping(uint256 => uint256)) public fuelIdIndex;
// tracks owner of a fuelId
mapping(uint256 => address) public loaderOf;
constructor(address _fuel, address _energy) {
fuel = Fuel(_fuel);
energy = Energy(_energy);
}
function stake(uint256 fuelId) public nonReentrant {
// safe checks
require(
fuel.ownerOf(fuelId) == msg.sender,
"You're not the owner of this NFT"
);
// push new token to staking collection
loaders[msg.sender].fuelIds.push(fuelId);
// updates index reference of fuelId
uint256 totalFuel = loaders[msg.sender].fuelIds.length;
fuelIdIndex[msg.sender][fuelId] = totalFuel - 1;
// inits staking block
loaders[msg.sender].loadBlock[fuelId] = block.number;
// add it to reference
loaderOf[fuelId] = msg.sender;
fuel.safeTransferFrom(address(msg.sender), address(this), fuelId);
}
function unstake(uint256 fuelId) public nonReentrant {
// safe checks
require(ownedByThis(fuelId), "This fuel is not being loaded here!");
require(
_loaderOf(fuelId) == address(msg.sender),
"You haven't loaded this fuel here!"
);
uint256 lastFuelIndex = loaders[msg.sender].fuelIds.length - 1;
uint256 fuelIndex = fuelIdIndex[msg.sender][fuelId];
// swap current fuelId to last position
if (lastFuelIndex != fuelIndex) {
uint256 lastFuelId = loaders[msg.sender].fuelIds[lastFuelIndex];
loaders[msg.sender].fuelIds[fuelIndex] = lastFuelIndex; // Move the last token to the slot of the to-delete token
fuelIdIndex[msg.sender][lastFuelId] = fuelIndex; // Update the moved token's index
}
// remove the last element from mapping and array
delete fuelIdIndex[msg.sender][fuelId];
delete loaders[msg.sender].fuelIds[lastFuelIndex];
delete loaders[msg.sender].loadBlock[fuelId];
delete loaderOf[fuelId];
// Transfer back to the owner
fuel.safeTransferFrom(address(this), address(msg.sender), fuelId);
claim(fuelId);
}
function claim(uint256 fuelId) public {
// safe checks
require(ownedByThis(fuelId), "This fuel is not being loaded here!");
require(
_loaderOf(fuelId) == address(msg.sender),
"You haven't loaded this fuel here!"
);
uint256 rewardsToClaim = getPendingRewards(msg.sender, fuelId);
energy.mintRewards(msg.sender, rewardsToClaim);
loaders[msg.sender].loadBlock[fuelId] = block.number;
}
function claimAll() public nonReentrant {
// safe checks
require(
loaders[msg.sender].fuelIds.length > 0,
"You have no fuel loaded here!"
);
uint256 totalFuelLoaded = totalFuelLoadedBy(msg.sender);
for (uint256 i = 0; i < totalFuelLoaded; i++) {
uint256 fuelId = loaders[msg.sender].fuelIds[i];
claim(fuelId);
}
}
function getPendingRewards(address account, uint256 fuelId)
public
view
returns (uint256)
{
uint256 loadBlock = loaders[account].loadBlock[fuelId];
uint256 blocksElapsed = block.number - loadBlock;
return blocksElapsed * rewardsPerBlock;
}
function getAllPendingRewards() public view returns (uint256) {
uint256 totalFuelLoaded = totalFuelLoadedBy(msg.sender);
uint256 totalRewards = 0;
for (uint256 i = 0; i < totalFuelLoaded; i++) {
uint256 fuelId = loaders[msg.sender].fuelIds[i];
totalRewards += getPendingRewards(msg.sender, fuelId);
}
return totalRewards;
}
function _loaderOf(uint256 fuelId) public view returns (address) {
return loaderOf[fuelId];
}
function totalFuelLoadedBy(address account) public view returns (uint256) {
return loaders[account].fuelIds.length;
}
function generatorAddress() public view returns (address) {
return address(this);
}
function ownedByThis(uint256 fuelId) public view returns (bool) {
return address(fuel.ownerOf(fuelId)) == address(this);
}
function onERC721Received(
address operator,
address from,
uint256 fuelId,
bytes calldata data
) external override returns (bytes4) {
return this.onERC721Received.selector;
}
}
If you want to test the flow (and I hope you do) be sure to deploy first the Fuel and Energy contracts, then use the address of the contracts as constructor arguments when deploying the Generator contract. Then approveForAll the generator address in the fuel instance, mint some nfts, stake in the generator contract and try to unstake. Every function will work just fine but the unstake function.
Thanks again for any help!
Function ownedByThis takes address ownerOf(fuelId) from you Fuel contract, but after you staked your NFT in Generator.sol, now Generator.sol is owner of this NFT, and your require statement with function ownedByThis is not working. Also i added (delete loaderOf[fuelId];) to the very bottom of your Claim function. Before unstake your nft dont forget to use AddMinter funtion for address of Generator.sol contract. Hope i was useful.
Updated code below
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "#openzeppelin/contracts/security/ReentrancyGuard.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "#openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import "./Energy.sol";
import "./Fuel.sol";
contract Generator is Ownable, ReentrancyGuard, ERC721Holder {
Fuel fuel;
Energy energy;
struct Loader {
uint256[] fuelIds;
mapping(uint256 => uint256) loadBlock;
}
uint256 rewardsPerBlock = 5;
mapping(address => Loader) loaders;
// Enumeration of fuelIds staked indexes of a loader
mapping(address => mapping(uint256 => uint256)) public fuelIdIndex;
// tracks owner of a fuelId
mapping(uint256 => address) public loaderOf;
constructor(address _fuel, address _energy) {
fuel = Fuel(_fuel);
energy = Energy(_energy);
}
function stake(uint256 fuelId) public nonReentrant {
// safe checks
require(
fuel.ownerOf(fuelId) == msg.sender,
"You're not the owner of this NFT"
);
// push new token to staking collection
loaders[msg.sender].fuelIds.push(fuelId);
// updates index reference of fuelId
uint256 totalFuel = loaders[msg.sender].fuelIds.length;
fuelIdIndex[msg.sender][fuelId] = totalFuel - 1;
// inits staking block
loaders[msg.sender].loadBlock[fuelId] = block.number;
// add it to reference
loaderOf[fuelId] = msg.sender;
fuel.safeTransferFrom(address(msg.sender), address(this), fuelId);
}
function unstake(uint256 fuelId) public nonReentrant {
// safe checks
require(msg.sender == loaderOf[fuelId], "You are not the owner");
//require(ownedByThis(fuelId), "This fuel is not being loaded here!");
// require(
// _loaderOf(fuelId) == address(msg.sender),
// "You haven't loaded this fuel here!"
// );
uint256 lastFuelIndex = loaders[msg.sender].fuelIds.length - 1;
uint256 fuelIndex = fuelIdIndex[msg.sender][fuelId];
// swap current fuelId to last position
if (lastFuelIndex != fuelIndex) {
uint256 lastFuelId = loaders[msg.sender].fuelIds[lastFuelIndex];
loaders[msg.sender].fuelIds[fuelIndex] = lastFuelIndex; // Move the
last token to the slot of the to-delete token
fuelIdIndex[msg.sender][lastFuelId] = fuelIndex; // Update the
moved token's index
}
// remove the last element from mapping and array
delete fuelIdIndex[msg.sender][fuelId];
delete loaders[msg.sender].fuelIds[lastFuelIndex];
delete loaders[msg.sender].loadBlock[fuelId];
// Transfer back to the owner
fuel.safeTransferFrom(address(this), address(msg.sender), fuelId);
claim(fuelId);
}
function claim(uint256 fuelId) public {
// safe checks
//require(ownedByThis(fuelId), "This fuel is not being loaded here!");
require(msg.sender == loaderOf[fuelId], "You are not the owner");
// require(
// _loaderOf(fuelId) == address(msg.sender),
// "You haven't loaded this fuel here!"
// );
uint256 rewardsToClaim = getPendingRewards(msg.sender, fuelId);
energy.mintRewards(msg.sender, rewardsToClaim);
loaders[msg.sender].loadBlock[fuelId] = block.number;
delete loaderOf[fuelId];
}
function claimAll() public nonReentrant {
// safe checks
require(
loaders[msg.sender].fuelIds.length > 0,
"You have no fuel loaded here!"
);
uint256 totalFuelLoaded = totalFuelLoadedBy(msg.sender);
for (uint256 i = 0; i < totalFuelLoaded; i++) {
uint256 fuelId = loaders[msg.sender].fuelIds[i];
claim(fuelId);
}
}
function getPendingRewards(address account, uint256 fuelId) public view
returns (uint256) {
uint256 loadBlock = loaders[account].loadBlock[fuelId];
uint256 blocksElapsed = block.number - loadBlock;
return blocksElapsed * rewardsPerBlock;
}
function getAllPendingRewards() public view returns (uint256) {
uint256 totalFuelLoaded = totalFuelLoadedBy(msg.sender);
uint256 totalRewards = 0;
for (uint256 i = 0; i < totalFuelLoaded; i++) {
uint256 fuelId = loaders[msg.sender].fuelIds[i];
totalRewards += getPendingRewards(msg.sender, fuelId);
}
return totalRewards;
}
function _loaderOf(uint256 fuelId) public view returns (address) {
return loaderOf[fuelId];
}
function totalFuelLoadedBy(address account) public view returns (uint256) {
return loaders[account].fuelIds.length;
}
function generatorAddress() public view returns (address) {
return address(this);
}
// function ownedByThis(uint256 fuelId) public view returns (bool) {
// return address(fuel.ownerOf(fuelId)) == address(this);
// }
function onERC721Received(address, address, uint256, bytes memory) public
virtual override returns (bytes4) {
return this.onERC721Received.selector;
}
}

'Gas Estimation Failed' Error when trying to deploy a contract on remix to the Ganache test net

This is what the error reads:
Gas estimation errored with the following message (see below). The
transaction execution will likely fail. Do you want to force sending?
Internal JSON-RPC error. { "message": "Returned error: project ID does
not have access to archive state", "code": -32000, "data": { "stack":
"Error: Returned error: project ID does not have access to archive
state\n at Object.ErrorResponse
(/usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2110625)\n
at a
(/usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2108932)\n
at
/usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2093154\n
at runMicrotasks ()\n at processTicksAndRejections
(internal/process/task_queues.js:95:5)", "name": "Error" } }
I have made the settings for deployment on remix:
Injected Web 3
pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;
interface Structs {
struct Val {
uint256 value;
}
enum ActionType {
Deposit, // supply tokens
Withdraw, // borrow tokens
Transfer, // transfer balance between accounts
Buy, // buy an amount of some token (externally)
Sell, // sell an amount of some token (externally)
Trade, // trade tokens against another account
Liquidate, // liquidate an undercollateralized or expiring account
Vaporize, // use excess tokens to zero-out a completely negative account
Call // send arbitrary data to an address
}
enum AssetDenomination {
Wei // the amount is denominated in wei
}
enum AssetReference {
Delta // the amount is given as a delta from the current value
}
struct AssetAmount {
bool sign; // true if positive
AssetDenomination denomination;
AssetReference ref;
uint256 value;
}
struct ActionArgs {
ActionType actionType;
uint256 accountId;
AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
address otherAddress;
uint256 otherAccountId;
bytes data;
}
struct Info {
address owner; // The address that owns the account
uint256 number; // A nonce that allows a single address to control many accounts
}
struct Wei {
bool sign; // true if positive
uint256 value;
}
}
contract DyDxPool is Structs {
function getAccountWei(Info memory account, uint256 marketId) public view returns (Wei memory);
function operate(Info[] memory, ActionArgs[] memory) public;
}
/**
* #dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see `ERC20Detailed`.
*/
interface IERC20 {
function balanceOf(address account) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
}
contract DyDxFlashLoan is Structs {
DyDxPool pool = DyDxPool(0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e);
address public WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
mapping(address => uint256) public currencies;
constructor() public {
currencies[WETH] = 1;
}
modifier onlyPool() {
require(
msg.sender == address(pool),
"FlashLoan: could be called by DyDx pool only"
);
_;
}
function tokenToMarketId(address token) public view returns (uint256) {
uint256 marketId = currencies[token];
require(marketId != 0, "FlashLoan: Unsupported token");
return marketId - 1;
}
// the DyDx will call `callFunction(address sender, Info memory accountInfo, bytes memory data) public` after during `operate` call
function flashloan(address token, uint256 amount, bytes memory data)
internal
{
IERC20(token).approve(address(pool), amount + 1);
Info[] memory infos = new Info[](1);
ActionArgs[] memory args = new ActionArgs[](3);
infos[0] = Info(address(this), 0);
AssetAmount memory wamt = AssetAmount(
false,
AssetDenomination.Wei,
AssetReference.Delta,
amount
);
ActionArgs memory withdraw;
withdraw.actionType = ActionType.Withdraw;
withdraw.accountId = 0;
withdraw.amount = wamt;
withdraw.primaryMarketId = tokenToMarketId(token);
withdraw.otherAddress = address(this);
args[0] = withdraw;
ActionArgs memory call;
call.actionType = ActionType.Call;
call.accountId = 0;
call.otherAddress = address(this);
call.data = data;
args[1] = call;
ActionArgs memory deposit;
AssetAmount memory damt = AssetAmount(
true,
AssetDenomination.Wei,
AssetReference.Delta,
amount + 1
);
deposit.actionType = ActionType.Deposit;
deposit.accountId = 0;
deposit.amount = damt;
deposit.primaryMarketId = tokenToMarketId(token);
deposit.otherAddress = address(this);
args[2] = deposit;
pool.operate(infos, args);
}
}
contract Flashloan is DyDxFlashLoan {
uint256 public loan;
constructor() public payable {
(bool success, ) = WETH.call.value(msg.value)("");
require(success, "fail to get weth");
}
function getFlashloan(address flashToken, uint256 flashAmount) external {
uint256 balanceBefore = IERC20(flashToken).balanceOf(address(this));
bytes memory data = abi.encode(flashToken, flashAmount, balanceBefore);
flashloan(flashToken, flashAmount, data); // execution goes to `callFunction`
}
function callFunction(
address, /* sender */
Info calldata, /* accountInfo */
bytes calldata data
) external onlyPool {
(address flashToken, uint256 flashAmount, uint256 balanceBefore) = abi
.decode(data, (address, uint256, uint256));
uint256 balanceAfter = IERC20(flashToken).balanceOf(address(this));
require(
balanceAfter - balanceBefore == flashAmount,
"contract did not get the loan"
);
loan = balanceAfter;
// Use the money here!
}
}

Swap ETH to DAI using ISwapRouter from Uniswap

I am trying to get started with Uniswap V3. As an example, I took the most basic use case: Given X amount of ETH do a swap to DAI. Unfortunately, I am not being able to make it work.
There is already a very similar question (with no answer) but slightly different as the code doesn't look like mine.
I am using Hardhat to fork the main-net and then I connect Remix to localhost:8545
npx hardhat node --fork https://mainnet.infura.io/v3/{MY_API_KEY}
Hardhat config down below:
solidity: {
compilers: [
{
version: "0.8.7",
settings: {
optimizer: {
enabled: true,
runs: 1000,
}
}
}
]
}
As you can notice (full contract at the very bottom), the contract offers 3 payable functions:
function convertExactEthToDai() external payable;
function convertEthToExactDai(uint256 daiAmount) external payable;
function getEstimatedETHforDAI(uint daiAmount) external payable returns (uint256);
All of them fail, even getEstimatedETHforDAI, which is fairly simple and readonly (almost). There is no given reason, so I am blind. When I execute the function from Remix, I just get a generic error "Returned error: Error: Transaction reverted without a reason string".
When I look at hardhat console, I see this error:
eth_sendTransaction
Contract call: <UnrecognizedContract>
Transaction: 0xe17fd5a07ca4a0d5a0f91525a77d21152c41f4dc2a9e59feaac4fec7452ba3a1
From: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
To: 0xc351628eb244ec633d5f21fbd6621e1a683b1181
Value: 0 ETH
Gas used: 52351 of 3000000
Block #13238807: 0x7471674d8b76462230d687644e20970137640a7b2fe005c264a04c2af35d4985
Error: Transaction reverted without a reason string
at <UnrecognizedContract>.<unknown> (0xb27308f9f90d607463bb33ea1bebb41c27ce5ab6)
at <UnrecognizedContract>.<unknown> (0xc351628eb244ec633d5f21fbd6621e1a683b1181)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at HardhatNode._mineBlockWithPendingTxs (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\node.ts:1582:23)
at HardhatNode.mineBlock (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\node.ts:435:16)
at EthModule._sendTransactionAndReturnHash (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\modules\eth.ts:1494:18)
at HardhatNetworkProvider._sendWithLogging (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\provider.ts:129:22)
at HardhatNetworkProvider.request (D:\Repositories\TheRock\facutherock.blockchain.gettingstarted\node_modules\hardhat\src\internal\hardhat-network\provider\provider.ts:106:18)
Looks like the contract is invalid, however I can in EtherScan the Quoter and the Router
Any idea? I really appreciate it.
Here is the full contract
// SPDX-License-Identifier: UNLICENCED
pragma solidity ^0.8.0;
pragma abicoder v2;
import "https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/interfaces/ISwapRouter.sol";
import "https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/interfaces/IQuoter.sol";
interface IUniswapRouter is ISwapRouter {
function refundETH() external payable;
}
contract Uniswap3 {
IUniswapRouter public constant uniswapRouter = IUniswapRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
IQuoter public constant quoter = IQuoter(0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6);
address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address private constant WETH9 = 0xd0A1E359811322d97991E03f863a0C30C2cF029C;
function convertExactEthToDai() external payable {
require(msg.value > 0, "Must pass non 0 ETH amount");
uint256 deadline = block.timestamp + 15;
address tokenIn = WETH9;
address tokenOut = DAI;
uint24 fee = 3000;
address recipient = msg.sender;
uint256 amountIn = msg.value;
uint256 amountOutMinimum = 1;
uint160 sqrtPriceLimitX96 = 0;
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams(
tokenIn,
tokenOut,
fee,
recipient,
deadline,
amountIn,
amountOutMinimum,
sqrtPriceLimitX96
);
uniswapRouter.exactInputSingle{ value: msg.value }(params);
uniswapRouter.refundETH();
// refund leftover ETH to user
(bool success,) = msg.sender.call{ value: address(this).balance }("");
require(success, "refund failed");
}
function convertEthToExactDai(uint256 daiAmount) external payable {
require(daiAmount > 0, "Must pass non 0 DAI amount");
require(msg.value > 0, "Must pass non 0 ETH amount");
uint256 deadline = block.timestamp + 15; // using 'now' for convenience, for mainnet pass deadline from frontend!
address tokenIn = WETH9;
address tokenOut = DAI;
uint24 fee = 3000;
address recipient = msg.sender;
uint256 amountOut = daiAmount;
uint256 amountInMaximum = msg.value;
uint160 sqrtPriceLimitX96 = 0;
ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter.ExactOutputSingleParams(
tokenIn,
tokenOut,
fee,
recipient,
deadline,
amountOut,
amountInMaximum,
sqrtPriceLimitX96
);
uniswapRouter.exactOutputSingle{ value: msg.value }(params);
uniswapRouter.refundETH();
// refund leftover ETH to user
(bool success,) = msg.sender.call{ value: address(this).balance }("");
require(success, "refund failed");
}
// do not used on-chain, gas inefficient!
function getEstimatedETHforDAI(uint daiAmount) external payable returns (uint256) {
address tokenIn = WETH9;
address tokenOut = DAI;
uint24 fee = 10000;
uint160 sqrtPriceLimitX96 = 0;
return quoter.quoteExactOutputSingle(
tokenIn,
tokenOut,
fee,
daiAmount,
sqrtPriceLimitX96
);
}
// important to receive ETH
receive() payable external {}
}