How the 721Starter Raffle System Works
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
- Collecting
SOS Power
of participants. On chain data from the Ethereum blocks are saved. - 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) - Build Merkle Tree for participants by the following steps.
- Sort participants by weight in descending order with the wallet address and weight.
- 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
, amounts
and 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 index1
occupied slot 0~199address 2
with index2
occupied slot 200~299address 3
with index3
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.