How the 721Starter Raffle System Works

The OpenDAO
3 min readFeb 25, 2022

--

Art by BobShiby#4618

We have previously shared how SOS Power is evaluated in this article.

Today we will share how the raffle system guarantees fairness and randomness.

Fairness

721Starter uses Chainlink’s VRF technology in the raffle procedure and use Merkle Tree to make the result provable.

Raffle Procedure

This is how it looks like beneath the hood. There are off chain and on chain steps.

Steps

  1. Collecting SOS Power of participants. On chain data from the Ethereum blocks are saved.
  2. Participant’s weight in the raffle is calculated taking SOS Power * Booster. (Remember how the previous article mentioned a 10% Booster if you own the Tier 1 genesis NFT)
  3. Build Merkle Tree for participants by the following steps.
  4. Sort participants by weight in descending order with the wallet address and weight.
  5. Generate leaf node by doing keccak256(index:uint32, wallet:address, weight:uint16)
// A typescript example using ethers.js to build MerkleTree leaves

function getLeaf(idx: number, wallet: string, weight: number) {
return Buffer.from(solidityKeccak256(
["uint32", "address", "uint16"],
[idx, wallet, weight]
).substring(2), "hex");
}

6. Build Merkle Tree with leaf nodes made by participants‘ list following below criteria:
- Sort pairs in ascending.
- Duplicate odd nodes, to make up for occurrances where there are odd number of participants.

7. Group the participants by weight and amount and sort by weight

[
{ "address": "<address 1>", "weight": 100, "index": 1 },
{ "address": "<address 2>", "weight": 200, "index": 2 },
{ "address": "<address 3>", "weight": 100, "index": 3 },
]

// Group into below arrays

{
"weights": [200, 100],
"amounts": [1, 2]
}

8. Use parameter weights, amountsand the Merkle Root of the tree built in the step b. to start the raffle with the help of this smart contract.

9. When Chainlink Oracle fulfills the randomness of the the raffle, a random seed from Chainlink is assigned for this Merkle root.

10. GetWinners will be used to get the winner list based on the randomness assigned to this Merkle root.

How does GetWinners works?

A raffle project in the contract is stored as below structure.

struct Project {
bool raffleDone;
uint16 winnersToSelect;
uint232 randomSeed; // From Chainlink VRF service
uint16[] amounts;
uint16[] weights;
}

randomSeed is used to generate a sequence of random numbers. Each random number is mapping to a slot, which also represents a winner.

Taking below participants as an example.

[
{ "address": "<address 1>", "weight": 200, "index": 1 },
{ "address": "<address 2>", "weight": 100, "index": 2 },
{ "address": "<address 3>", "weight": 100, "index": 2 },
]
// Group into below arrays{
"weights": [200, 100],
"amounts": [1, 2]
}

amounts weights are placed into slots as below:

  • address 1 with index 1 occupied slot 0~199
  • address 2 with index 2 occupied slot 200~299
  • address 3 with index 3 occupied slot 300~399

If the random number sequence is [198,314], winners are <address 1> <address 2>.

If winners win again, the duplicate is ignored and consecutive random number is generated to get unique winners.

--

--