in

Question about lottery token contract

As a fun little project I wanted to deploy to a testnet a lottery ERC20 token and here is what I came up with (this is not final version):

pragma solidity ^0.8.0;

import “./ERC20.sol”;

contract LotToken is ERC20 {
uint256 private _ethTicketRatio = 1;
uint256 private _winChance = 4 * (10 ** 18); //4 eth amount of tickets gurantees win
constructor() ERC20(“Lottery Token”, “LOTTERY”) {}

function buyTickets() public payable {
require(msg.value != 0, “LOTTERY: Must send eth to buy tickets”);
_mint(msg.sender, msg.value * _ethTicketRatio);
}

function useTickets(address payable _player, uint256 _tickets) public{
require(balanceOf(msg.sender) >= _tickets, “LOTTERY: Tried to use more tickets than sender has”);
_burn(msg.sender, _tickets);

uint256 _roll = uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, block.difficulty))) % _winChance;
if (_roll <= _tickets) {
_player.transfer(address(this).balance);
}
}

function winChance() public view returns(uint256) {
return _winChance;
}
}

Even though I’m not building on deploying it to the mainnet, I wondered why my method for randomness is insecure. I’ve read that theoretically, a bad actor can run the hash I’m using in the function and synchronizing ahead of time to play at the right moment. Is it really feasible to time a contract call to the second?

I would like an in-depth explanation of a theoretical attack and if there are other solution besides oracles.

What do you think?

Leave a Reply

Your email address will not be published. Required fields are marked *

GIPHY App Key not set. Please check settings

4 Comments

  1. My understanding is that a bad acting miner that *happens* to be the block publisher when the lottery takes place essentially has a chance to manipulate the block just before it gets submitted.

    Given your example, I think one thing to manipulate would simply be the timestamp of the block being published. Because the miner gets to submit the timestamp with the block (within some tolerance).

    As a bad acting miner, I could see what happens if I delay publishing the block by 1 microsecond. Maybe my ticket wins. If not, then what about 2, 3, 4 μs? I can simulate many different outcomes (it’s just a keccak256 call) very quickly, until I find some timestamp that is more favorable for me. So that’s the timestamp I’ll publish with the block.

    You can remove timestamp from the equation but there’s usually some thing a miner could fiddle with to extract value.

    One suggestion is to replace timestamp with block number. The winner is still 100% predictable by the publisher, but they’d have to get more creative with what they manipulate in the block. Like conjuring up some transactions right before the lottery that affect its outcome.

    I’m a noob still but that’s my understanding. Would love to hear from an expert to know if I’m on the right track.

  2. You’re gonna get frontrun, people can just make a bot to model burning their ticket(s). If successful (win) continue. Otherwise revert (or don’t submit the transaction) and retry next block

  3. Check out the great discussion in the recent Meetbits exploit for a good explanation of why your method for obtaining randomness isn’t secure

    &#x200B;

    [https://forum.openzeppelin.com/t/understanding-the-meebits-exploit/8281/3](https://forum.openzeppelin.com/t/understanding-the-meebits-exploit/8281/3)

    The best current way to get verifiable and secure randomness is to use a VRF function like Chainlink VRF

Foundation Devices —Zach Herbert’s Crypto Con Artist Team Creeps into Bitcoin | by Fiach_Dubh | Coinmonks

The BEST Palantir Analysis Around!