4

Hi I have this error :

transact to Roulette.betSingle errored: Error: gas required exceeds allowance or always failing transaction

My programm :

contract Roulette {

uint public lastRoundTimestamp;
uint public nextRoundTimestamp;

address _creator;
uint _interval;

enum BetType { Single, Odd, Even }

struct Bet {
    BetType betType;
    address player;
    uint number;
    uint value;
}

Bet[] public bets;

function getBetsCountAndValue() public constant returns(uint, uint) {
    uint value = 0;
    for (uint i = 0; i < bets.length; i++) {
        value += bets[i].value;
    }
    return (bets.length, value);
}

event Finished(uint number, uint nextRoundTimestamp);

modifier transactionMustContainEther() {
    if (msg.value == 0) throw;
    _;
}

modifier bankMustBeAbleToPayForBetType(BetType betType) {
    uint necessaryBalance = 0;
    for (uint i = 0; i < bets.length; i++) {
        necessaryBalance += getPayoutForType(bets[i].betType) * bets[i].value;
    }
    necessaryBalance += getPayoutForType(betType) * msg.value;
    if (necessaryBalance > this.balance) throw;
    _;
}

function getPayoutForType(BetType betType) constant returns(uint) {
    if (betType == BetType.Single) return 35;
    if (betType == BetType.Even || betType == BetType.Odd) return 2;
    return 0;
}

function Roulette(uint interval) {
    _interval = interval;
    _creator = msg.sender;
    nextRoundTimestamp = 1517784279 + _interval;
}

function betSingle(uint number) public payable transactionMustContainEther() bankMustBeAbleToPayForBetType(BetType.Single) {
    if (number > 36) throw;
    bets.push(Bet({
        betType: BetType.Single,
        player: msg.sender,
        number: number,
        value: msg.value
    }));
}

function betEven() public payable transactionMustContainEther() bankMustBeAbleToPayForBetType(BetType.Even) {
    bets.push(Bet({
        betType: BetType.Even,
        player: msg.sender,
        number: 0,
        value: msg.value
    }));
}

function betOdd() public payable transactionMustContainEther() bankMustBeAbleToPayForBetType(BetType.Odd) {
    bets.push(Bet({
        betType: BetType.Odd,
        player: msg.sender,
        number: 0,
        value: msg.value
    }));
}

function launch() public {
    if (now < nextRoundTimestamp) throw;

    uint number = uint(block.blockhash(block.number - 1)) % 37;

    for (uint i = 0; i < bets.length; i++) {
        bool won = false;
        uint payout = 0;
        if (bets[i].betType == BetType.Single) {
            if (bets[i].number == number) {
                won = true;
            }
        } else if (bets[i].betType == BetType.Even) {
            if (number > 0 && number % 2 == 0) {
                won = true;
            }
        } else if (bets[i].betType == BetType.Odd) {
            if (number > 0 && number % 2 == 1) {
                won = true;
            }
        }
        if (won) {
            if (bets[i].player.send(bets[i].value * getPayoutForType(bets[i].betType)))
            throw;
        }
    }

    uint thisRoundTimestamp = nextRoundTimestamp;
    nextRoundTimestamp = thisRoundTimestamp + _interval;
    lastRoundTimestamp = thisRoundTimestamp;

    bets.length = 0;

    Finished(number, nextRoundTimestamp);
}

}

HELP ME PLEASE ! :)

  • I compile this program with remix – Elsa Mar 8 '18 at 23:14
  • You get this error when submitting program to network? Or when you try to run the contract? – quantumpotato Mar 8 '18 at 23:46
  • when i submit a method. The contract is deployed normally. Sorry for my english i'am french lol – Elsa Mar 9 '18 at 0:05
  • How many bets do you have? getBetsCountAndValue() is bad practice to do for loops. Too many bets and you will exceed gas amount for the block. – quantumpotato Mar 9 '18 at 0:23
  • 1
    Please tell me exactly the call you are making, which function, what parameters, your gas, your gasPrice. – quantumpotato Mar 9 '18 at 15:07
2

You problem is caused by this lines

    necessaryBalance += getPayoutForType(betType) * msg.value;
    if (necessaryBalance > this.balance) throw;

At the beginning this.balance is zero.

When you call betSingle for the first time msg.value will be equal to this.balance. Now getPayoutForType(betType) will return 35.

And then necessaryBalance = 35 * msg.value and the check if (necessaryBalance > this.balance) throw will always be true causing the throws and reverse all changes to the contract.

The other bets functions will fail similarly, blocking the contract.


A few advices:

  • Avoid using throw, it is considered a bad practice, you should use require or assert.
  • Avoid unbounded for/while loops, they can cause out of gas errors if you have too many itaerations.
1

One of the reasons when a transaction requires too much gas to run is because the contract throws an exception. I noticed a few places where exceptions can be thrown in betSingle():

  • calling betSingle() with input > 36
  • throw in the modifier, transactionMustContainEther() if the transaction does not have a value amount
  • throw in the modifier, bankMustBeAbleToPayForBetType() if the contract does not have a balance to pay for the bet. Make sure the contract is created with a initial balance.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.