Building Randomness with Chainlink VRF: Part 2
Published: 2024-06-16 13:47:45


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r1q02ozne7erxiep23q1.png)


> “In the game of innovation, like in sports, every function plays a crucial role; it’s not just about having star players, but how every part works together seamlessly.”

## Overview

Welcome to the digital arena, where the players are smart contracts, and the game is as strategic as any sport you’ve ever seen. Today, we’re diving into the playbook (functions) of our MVP, RandomTeamSelector.sol, dissecting its moves and strategies to understand how it scores points in the blockchain league. If you missed [part 1, you can read it here.](https://charliej.hashnode.dev/building-randomness-with-chainlink-vrf-1)

Imagine a football coach picking players for a dream team, but instead of athletes, we’re drafting functions, each with a unique role that contributes to the victory of our smart contract. From the kickoff with `requestRandomTeamNames `to the final whistle with `chooseTeam`, we’ll explore how each function interacts, strategizes, and executes plays that would make even the most seasoned sports analysts take notes.

So, lace up your cleats, don your jerseys, and let’s get ready to rumble through the code, play by play, ensuring you understand the Xs and Os of our smart contract’s game plan.

## The Draft Pick: Initiating the Random Team Selection

In the grand league of smart contracts, the `requestRandomTeamNames `function is the first-round draft pick, setting the stage for a fair and unpredictable selection process. It’s the crucial coin toss, the unbiased referee, ensuring that every team manager gets a shot at glory in the blockchain championship.

**Here’s the play-by-play:**

Positioning the Players: The function takes an address manager as its input, which is akin to selecting the team manager who’s calling the shots.

Checking the Field: It checks if the manager has already made a team selection. If so, it throws a flag on the play with `RandomTeamSelector__AlreadySelected()`, stopping the action right there.

The Kickoff: If the coast is clear, it makes a call to `s_vrfCoordinator.requestRandomWords`, which is like passing the ball to the referee (the VRF Coordinator) to ensure a fair and random kickoff.

Setting the Strategy: The function sets up the request for random words with specific parameters like keyHash, subId, and callbackGasLimit, ensuring the play adheres to the game rules.

Executing the Play: Once the request is made, it records the requestId and associates it with the manager, marking the selection as ongoing with `SELECTION_ONGOING`.

Celebrating the Move: Finally, it announces the play to the spectators (other contracts and listeners) with the `SelectionMade `event.

Let’s break down each line of the requestRandomTeamNames function for clarity:

```
function requestRandomTeamNames(
    address manager
) public onlyOwner returns (uint256 requestId) {
```

This line declares the function `requestRandomTeamNames`, which takes one parameter: the address of the manager. It’s a public function, meaning it can be called by anyone, but with the `onlyOwner `modifier, it restricts access so only the contract owner can execute it. The function is set to return a uint256 variable named `requestId`.

    

```
if (s_managerSelections[manager].teamOptions.length > 0) {
        revert RandomTeamSelector__AlreadySelected();
    }
```

Here, we’re checking if the manager has already selected a team. If the `teamOptions `array for this manager is not empty (length > 0), the function will stop and revert the transaction with a custom error `RandomTeamSelector__AlreadySelected`.

    

```
requestId = s_vrfCoordinator.requestRandomWords(
```

This line assigns a new `requestId `by calling the `requestRandomWords `function from the `s_vrfCoordinator`, which is a reference to a Chainlink VRF Coordinator contract that provides secure and verifiable randomness. More specifically, you can see the `requestRandomWords `function directly in VRFCoordinatorV2.5.sol . It's in the lib folder

/lib/chainlink/contracts/src/v0.8/vrf/dev/VRFCoordinatorV2_5.sol

        

```
VRFV2PlusClient.RandomWordsRequest({
            keyHash: s_keyHash,
            subId: s_subscriptionId,
            requestConfirmations: requestConfirmations,
            callbackGasLimit: callbackGasLimit,
            numWords: numWords,
            extraArgs: VRFV2PlusClient._argsToBytes(
                VRFV2PlusClient.ExtraArgsV1({nativePayment: false})
            )
        })
    );
```

This section is constructing a `RandomWordsRequest `struct with various parameters:

`keyHash`: A unique identifier for the VRF key against which randomness is requested.

`subId`: The subscription ID for billing purposes.

`requestConfirmations`: The number of confirmations the VRF Coordinator requires before responding.

`callbackGasLimit`: The maximum amount of gas to use for the callback.

`numWords`: The number of random words requested.

`extraArgs`: Additional arguments, here converted to bytes, indicating no native token payment is required with this request.

    

```
s_requestToManager[requestId] = manager;
```

This line maps the `requestId `to the manager’s address, storing the association in a state variable.

    

```
s_managerSelections[manager].selectedTeam = SELECTION_ONGOING;
```

Here, the function sets the `selectedTeam `status to `SELECTION_ONGOING` for the manager, indicating that the selection process is currently underway.

    

```
emit SelectionMade(requestId, manager);
}
```

Finally, the function emits an event `SelectionMade `with the `requestId `and manager’s address, signaling to the blockchain that a selection process has been initiated.

This function sets up the initial request for randomness, which is the first step in the process of selecting a random team. It ensures that the selection is fair and hasn’t been tampered with by using a verifiable source of randomness.

## The Playmaker: fulfillRandomWords Function

In the playbook of our smart contract, the `fulfillRandomWords `function is the playmaker, the one who receives the pass (randomness) and makes the strategic moves (assigns team options). Here’s how it orchestrates the play:

**Receiving the Pass**: The function is triggered by a callback from the VRF Coordinator, carrying the `requestId `and an array of `randomWords`—the unpredictable elements that keep the game fair.

**Setting Up the Play**: It retrieves the manager’s address associated with the `requestId `and prepares an array `teamOptions `to hold the potential team picks.

**Executing the Strategy**: A loop runs through the `randomWords`, using modulo arithmetic to ensure the team options fall within a specified range (in this case, 1 to 40), representing the pool of available teams.

**Recording the Stats**: The manager’s selection is updated with these new team options, and the `selectedTeam `status is set to SELECTION_ONGOING, indicating the play is in progress.

**Announcing the Outcome**: Finally, the `SelectionRevealed `event is emitted, broadcasting the manager’s team options to all participants.

This function is the core of the selection process, taking the randomness provided by the VRF and translating it into actionable choices for the manager. It’s the moment when the crowd holds its breath as the playmaker weaves through the opposition, setting up for a score.

Let's take a deeper look at the code.

```
function fulfillRandomWords(
    uint256 requestId,
    uint256[] calldata randomWords
) internal override {
```

This function is an internal callback that is called by the VRF Coordinator when randomness is delivered. It overrides a function from the inherited VRFConsumerBaseV2Plus contract. The parameters are the ID of the request for randomness and the array of random numbers provided by the VRF.

   

```
 address manager = s_requestToManager[requestId];
```

This line retrieves the manager’s address that is associated with the given `requestId `from a mapping called `s_requestToManager`.

    

```
uint256[] memory teamOptions = new uint256;
```

Here, a new temporary array called `teamOptions `is created in memory to store the team IDs that will be generated from the random words.

    

```
for (uint256 i = 0; i < numWords; i++) {
        teamOptions[i] = (randomWords[i] % 40) + 1;
    }
```

This loop goes through each of the random numbers provided, uses modulo operation to ensure each number is within the range of 1 to 40, and assigns the result to the `teamOptions `array.

   

```
 s_managerSelections[manager] = ManagerSelection({
        teamOptions: teamOptions,
        selectedTeam: SELECTION_ONGOING
    });

```
The `s_managerSelections `mapping is updated for the manager with a new `ManagerSelection `struct, which includes the `teamOptions `array and sets the `selectedTeam `status to `SELECTION_ONGOING`.

    

```
emit SelectionRevealed(requestId, teamOptions);
}
```

Finally, the `SelectionRevealed `event is emitted, which includes the `requestId `and the `teamOptions` array, indicating that the selection options have been successfully generated and revealed.

Let’s proceed with the next function, teamName, which retrieves the name of the team chosen by a player.

## The Selector: teamName Function

In the arena of our smart contract, the `teamName `function is like the referee announcing the chosen player to the eager crowd. It’s the moment of truth where the selection is revealed and the anticipation turns into reality.

**Play by Play:**

**The Announcement**: This function is called upon to reveal the chosen team’s name, much like a referee would announce the selected player.

**The Verification**: Before the reveal, it ensures that a selection has indeed been made, akin to checking the roster before making the call.

**The Reveal**: Once confirmed, it retrieves and announces the team’s name, finalizing the player’s choice in the draft.

Line by Line Code Breakdown:

```
function teamName(address player) public view returns (string memory) {
```

This line declares the function `teamName`, which is a public function that anyone can call. It’s a view function, meaning it doesn’t modify the state on the blockchain. It takes one parameter, the address of the player, and returns a string representing the name of the team.

    

```
ManagerSelection storage selection = s_managerSelections[player];
```

Here, the function retrieves the `ManagerSelection `struct from the `s_managerSelections `mapping for the given player address and stores it in a local variable selection.

    

```
if (selection.selectedTeam == 0) {
        revert RandomTeamSelector__SelectionNotMade();
    }

```
This line checks if the `selectedTeam `property of the selection is 0, which would indicate that no selection has been made. If that’s the case, it reverts the transaction with a custom error `RandomTeamSelector__SelectionNotMade`.

    

```
return getTeamName(selection.selectedTeam);
}
```

Finally, the function returns the name of the selected team by calling another function `getTeamName `and passing the `selectedTeam `ID from the selection.

Moving on to the next function, let’s explore `getTeamOptions`, which retrieves all the team options available for a manager.

## The Strategist: getTeamOptions Function

In the tactical game of our smart contract, the `getTeamOptions `function is akin to a coach reviewing the playbook. It’s where the manager surveys all possible moves before making the crucial decision.

**Play by Play:**

**Surveying the Field**: This function allows the manager to look at all the potential team options at their disposal.

**Ensuring Fair Play**: It checks to make sure the selection process is still ongoing, akin to verifying that the game hasn’t ended before making a move.

**Revealing the Options**: Once confirmed, it presents the array of team IDs, laying out all the possible strategies for the manager to consider.

Line by Line Code Breakdown:

```
function getTeamOptions(
    address manager
) public view returns (uint256[] memory) {
```

This line declares the function `getTeamOptions`, which is publicly accessible and doesn’t alter the state on the blockchain. It takes the manager’s address as a parameter and returns an array of unsigned integers, which represent the team IDs.

    

```
ManagerSelection storage selection = s_managerSelections[manager];
```

Here, the function retrieves the `ManagerSelection `struct associated with the manager’s address from the `s_managerSelections `mapping.

    

```
if (selection.selectedTeam != SELECTION_ONGOING) {
        revert RandomTeamSelector__NoSelectionOptionsAvailable();
    }
```

This line checks if the selection process is not ongoing (meaning either not started or already completed). If so, it reverts the transaction with the error `RandomTeamSelector__NoSelectionOptionsAvailable`.

   

```
 return selection.teamOptions;
}
```

Finally, the function returns the `teamOptions `array from the selection, which contains the IDs of the available teams.

Let’s move on to the `chooseTeam `function, which allows a manager to finalize their team selection.

## The Decision Maker: chooseTeam Function

In the strategic game of our smart contract, the chooseTeam function is like the final whistle that seals the fate of the match. It’s where the manager makes the definitive call, locking in their team choice.

**Play by Play:**

The Final Call: This function is the decisive moment where the manager selects their team from the available options.

**Ensuring the Rules Are Followed**: It verifies that the selection process is ongoing and that the chosen team is a valid option.

**Locking in the Choice**: Once the checks are passed, it finalizes the manager’s team selection, much like a coach submitting the final lineup.

Line by Line Code Breakdown:

```
function chooseTeam(uint256 teamId) public {
```

This line declares the function `chooseTeam`, which is a public function that allows managers to choose a team. It takes one parameter, `teamId`, which is the ID of the team being chosen.

    

```
ManagerSelection storage selection = s_managerSelections[msg.sender];
```

Here, the function retrieves the `ManagerSelection `struct for the manager calling the function (indicated by `msg.sender`) from the `s_managerSelections `mapping.

   

```
 require(
        selection.selectedTeam == SELECTION_ONGOING,
        "Selection process not ongoing or already completed."
    );
```

This line checks that the selection process is currently ongoing for the manager. If not, it will revert the transaction with an error message.

    

```
bool validChoice = false;
    for (uint256 i = 0; i < selection.teamOptions.length; i++) {
        if (selection.teamOptions[i] == teamId) {
            validChoice = true;
            break;
        }
    }
```

The function then iterates through the `teamOptions `array to check if the `teamId `provided is a valid option. If it finds a match, it sets `validChoice `to true.

    

```
if (!validChoice) {
        revert RandomTeamSelector__InvalidTeamChoice();
    }
```

If no valid option is found (`validChoice `remains false), the function reverts the transaction with the error `RandomTeamSelector__InvalidTeamChoice`.

    

```
selection.selectedTeam = teamId;
    emit TeamChosen(msg.sender, teamId);
}
```

Once a valid choice is confirmed, the function sets the `selectedTeam `to the chosen `teamId `and emits the `TeamChosen `event, indicating that the manager has made their selection.

## Smart Contract Function Flow Summary

The `RandomTeamSelector.sol` smart contract orchestrates a series of functions to manage the process of random team selection. Here’s a summary of the flow:

**Initialization**: The contract begins with the `requestRandomTeamNames `function, where the owner initiates the random selection process for a manager. This function checks if the manager has already made a selection and, if not, requests randomness from the VRF Coordinator.

**Randomness Fulfillment**: Upon receiving randomness from the VRF Coordinator, the `fulfillRandomWords `callback function is triggered. It processes the random words to generate team options and updates the manager’s selection status to ongoing.

**Team Selection**: Managers can then call the `teamName `function to retrieve the name of the team that has been selected for them, provided that the selection has been made.

**Reviewing Options**: Before making a selection, managers can review their available team options using the `getTeamOptions `function, which returns an array of team IDs that are still in the running.

**Finalizing Selection**: Finally, the `chooseTeam `function allows managers to select their team from the provided options. It ensures the choice is valid and updates the manager’s selection, concluding the process.

Each function plays a critical role in ensuring the integrity and fairness of the random team selection process within the smart contract environment. The functions work together to manage the selection from initiation to finalization, providing transparency and control to the contract owner and participating managers.

And this is where I stand right now on the project. I know that I'll have to do some refactoring with some of these functions, but I'll likely leave that until after I write some tests and possibly after some interaction with a front-end.

[The code lives here.](https://github.com/SupaMega24/fantasy-team-vrf)