// I am getting gas estimation error while calling the function "drawNumbers". Can anyone please explain why I'm getting this error
pragma solidity ^0.8.0;
contract ABC{
uint max = 3;
uint[] numbers;
mapping(uint => bool) isNFTWon;
function drawNumbers() public returns (uint256[] memory) {
for(uint i=0; i<max; i++){
uint Ids = uint (keccak256(abi.encodePacked(block.timestamp, block.difficulty, msg.sender))) % 1000;
if(isNFTWon[Ids] == true){
i -= 1;
continue;
}
isNFTWon[Ids] = true;
numbers.push(Ids);
}
return numbers;
}
}
// I'm getting this error-----
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. { "code": -32000, "message": "gas required exceeds allowance (20000000)" }
This error shows that your transaction is going to be reverted. There is probably some requirement state failure or call failure.
The problem is seems like on line
i -= 1;
i is uint variable and when you decrementing the value of i, can underflow but with solidity version 0.8+ there is overflow/underflow check by default. If you want to try to better understand the problem is you can simply decrease your solidity version under 0.8 or bracket these lines like
unchecked {
i -= 1;
}
If value of i will rise to uint max. then we can decide that the problem caused by that.
Related
I am learning solidity. The code i have written compiles correctly and the same solution is given in the solutions of the question but it keeps reverting without any output. Please check the code once and let me know the mistake.
The question is to transfer the given amount from the amount array to the to array from the same position, i.e., from ith position of amount to ith position of to.
It shows the error message : "The transaction has been reverted to the initial state.
Note: The called function should be payable if you send value and the value you send should be less than your current balance."
//SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.5.0 <0.9.0;
contract Day4 {
address owner;
constructor() {
owner = msg.sender;
}
function send(address payable[] memory to, uint256[] memory amount)
public
payable
ownerOnly
{
require(to.length == amount.length, "to must be same length as amount");
for (uint256 i = 0; i < to.length; i++) {
to[i].transfer(amount[i]); //to array - 0x00 0x01 0x02
//amount array - 10 20 30
}
}
modifier ownerOnly() {
require(msg.sender == owner,"You are not the owner");
_;
}
}
Your issue depends from transfer() function. In details, this function allow you to transfer a specific amount from the smart contract balance to another address.
In your case, when you call directly send() function the smart contract balance is 0 because I think (since I don't see a deposit method) that you didn't deposit an amount to it.
For this reason, when you call send() function it reverts and doesn't works.
To solve this issue before call send() method, try to deposit a specific amount of ether to smart contract and then call send() method.
REMEMBER: The sum of array amount must be less to smart contract balance, otherwise it reverts.
I adjusted your smart contract in this way:
//SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.5.0 <0.9.0;
contract Day4 {
address owner;
constructor() {
owner = msg.sender;
}
function send(address payable[] memory to, uint256[] memory amount)
public
payable
ownerOnly
{
require(to.length == amount.length, "to must be same length as amount");
for (uint256 i = 0; i < to.length; i++) {
to[i].transfer(amount[i]);
}
}
modifier ownerOnly() {
require(msg.sender == owner,"You are not the owner");
_;
}
// NOTE: Call this function for first, and before this operation set the values inside msg.value textbox in
// Remix IDE (if you're using it)
function deposit() public payable {
}
}
I want the following function to activate after 6 mins(360 secs) of contract deployment because my task requires a withdrawal lock. should I put if (block.timestamp > 360) before the function or inside the function just before the remaining code?
function withdraw(uint256 amount) external updateReward(msg.sender) nonReentrant {
if (block.timestamp > 360) {
s_totalSupply -= amount;
s_balances[msg.sender] -= amount;
emit WithdrewStake(msg.sender, amount);
// transfer: send tokens from contract back to msg.sender.
bool success = s_stakingToken.transfer(msg.sender, amount);
if (!success) {
revert TransferFailed(); // revert resets everything done in a failed transaction.
}}
}
But I'm not even sure if if (block.timestamp > 360) is the right code for this case.
Figured it out for you my brother:
pragma solidity ^0.8.7;
import "hardhat/console.sol";
contract TimeTest{
uint256 public initialTime;
constructor () public{
initialTime = block.timestamp;
}
function withdraw() public {
uint256 nowTime = block.timestamp-initialTime; // time between deployment of contract and now.
console.log(nowTime);
if (nowTime > 60) {
console.log("Time is up");
}
}
}
The reason why you have to do that is because block.timestamp doesnt represent the time since the contract was deployed, but the time since the unix epoch. The name of the variable is a little misleading.
You can find more info here: https://docs.soliditylang.org/en/v0.8.13/units-and-global-variables.html
Or here:https://programtheblockchain.com/posts/2018/01/12/writing-a-contract-that-handles-time/ (just know that "now" doesnt exist anymore since version 0.0.7. "now" was equivalent to "block.timestamp". But the tutorial is still valid if you replace "now" with "block.timestamp".
I'm wondering what's the difference from
contract TestToken {
mapping(address => uint) balance;
error InsufficientBalance(uint256 available, uint256 required);
function transfer(address to, uint256 amount) public {
if (amount > balance[msg.sender])
// Error call using named parameters. Equivalent to
// revert InsufficientBalance(balance[msg.sender], amount);
revert InsufficientBalance({
available: balance[msg.sender],
required: amount
});
balance[msg.sender] -= amount;
balance[to] += amount;
}
// ...
}
and
contract TestToken {
mapping(address => uint) balance;
error InsufficientBalance(uint256 available, uint256 required);
function transfer(address to, uint256 amount) public {
require(balance[msg.sender]<amount, "Insufficient Balance");
balance[msg.sender] -= amount;
balance[to] += amount;
}
// ...
}
to handle error in solidity
From the low-level standpoint, both approaches are the same. Both throw an exception with an array of bytes as the exception data.
You can catch both errors in the low-level catch (bytes memory) block
function foo() public {
try this.transfer(address(0x123), 2) {
// ok
} catch (bytes memory data) {
// returns either the encoded object or the encoded string
}
}
But you can only catch the string-encoded error with the "regular" catch Error(string memory) block
function foo() public {
try this.transfer(address(0x123), 2) {
// ok
} catch Error (string memory reason) {
// returns the string message
// fails to catch if an object is returned
}
}
Docs: https://docs.soliditylang.org/en/v0.8.13/control-structures.html#try-catch
Note that there is a logical error in the second snippet.
// `balance` needs to be lower than `amount`
// otherwise fail
require(balance[msg.sender]<amount, "Insufficient Balance");
should be
// `balance` needs to be larger or equal than `amount`
// otherwise fail
require(balance[msg.sender] => amount, "Insufficient Balance");
In require we specify a condition that, if false, it will revert.
Revert and error is more sophisticated version of the require, where you can specify the condition to depend on the context and create a custom error.
For example
error InvalidAmount (uint256 sent, uint256 minRequired);
contract TestToken {
mapping(address => uint) balances;
uint minRequired;
constructor (uint256 _minRequired) {
minRequired = _minRequired;
}
function list() public payable {
uint256 amount = msg.value;
if (amount < minRequired) {
revert InvalidAmount({
sent: amount,
minRequired: minRequired
});
}
balances[msg.sender] += amount;
}
}
In that code an error called "Invalid amount" is required and it is made to depend on the minimum amount fed in to the constructor. If amount is less than minimum amount, transaction will revert with the custom error which will specify the amount fed by the user and the minimum amount fed in to constructor.
I am working on a smart contract, and I am testing it by deploying it on truffle. While it compiles fine, when I call the train() function, I get the following error:
Error: VM Exception while processing transaction: invalid opcode
After reading a bit on this, I understood it is usually caused after a revert has occurred, so I tried commenting out the 2 require functions I had just to see if it would behave differently, and it did not.
Checking out this question did not help me, or I did not see how it could.
Here is the train() function, as well as the mapping and struct type I am using in it. I should note that upon creation of a Developer, their wallet is set to 300 so I do not see how the first call of the train function by the owner could revert.
struct Developer {
address owner;
string name;
bytes32 namehash;
bytes32[] skills;
uint256[] skill_levels;
uint wallet;
}
mapping (bytes32=>Developer) public developers_all;
function train(string _name, bytes32 _skill) public {
bytes32 h = keccak256(abi.encodePacked(_name));
require(developers_all[h].owner == msg.sender, "Only the owner of the developer can train them");
require(developers_all[h].wallet >= 150, "Insufficient funds");
uint256 i = 0;
do {
if (developers_all[h].skills[i] == _skill) {
developers_all[h].skill_levels[i]++;
} else if ((i == (developers_all[h].skills.length - 1)) || (developers_all[h].skills.length == 0)) {
developers_all[h].skills.push(_skill);
developers_all[h].skill_levels.push(1);
}
i++;
} while (i < developers_all[h].skills.length);
developers_all[h].wallet = developers_all[h].wallet - 150;
}
Thank you for any help.
This is most likely because you are trying to access the first entry of an empty array. You're using a do while loop, and you're trying to access developers_all[h].skills[i] before you're checking developers_all[h].skills.length == 0, so it is possible that the array is empty at the first if statement in the do while.
You could rewrite the code to something like the following, to make sure you're never accessing an unassigned array slot.
bool foundSkill = false;
for (uint i = 0; i < developers_all[h].skills.length; i++) {
if (developers_all[h].skills[i] == _skill) {
developers_all[h].skill_levels[i]++;
foundSkill = true;
break;
}
}
if (!foundSkill) {
developers_all[h].skills.push(_skill);
developers_all[h].skill_levels.push(1);
}
Do note that looping through the whole array and doing the comparisons is quite costly, and can become impossible if the array size gets too big. You might want consider changing the structure to something like:
struct Developer {
address owner;
string name;
bytes32 namehash;
mapping(bytes32 => uint) skill_levels;
uint wallet;
}
That way you could just replace the whole thing with
developers_all[h].skill_levels[skill]++;
But you wouldn't be able to loop over the skills.
This contract appears to be a game offering 1/16 odds at the balance of the contract. However, when running the code in a debugger it appears as if the 'secretNumber' variable is being overwritten before it is used.
pragma solidity ^0.4.19;
contract CryptoRoulette {
uint256 private secretNumber;
uint256 public lastPlayed;
uint256 public betPrice = 0.1 ether;
address public ownerAddr;
struct Game {
address player;
uint256 number;
}
Game[] public gamesPlayed;
function CryptoRoulette() public {
ownerAddr = msg.sender;
shuffle();
}
function shuffle() internal {
// initialize secretNumber with a value between 0 and 15
secretNumber = uint8(sha3(now, block.blockhash(block.number-1))) % 16;
}
function play(uint256 number) payable public {
require(msg.value >= betPrice && number < 16);
Game game;
game.player = msg.sender;
game.number = number;
gamesPlayed.push(game);
if (number == secretNumber) {
// win!
msg.sender.transfer(this.balance);
}
shuffle();
lastPlayed = now;
}
function kill() public {
if (msg.sender == ownerAddr && now > lastPlayed + 1 days) {
suicide(msg.sender);
}
}
function() public payable { }
}
The way secretNumber is updated, it should always be less than 16
secretNumber = uint8(sha3(now, block.blockhash(block.number-1))) % 16;
This debugger output shows that during execution of if (number == secretNumber) { the value of secretNumber has been updated to, oddly enough, the callers address (msg.sender).
`
(243) PUSH1 0x00
000000000000000000000000000000000000000000000000000000006898f82b
0000000000000000000000000000000000000000000000000000000000000143
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000000000000000000000000000000000000000000000 (top)
40: if (number == secretNumber) {
^^^^^^^^^^^^
debug(develop:0x98cacf83...)> i
CryptoRoulette.sol | 0xbd2c938b9f6bfc1a66368d08cb44dc3eb2ae27be:
40: if (number == secretNumber) {
^^^^^^
debug(develop:0x98cacf83...)> p
CryptoRoulette.sol | 0xbd2c938b9f6bfc1a66368d08cb44dc3eb2ae27be:
(245) DUP3
000000000000000000000000000000000000000000000000000000006898f82b
0000000000000000000000000000000000000000000000000000000000000143
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000627306090abab3a6e1400e9345bc60c78a8bef57 (top)
40: if (number == secretNumber) {
^^^^^^
My guess is that the storage access before the condition is causing the stack to be corrupted somehow.
Is this a known vulnerability? Can someone please explain what is going on?
This is a common issue when attempting to create a local reference without specifying the correct storage location.
From the Solidity docs:
There are defaults for the storage location depending on which type of variable it concerns:
state variables are always in storage
function arguments are in memory by default
local variables of struct, array or mapping type reference storage by default
local variables of value type (i.e. neither array, nor struct nor mapping) are stored in the stack
The bolded comment indicates that the line Game game; defaults to storage. If you don't initialize a storage variable, it will point to storage slot 0 by default. The end result is when you make a change to game (with game.player = msg.sender;), it will write the value out to the first slot, which will be whatever the first variable is in your contract (in this case, secretNumber).
The correct way to write this is to use Game memory game;. The compiler will give you a warning if you omit the memory keyword for this exact reason.