Ethereum Lottery Example Part 2: Participant Aggregation and Winner Selection

Part 1 of this series: Ethereum Lottery Example Part 1: Setting Up Environment

Develop Functionality to Randomly Select a Winner

Before we jump into handling the monetary or scheduling part of things, let’s break this project down by first implementing and manually testing a contract that is able to aggregate a list of lottery entrants, and then randomly select a winner.

Random Number Selection

Solidarity does not provide a random number generator.

We cannot use recent transaction hashes to generate a random number, as there is risk of interference by miners. This blog article explains the concepts in-depth and proposes an alternative solution to using a third-party oracle.

Using an Oracle

We will use an outside Oracle, Provable, for generating a random number. To put it simply, oracles allow blockchain apps to access external APIs.

We chose to use Provable for this example because it will support our application development for free both within the Javascript VM and on the Ropsten test network.

To ease development, Provable doesn’t charge a contract for its first request of data done using the default gas parameters. Successive requests will require the contract to pay the Provable fee and the ether necessary to pay for the callback transaction. Both are automatically taken from the contract balance. If the contract doesn’t have enough funds in his balance, the request will fail and Provable won’t return any data.

https://docs.provable.xyz/#ethereum-quick-start

This works for us, because we only need our contract to select the winner once, at the conclusion of the lottery.

Step 1: Active Provable Plugin for Remix IDE

We need to activate the Provable Plugin Module within Remix IDE. This will allow us to use the Provable API within our Javascript VM deployment environment.

Review Part 1 of this series if you’re not sure how to activate a plugin module in Remix IDE.

Step 2: Write and Test Solidarity Code

pragma solidity >= 0.5.0 < 0.6.0;
import "github.com/provable-things/ethereum-api/provableAPI_0.5.sol";

contract Lotto is usingProvable {
    address[] public entrants;
    address public winner;
    bytes32 provableQueryId;
    
    function enter() public {
        entrants.push(msg.sender);
    }
    
    function selectWinner() public {
        if(winnerHasNotBeenSet() && provableQueryHasNotRun()){
            provableQueryId = provable_query("WolframAlpha", constructProvableQuery());
        }
    }
    
    function winnerHasNotBeenSet() private view returns (bool){
        return winner == address(0);
    }
    
    function provableQueryHasNotRun() private view returns (bool){
        return provableQueryId == 0;
    }
    
    function constructProvableQuery() private view returns (string memory){
        return strConcat("random number between 0 and ", uint2str(entrants.length-1));
    }
    
    //provable callback for selectWinner function
    function __callback(bytes32 myid, string memory result) public {
        if(myid != provableQueryId) revert();
        winner = entrants[parseInt(result)];
    }
}

Here is a basic solidity contract that allows us to enter into the lottery, and then later, randomly select a winner.

To select a winner, we use our Provable Integration to generate a random number, corresponding to our winner.

Using WolframAlpha as a datasource to generate secure random numbers is not good practice in production. If a programmer was to convert this proof of concept to production ready code, they would want to substitute a different Provable data source and validate the result.

There are missing features here. Features such as automating the conclusion of the lottery, and actually collecting from and distributing money to participants have not been implemented. We will add more functionality in future articles.

Step 3: Test Locally

If you have trouble deploying locally, please review Part 1: Step 3 of this series. If you’re not sure how to interact with the contract, move on to Step 4 of this article and watch the video.

Remember: We activated the Provable Plugin Module for Remix IDE in Step 1. Clicking the provable tab will allow you to see debug information for your program’s interaction with the provable API.

Figure 1: Using Provable interface in Remix IDE

Step 4: Test on Testnet

  1. Install Metamask for your browser (create a new wallet)
  2. In the upper right, click the “My Accounts” circle and select “Create Account” to create a second test account
  3. Switch to Ropsten Test Network
  4. Using the Metamask browser plugin, switch between your two accounts on the ether faucet and so that both test accounts have a positive Ether balance on the Ropsten Test Network
  5. Return to the Remix IDE instance, and refresh the page to cause it to reload
  6. Navigate to the Deploy & Run Transactions tab in the Remix IDE instance
  7. Select Injected Web3 as your environment.
  8. When prompted, connect both your test accounts in the Metamask dialog
  9. Select your contract (Lotto.sol)
  10. Click the “Deploy” button
  11. You can now interact with the contract!

Notes on interacting with the contract in testnet:

  1. Change your account in Metamask, Remix will only display the active account
  2. the selectWinner function may take a 10-15 seconds for the callback to be called, just be patient
https://youtu.be/n8KSSXhLZmo
Video 1: Interacting with contract on Ethereum Ropsten Test Network (in real-time, it’s a slow video). In the video, I show how the entrants array is populated and a winner is selected.

Follow-Up

Now that we have basic functionality working, there’s a lot of enhancements we need to add before we have a working lottery. I will be taking these on in future articles. This is not an all-inclusive list.

  • Add functionality to collect and distribute Ether payments
  • Use a scheduling service to automatically invoke the winner selection functionality
  • Only allow certain addresses to invoke the winner selection functionality
  • Prevent entering the lottery after a winner is selected
  • Add testing

Programmer’s Note

I am writing this article series as a way to better learn Blockchain programming, and it should not be considered as a source of “best practice”. I’m still learning.

If you spot any issues or have any suggestions, please leave a comment or send me an email.

Resources

Legal Disclaimer

This tutorial was intended to be used as an educational demonstration of Ethereum and related technologies. We picked a lottery application for our example because we feel it is a game most of our readers will be familiar with while being simple to implement.

Do not run this code in any production settings without contacting a lawyer first for advice. Lotteries are typically regulated in all jurisdictions.

There are no implied guarantees about the quality of this example. Code samples in this article are provided on an as-is basis and could (likely) contain bugs.

Update 9/6/21

  1. Updated code sample to reflect new location of provable import and specify version
  2. Ropsten test network is too slow to use apparently. I was able to run this application successfully on the Korvan testnet

Leave a Reply

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