Error: value out-of-bounds on callbackGasLimit variable - solidity

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

Related

VM Exception while processing transaction: reverted with reason string 'message'

I don't understand what's wrong, why I am getting this error on function testing.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "#openzeppelin/contracts/utils/Address.sol";
contract Voting {
address public owner;
uint public counter;
uint public minCandidates = 2;
uint public maxCandidates;
uint public immutable Comission;
struct Candidate {
uint balance;
bool isExistOnThisVoting;
}
struct _Voting {
bool started;
address Winner;
uint StartDate;
uint WinnerBalance;
uint Bank;
uint Period;
mapping(address => Candidate) Candidates;
}
mapping(uint => _Voting) private Votings;
modifier onlyOwner() {
require(msg.sender == owner, "Sorry, but you are not an owner!");
_;
}
constructor(uint _maxCandidates, uint _comission) {
owner = msg.sender;
Comission = _comission;
maxCandidates = _maxCandidates;
}
function addVoting(address[] calldata _candidates, uint _period) public onlyOwner {
require(minCandidates <= _candidates.length && _candidates.length < maxCandidates,
"The number of candidates must comply with the voting rules!"
);
Votings[counter].Period = _period;
for( uint i = 0; i < _candidates.length; i++) {
addCandidate(counter, _candidates[i]);
}
emit votingDraftCreated(counter);
counter++;
}
function editVotingPeriod(uint _id, uint _newPeriod) public onlyOwner {
require(Votings[_id].started = false, "The voting has already begun!");
Votings[_id].Period = _newPeriod;
}
function addCandidate(uint _id, address _candidate) public onlyOwner {
require(address(_candidate) != address(0), "This candidate with zero address!");
require(Address.isContract(_candidate) == false, "A contract can't be a candidate!");
require(Votings[_id].started, "The voting has already begun!");
Votings[_id].Candidates[_candidate].isExistOnThisVoting = true;
emit candidateInfo(_id, _candidate, true);
}
function deleteCandidate(address _candidate, uint _id) public onlyOwner {
require(Votings[_id].started, "The voting has already begun!");
Votings[_id].Candidates[_candidate].isExistOnThisVoting = false;
emit candidateInfo(_id, _candidate, false);
}
function startVoting(uint _id) public onlyOwner {
Votings[_id].started = true;
Votings[_id].StartDate = block.timestamp;
emit votingStarted(_id, block.timestamp);
}
function takePartInVoting(uint _id, address _candidate) public payable {
require(Address.isContract(msg.sender) == false, "A contract can't vote!");
require(Votings[_id].started, "The voting doesn't start!");
require(Votings[_id].StartDate + Votings[_id].Period > block.timestamp, "The voting has ended!");
require(checkCandidate(_id, _candidate), "This candidates does not exist in this voting!");
Votings[_id].Candidates[_candidate].balance += msg.value;
Votings[_id].Bank += msg.value;
if (Votings[_id].Candidates[_candidate].balance > Votings[_id].WinnerBalance) {
Votings[_id].WinnerBalance = Votings[_id].Candidates[_candidate].balance;
Votings[_id].Winner = _candidate;
}
}
function withDrawPrize(uint _id) public {
require(Votings[_id].started, "The voting doesn't start!");
require(Votings[_id].StartDate + Votings[_id].Period < block.timestamp, "The voting is not ended yet!");
require(msg.sender == Votings[_id].Winner, "You are not a winner!");
require(Votings[_id].Bank > 0, "You have already receive your prize!");
uint amount = Votings[_id].Bank;
uint ownerComission = (Comission * amount) / 100;
uint clearAmount = amount - ownerComission;
Votings[_id].Bank = 0;
payable(owner).transfer(ownerComission);
payable(msg.sender).transfer(clearAmount);
}
function checkCandidate(uint _id, address _candidate) public view returns(bool) {
return(Votings[_id].Candidates[_candidate].isExistOnThisVoting);
}
function getVotingInfo(uint256 _id) public view returns (
bool,
uint256,
uint256,
uint256,
uint256,
address
) {
return(
Votings[_id].started,
Votings[_id].StartDate,
Votings[_id].WinnerBalance,
Votings[_id].Bank,
Votings[_id].Period,
Votings[_id].Winner);
}
function setMaxCandidates(uint _maxCandidates) public onlyOwner {
require(minCandidates <= _maxCandidates, "Minimum number of candidates is 2");
maxCandidates = _maxCandidates;
}
event candidateInfo(uint indexed id, address indexed candidate, bool existOnThisVoting);
event votingDraftCreated(uint indexed id);
event votingStarted(uint indexed id, uint startDate);
}
Here is the contract and the test for it:
const { time, loadFixture } = require("#nomicfoundation/hardhat-network-helpers");
const { ethers } = require("hardhat");
const { expect } = require("chai");
describe("Voting", function () {
async function deploy() {
const [owner, user] = await ethers.getSigners();
const Voting = await ethers.getContractFactory("Voting");
const myVoting = await Voting.deploy(5, 10);
return { myVoting, owner, user };
}
it("An owner can change voting's period", async function () {
const { myVoting, owner } = await loadFixture(deploy);
await myVoting.connect(owner).editVotingPeriod(0, 200);
const _votingInfo = await myVoting.getVotingInfo(0);
expect(_votingInfo[2]).to.equal(200);
});
it("Owner can create another voting", async function () {
const { myVoting, owner, user } = await loadFixture(deploy);
const counter_before = await myVoting.counter();
let candidates = new Array();
for (i = 1; i < 4; i++) candidates.push(owner.address);
await myVoting.connect(owner).addVoting(candidates, 100);
const is_candidate = await myVoting.checkCandidate(counter_before, user.address);
expect(is_candidate).to.equal(false);
});
And only two tests are failing with message -> Error: VM Exception while processing transaction: reverted with reason string 'The voting has already begun!'
Could somebody help me to understand why did I get this error?
I don't understand why I got this error.
I found a bug in the solidity code. You're using = instead of == in this line:
require(Votings[_id].started = false, "The voting has already begun!");
I'm not sure this alone will fix your problem, but try again!

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;
}
}

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

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
);

