Appearance
Tutorial: verifiable tournament seeding
Source: verity-examples/contracts/src/TournamentSeeding.sol.
The organizer registers up to 128 participants and requests a seeding. One random word drives a Fisher–Yates shuffle, and anyone can recompute the bracket from the public random word.
Freeze, then request
solidity
function requestSeeding() external onlyOwner returns (uint256 requestId) {
frozen = true; // no more entrants
requestId = s_coordinator.requestRandomWords(keyHash, subId, confirmations, 60_000, 1);
}Store the word, derive in a view
The callback stores only the random word, at a fixed cost of ~25k gas whatever the field size:
solidity
function fulfillRandomWords(uint256 requestId, uint256[] memory words) internal override {
if (requestId != seedingRequestId || seeded) return;
seeded = true;
randomWord = words[0];
}
function seeding() external view returns (string[] memory) {
return shuffle(s_participants, randomWord); // computed on read
}Our first version wrote the shuffled list into storage inside the callback. With 128 entrants that needs millions of gas, more than any sensible callback limit. A callback that runs out of gas is final, and the tournament would have no seeding. Keeping the callback tiny avoids that whole class of failure.
The shuffle
solidity
for (uint256 i = list.length - 1; i > 0; i--) {
uint256 j = uint256(keccak256(abi.encode(word, i))) % (i + 1);
(list[i], list[j]) = (list[j], list[i]);
}Each step draws from keccak256(word, i), so one 256-bit word covers any number of participants. shuffle is public pure: paste the word from the Seeded event and verify the bracket yourself.