Gas Estimation Error – Understanding Common Issues in Solidity Remix

gasremixsolidity

I got the "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." in remix while using the drawWinner function

    pragma solidity ^0.4.18;

    contract RecurringLottery {
         struct Round {
            uint deployBlock;
            uint endBlock;
            uint drawBlock;
            Entry[] entries;
            uint totalQuantity;
            address winner;
       }
struct Entry {
    address buyer;
    uint quantity;
}

uint constant public TICKET_PRICE = 1e15;

mapping(uint => Round) public rounds;
uint public round;
uint public duration;
mapping (address => uint) public balances;

// duration is in blocks. 1 day = ~5500 blocks
function RecurringLottery (uint _duration) public {
    round = 1;
    duration = _duration;
    rounds[round].deployBlock = block.number;
    rounds[round].endBlock = block.number + duration;
    rounds[round].drawBlock = block.number + duration + 5;
}

function buy () payable public {
    require(msg.value % TICKET_PRICE == 0);

    if (block.number > rounds[round].endBlock) {
        round += 1;
        rounds[round].deployBlock = block.number;
        rounds[round].endBlock = block.number + duration;
        rounds[round].drawBlock = block.number + duration + 5;
    }

    uint quantity = msg.value / TICKET_PRICE;
    Entry memory entry = Entry(msg.sender, quantity);
    rounds[round].entries.push(entry);
    rounds[round].totalQuantity += quantity;
}

function drawWinner (uint roundNumber) public {
    Round storage drawing = rounds[roundNumber];
    require(drawing.winner ==  address(0));
    require(block.number > drawing.drawBlock);
    require(drawing.entries.length > 0);

    // pick winner
    bytes32 rand = keccak256(
        block.blockhash(drawing.drawBlock)
    );
    uint counter = uint(rand) % drawing.totalQuantity;
    for (uint i=0; i < drawing.entries.length; i++) {
        uint quantity = drawing.entries[i].quantity;
        if (quantity > counter) {
            drawing.winner = drawing.entries[i].buyer;
            break;
        }
        else
            counter -= quantity;
    }

    balances[drawing.winner] += TICKET_PRICE * drawing.totalQuantity;
}

function withdraw () public {
    uint amount = balances[msg.sender];
    balances[msg.sender] = 0;
    msg.sender.transfer(amount);
}

function deleteRound (uint _round) public {
    require(block.number > rounds[_round].drawBlock + 100);
    require(rounds[_round].winner != address(0));
    delete rounds[_round];
}

function () payable public {
    buy();
}

}

Best Answer

You usually get that error when code execution fails, this can be because of regular execution or because one of the reverts is catching an error, in your case, problem were the reverts, here is how you can find out:

This feature is not yet on nodes, only in in javascriptVM which you can use in remix, or in your truffle tests/ganache, but it's recommended that you start using it now; you can add an error message to your reverts, this is the easiest way to get better comprehensive errors, IE:

require(condition, "string returned if condition fails");

Note that I'm using javascript vm for this example instead of injected web3. When I execute your code, the first error we can find is in the second require:

require(block.number > drawing.drawBlock, "Can't draw yet");

We can't draw until some blocks have passed, I forced the generation of some blocks in the VM by calling other functions, if you are doing this on ethereum, you just need to wait.

After solving this problem, it crashed in the next line:

require(drawing.entries.length > 0, "No entries for that round");

So I called buy() a couple of times sending the amount of ether specified in TICKET_PRICE, then I checked that the entries where added by calling rounds(numberOfRound), and after calling drawWinner(numberOfRound) with a valid round, the function worked correctly.

You should create a call that will tell you if that function will work or it will fail by checking those conditions, that way you can solve a ton of headaches, remember to put error messages on require, and if you want to see everything step by step, the debugger provided in remix is pretty useful.

TLDR: For your code to work, you need to make sure that enough blocks have passed, and that the round has at least one entry.

By the way, there are a couple of vulnerabilities in your drawWinner() function, don't use it on production!

Related Topic