Adding a return function to Truffles Pet Shop

I am following the Truffle tutorials, and am on https://www.trufflesuite.com/tutorials/pet-shop. I would like to add a return function that sets the adopters[PetId] in the solidity contract back to the 0 address. I currently have:
function returnPet(uint petId) public returns (uint) {
require(petId >= 0 && petId <= 15);
adopters[petId] = msg.sender;
return petId;
}
in my Adoption.sol
and in my app.js is:
handleReturn: function(event) {
event.preventDefault();
var petId = parseInt($(event.target).data('id'));
var adoptionInstance;
web3.eth.getAccounts(function(error, accounts) {
if (error) {
console.log(error);
}
var account = accounts[0];
App.contracts.Adoption.deployed().then(function(instance) {
adoptionInstance = instance;
return adoptionInstance.returnPet(petId);
}).then(function(result) {
return App.markAdopted();
}).catch(function(err) {
console.log(err.message);
});
});
but it hits the catch and returns: invalid address.
In short:
How do I zero out an address in an array of addresses?emphasized text
Your current code in returnPet() sets the adopters[petId] value to msg.sender, which means "the caller address".
I would like to add a return function that sets the adopters[PetId] in the solidity contract back to the 0 address
You can do it by setting the value to address(0).
So your function will then look like
function returnPet(uint petId) public returns (uint) {
require(petId >= 0 && petId <= 15);
adopters[petId] = address(0);
return petId;
}

oraclize_newRandomDSQuery (Oraclize) on testrpc

I have the following contract that i plan on using as an external contract in a different contract:
pragma solidity ^0.4.24;
import "./Ownable.sol";
import "./oraclizeAPI.sol";
interface EtherHiLoRandomNumberRequester {
function incomingRandomNumber(address player, uint8 randomNumber) external;
function incomingRandomNumberError(address player) external;
}
interface EtherHiLoRandomNumberGenerator {
function generateRandomNumber(address player, uint8 max) external returns (bool);
}
/// #title EtherHiLoRandom
/// #dev the contract than handles the EtherHiLo random numbers
contract EtherHiLoRandom is usingOraclize, Ownable, EtherHiLoRandomNumberGenerator {
uint8 constant FAILED_ROLE = 69;
uint public rngCallbackGas;
mapping(bytes32 => uint) private failedRolls;
mapping(bytes32 => address) private rollIdToPlayer;
mapping(bytes32 => uint8) private rollIdToMax;
mapping(bytes32 => address) private rollIdToCaller;
constructor() public {
oraclize_setProof(proofType_Ledger);
setRNGCallbackGasConfig(1500000, 10000000000);
}
function generateRandomNumber(address player, uint8 max) external returns (bool) {
bytes32 rollId = oraclize_newRandomDSQuery(0, 7, rngCallbackGas);
if (failedRolls[rollId] == FAILED_ROLE) {
delete failedRolls[rollId];
delete rollIdToPlayer[rollId];
delete rollIdToMax[rollId];
delete rollIdToCaller[rollId];
return false;
}
rollIdToPlayer[rollId] = player;
rollIdToMax[rollId] = max;
rollIdToCaller[rollId] = msg.sender;
return true;
}
function __callback(bytes32 rollId, string _result, bytes _proof) public {
require(msg.sender == oraclize_cbAddress());
address player = rollIdToPlayer[rollId];
address caller = rollIdToCaller[rollId];
uint8 max = rollIdToMax[rollId];
// avoid reorgs
if (player == address(0)) {
failedRolls[rollId] = FAILED_ROLE;
return;
}
delete failedRolls[rollId];
delete rollIdToPlayer[rollId];
delete rollIdToMax[rollId];
delete rollIdToCaller[rollId];
EtherHiLoRandomNumberRequester requester = EtherHiLoRandomNumberRequester(caller);
if (oraclize_randomDS_proofVerify__returnCode(rollId, _result, _proof) != 0) {
requester.incomingRandomNumberError(player);
} else {
uint8 randomNumber = uint8(uint(keccak256(_result)) % max);
requester.incomingRandomNumber(player, randomNumber);
}
}
/// OWNER / MANAGEMENT RELATED FUNCTIONS
function transferBalance(address to, uint amount) public onlyOwner {
to.transfer(amount);
}
function setRNGCallbackGasConfig(uint gas, uint price) public onlyOwner {
rngCallbackGas = gas;
oraclize_setCustomGasPrice(price);
}
function destroyAndSend(address _recipient) public onlyOwner {
selfdestruct(_recipient);
}
}
And I have the following test:
pragma solidity ^0.4.24;
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/EtherHiLoRandom.sol";
contract TestEtherHiLoRandom is EtherHiLoRandomNumberRequester {
function incomingRandomNumber(address player, uint8 randomNumber) {
//require(false, "incomingRandomNumber");
}
function incomingRandomNumberError(address player) {
//require(false, "incomingRandomNumberError");
}
function testInitialBalanceUsingDeployedContract() {
EtherHiLoRandom subject = EtherHiLoRandom(DeployedAddresses.EtherHiLoRandom());
bool result = subject.generateRandomNumber(msg.sender, 5);
Assert.isTrue(result, "generateRandomNumber failed");
Assert.isTrue(true, "itchy balls");
}
}
My test fails in TestEtherHiLoRandom when it calls oraclize_newRandomDSQuery. The error that i get is:
TestEtherHiLoRandom
1) testInitialBalanceUsingDeployedContract
> No events were emitted
0 passing (3s) 1 failing
1) TestEtherHiLoRandom
testInitialBalanceUsingDeployedContract:
Error: VM Exception while processing transaction: revert
at Object.InvalidResponse (node_modules/truffle/build/webpack:/~/web3/lib/web3/errors.js:38:1)
at node_modules/truffle/build/webpack:/~/web3/lib/web3/requestmanager.js:86:1
at node_modules/truffle/build/webpack:/packages/truffle-provider/wrapper.js:134:1
at XMLHttpRequest.request.onreadystatechange (node_modules/truffle/build/webpack:/~/web3/lib/web3/httpprovider.js:128:1)
at XMLHttpRequestEventTarget.dispatchEvent (node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:64:1)
at XMLHttpRequest._setReadyState (node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:354:1)
at XMLHttpRequest._onHttpResponseEnd (node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:509:1)
at IncomingMessage.<anonymous> (node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:469:1)
at endReadableNT (_stream_readable.js:1056:12)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
at process._tickCallback (internal/process/next_tick.js:180:9)
Any idea what i'm doing wrong? I haven't yet ran it on any test networks, only my local testrpc (i'm using ethereum-bridge and have verified that the Oraclize contract is properly deployed on the testrpc).