From 8eb3e746350c4776f210ba75ecf63b77bf0d59b5 Mon Sep 17 00:00:00 2001 From: pappas999 Date: Thu, 26 May 2022 12:21:32 +0930 Subject: [PATCH 1/4] update to VRFV2 --- en/19/07.md | 31 ++++++----- en/19/08.md | 76 ++++++++++++++++++--------- en/19/09.md | 73 +++++++++++++++----------- en/19/10.md | 145 +++++++++++++++++++++++++++++++++------------------- en/19/11.md | 116 ++++++++++++++++++++++++++--------------- 5 files changed, 279 insertions(+), 162 deletions(-) diff --git a/en/19/07.md b/en/19/07.md index fced904e9a..84422b59d9 100644 --- a/en/19/07.md +++ b/en/19/07.md @@ -6,9 +6,10 @@ material: editor: language: sol startingCode: | - pragma solidity ^0.6.6; + pragma solidity ^0.8.7; - // 1. Import the "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol" contract + // 1. Import the "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol" contract + // 2. Import the "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol" contract contract ZombieFactory { @@ -33,9 +34,10 @@ material: } answer: > - pragma solidity ^0.6.6; + pragma solidity ^0.8.7; - import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol"; + import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; + import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"; contract ZombieFactory { @@ -91,7 +93,7 @@ Let's take a look back at our good friend the zombie code, where we gave our zom Chainlink VRF is a way to get randomness from outside the blockchain, but in a proven cryptographic manner. This is important because we always want our logic to be truly incorruptible. Another naive attempt at getting randomness outside the blockchain would be to use an off-chain API call to a service that returns a random number. But if that services goes down, is bribed, hacked, or otherwise, you could potentially be getting back a corrupt random number. Chainlink VRF includes on-chain verification contracts that cryptographically prove that the random number the contract is getting is really random. -Chainlink VRF (Verifiable Randomness Function) +Chainlink VRF (Verifiable Randomness Function) ### Basic Request Model @@ -112,9 +114,13 @@ To recap, the process is as such: 3. In a second transaction created by the Chainlink node, it returns the data on-chain by calling a function described by the callee contract 4. In the case of the Chainlink VRF, a randomness proof is done to ensure the number is truly random -Now, similar to when we make a transaction on Ethereum or any Solidity compatible blockchain, as you know, we have to pay some transaction gas. To work with oracles, we have to pay a little bit of oracle gas, also known as the LINK or Chainlink token. The LINK token is specifically designed to work with oracles and ensure the security of the Chainlink oracle networks. Whenever we make a request following the basic request model, our contracts must be funded with a set amount of LINK, as defined by the specific oracle service that we are using (each service has different oracle gas fees). +Now, similar to when we make a transaction on Ethereum or any Solidity compatible blockchain, as you know, we have to pay some transaction gas. To work with oracles, we have to pay a little bit of oracle gas, also known as the LINK or Chainlink token. The LINK token is specifically designed to work with oracles and ensure the security of the Chainlink oracle networks. With the latest version of VRF, the payment of LINK for getting randomness is done using the Subscription Manager. -Chainlink Basic Oracle Request Model +The Subscription Manager can be thought of as the one-stop-shop for funding your use of Chainlink decentralized services from a single location, so you don't have to manage multiple wallets across several different systems and applications. It lets you create an account and pre-pay for using VRF in your smart contracts, so you don't have to provide funding each time your application requests randomness. The concept is very similar to a public transport pass that you 'load up' with credit, then you can use the pass on any form of transport whenever you like. With the Subscription Manager and VRF, you 'load up' your subscription account with LINK, then anytime you want to use a decentralized service like VRF, it automatically subtracts the required amount of LINK from your account. + + + +Chainlink Basic Oracle Request Model ### Why didn't we do this with the data feeds? @@ -124,20 +130,21 @@ In summary, someone else followed a bit more advanced version of the basic reque ### Chainlink VRF Under the Hood -The Chainlink VRF follows this basic request model, with one added benefit; since there is a cryptographic proof on-chain of the randomness of the number from a Chainlink VRF node, we are safe to work with a single Chainlink VRF node! As the technology gets better and better though, even more decentralized versions of the Chainlink VRF are being created, but luckily, we are good to work with this secure method of randomness for our smart contracts. +Chainlink VRF follows a version of this basic request model using a subscription manager, with one added benefit; since there is a cryptographic proof on-chain of the randomness of the number from a Chainlink VRF node, we are safe to work with a single Chainlink VRF node! As the technology gets better and better though, even more decentralized versions of the Chainlink VRF are being created, but luckily, we are good to work with this secure method of randomness for our smart contracts. We aren't going to go deep into the proofs the many researchers have done to ensure the randomness returned by Chainlink VRF nodes, but here is the basically what's happening for this magic to occur. In brief, a smart contract requests randomness by specifying a hash used to uniquely identify a Chainlink oracle. That hash is used by the Chainlink node with its own secret key to generate a random number, which is then returned to the contract on-chain, along with a cryptographic proof. An on-chain contract (called the `VRF Coordinator`) takes the random number along with the proof, and is verified using the oracle’s public key. Relying on the widely accepted signature and proof verification capabilities of a blockchain, this enables contracts to consume only randomness that has also been verified by the same on-chain environment running the contract itself. -You can check out the Chainlink VRF contracts to see the exact functions the system is using. +You can check out the Chainlink VRF contracts to see the exact functions the system is using. -Wow, OK, there is a lot of big brained concepts here! Let's finally dive into learning how to pull a random number into our smart contract. We can get started, by once again pulling the Chainlink VRF contract code from NPM / Github that allows us to interact with a Chainlink VRF node. We are going to `inherit` the functionality of the `VRFConsumerbase` contract code into our code to emit events, and define what functions the Chainlink node is going to callback (respond) to. +Wow, OK, there is a lot of big brained concepts here! Let's finally dive into learning how to pull a random number into our smart contract. We can get started, by once again pulling the Chainlink VRF contract code from NPM / Github that allows us to interact with a Chainlink VRF node. We are going to `inherit` the functionality of the `VRFConsumerbaseV2` contract code into our code to emit events, and define what functions the Chainlink node is going to callback (respond) to. We will then also inherit the functionality of the `VRFCoordinatorV2Interface` contract code into our code so we can interact with the VRF Coordinator contract and the Subscription Manager. ## Putting it to the test -1. Import the `VRFConsumerBase.sol` Solidity version `0.6.6` from the Chainlink NPM / GitHub. +1. Import the `VRFCoordinatorV2Interface.sol` Solidity version `0.8` from the Chainlink NPM / GitHub. +2. Import the `VRFConsumerBaseV2.sol` Solidity version `0.8` from the Chainlink NPM / GitHub. -> The `VRFConsumerBase.sol` includes all the functions we need to interact with a Chainlink VRF Coordinator and node. You can always find examples in the Chainlink Documentation. +> The `VRFConsumerBase.sol` and `VRFCoordinatorV2Interface` contracts include all the functions we need to interact with a Chainlink VRF Coordinator, Chainlink node and the Chainlink Subscription Manager. You can always find examples in the Chainlink Documentation. diff --git a/en/19/08.md b/en/19/08.md index 7b5d74811b..c57aa3d53c 100644 --- a/en/19/08.md +++ b/en/19/08.md @@ -6,11 +6,14 @@ material: editor: language: sol startingCode: | - pragma solidity ^0.6.6; - import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol"; + pragma solidity ^0.8.7; - // 1. Have our `ZombieFactory` contract inherit from the `VRFConsumerBase` contract - contract ZombieFactory { + import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; + import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"; + + contract ZombieFactory /*inherit functionality of VRFConsumerBaseV2*/ { + + //create all the new variables required by Chainlink VRF uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; @@ -22,7 +25,7 @@ material: Zombie[] public zombies; - // 2. Create a constructor + //create constructor function _createZombie(string memory _name, uint _dna) private { zombies.push(Zombie(_name, _dna)); @@ -35,11 +38,22 @@ material: } answer: > - pragma solidity ^0.6.6; + pragma solidity ^0.8.7; - import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol"; + import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; + import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"; - contract ZombieFactory is VRFConsumerBase { + contract ZombieFactory is VRFConsumerBaseV2 { + + VRFCoordinatorV2Interface COORDINATOR; + uint64 s_subscriptionId; + address vrfCoordinator = 0x6168499c0cFfCaCD319c818142124B7A15E857ab; + bytes32 keyHash = 0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc; + uint32 callbackGasLimit = 100000; + uint16 requestConfirmations = 3; + uint32 numWords = 1; + uint256 public s_requestId; + address s_owner; uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; @@ -51,10 +65,7 @@ material: Zombie[] public zombies; - constructor() VRFConsumerBase( - 0x6168499c0cFfCaCD319c818142124B7A15E857ab, // VRF Coordinator - 0x01BE23585060835E02B77ef475b0Cc51aA1e0709 // LINK Token - ) public{ + constructor(uint64 subscriptionId) VRFConsumerBaseV2(vrfCoordinator) { } @@ -71,20 +82,26 @@ material: --- -You got it! The `VRFConsumerBase` contract includes all the code we need to send a request to a Chainlink oracle, including all the event logging code. +You got it! The `VRFConsumerBaseV2` and `VRFCoordinatorV2Interface` contracts includes all the code we need to send a randomness request to a Chainlink oracle. -Now, as we said, to interact with a Chainlink node, we need to know a few variables. +Now, as we said, to interact with a Chainlink node to request randomness, we need to know a few variables: -- The address of the Chainlink token contract. This is needed so our contract can tell if we have enough `LINK` tokens to pay for the gas. +- The subscription ID that this contract belongs to. This is so the Subscription Manager knows which account to for payment of LINK for each request - The VRF coordinator contract address. This is needed to verify that the number we get is actually random. - The Chainlink node keyhash. This is used identify which Chainlink node we want to work with. - The Chainlink node fee. This represents the fee (gas) the Chainlink will charge us, expressed in `LINK` tokens. +- The Callback Gas Limit. This determines the gas limit you want to specify for the callback transaction that writes the result on-chain. +- The number of requeset confirmations. This determines how many confirmations to wait before assuming the result is valid. +- The number of random numbers to receive back, because you can request multiple random numbers. + -You can find all these variables in the Chainlink VRF Contract addresses documentation page. Once again, the addresses will be different across networks, but for the scope of this lesson we will again be working with the Rinkeby network. +You can find some these variables in the Chainlink VRF Contract addresses documentation page. Once again, the addresses will be different across networks, but for the scope of this lesson we will again be working with the Rinkeby network. For the ones that aren't found in the Chainlink docs, you need to decide and set them yourself. +As you look at them all, you might ask 'where do I get the subscription ID from'? The answer is, you need to go to the Chainlink Subscription Manager, and create a new subscription that you can use. Once you've created one, you'll be given a subscription ID. This is the number you would put in your smart contract as the subscription ID. You don't need to do this step now for this example as we're just focusing on the smart contract code, but just be aware of it for when you try Chainlink VRF on a live public network. -As said in the last lesson, we are going to inherit the functionality of this VRFConsumerBase. But how do we implement a constructor of an inherited contract? And the answer is that we can have a constructor in a constructor. + +Speaking of our smart contract, as said in the last lesson, we are going to inherit the functionality of this `VRFConsumerBaseV2`. But how do we implement a constructor of an inherited contract? And the answer is that we can have a constructor in a constructor. Let's take a look at this sample code: @@ -98,20 +115,17 @@ contract X is Y { To use a constructor of an inherited contract, we just put the constructor declaration as part of our contract's constructor. -We can do the same thing with the `VRFConsumerBase` contract: +We can do the same thing with the `VRFConsumerBaseV2` contract: ```javascript -constructor() VRFConsumerBase( - 0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B, // VRF Coordinator - 0x01BE23585060835E02B77ef475b0Cc51aA1e0709 // LINK Token -) public{ +constructor() VRFConsumerBaseV2(vrfCoordinator) { } ``` ## Putting it to the test -1. Have our `ZombieFactory` contract inherit from the `VRFConsumerBase` contract. If you don't remember the syntax, here's an example: +1. Have our `ZombieFactory` contract inherit from the `VRFConsumerBaseV2` contract. If you don't remember the syntax, here's an example: ```javascript // Create a contract named `parent` @@ -125,7 +139,21 @@ constructor() VRFConsumerBase( } ``` -2. Create a constructor for the `ZombieFactory` contract that calls the constructor of the `VRFConsumerBase` contract, passing with addresses of the VRF Coordinator and LINK token contract from the Rinkeby network as arguments. Leave the body of the constructor empty. You can copy and paste the addresses from the example above or get them from the Chainlink VRF Contract Addresses page of the Chainlink documentation. Again, if you don't remember the syntax, check the example we provided above. +2. Add the following variables in the ZombieFactory contract. They will be required for the call to VRF + - `COORDINATOR`, of type `VRFCoordinatorV2Interface` + - `s_subscriptionId`, a `uint64`. + - `vrfCoordinator`, an `address` hard-coded to the Rinkeby coordinator address of `0x6168499c0cFfCaCD319c818142124B7A15E857ab` + - `keyHash`, a `bytes32` hard-coded to the 30 gwei Key Hash on Rinkeby `0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc` + - `callbackGasLimit`, a `uint32` set to the value `100000` + - `requestConfirmations`, a `uint16` set to the value `3` + - `numWords`, a `uint32` set to the value `1` + - `s_requestId`, a public `uint256` + - `s_owner`, an `address` + + +3. Create a constructor for the `ZombieFactory` contract that takes a uint64 input parameter called '`subscriptionId`. The construction should call the constructor of the `VRFConsumerBaseV2` contract, passing with addresses of the VRF Coordinator from the Rinkeby network as an argument. Again, if you don't remember the syntax, check the example we provided above. Leave the body of the constructor blank for now. + + > Wondering where some of these values came from? We did all the legwork for you, and pulled them from the Chainlink VRF Contract Addresses page of the Chainlink documentation. diff --git a/en/19/09.md b/en/19/09.md index fcb498e4a0..c7794e7454 100644 --- a/en/19/09.md +++ b/en/19/09.md @@ -6,16 +6,27 @@ material: editor: language: sol startingCode: | - pragma solidity ^0.6.6; - import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol"; + pragma solidity ^0.8.7; - contract ZombieFactory is VRFConsumerBase { + import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; + import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"; + + contract ZombieFactory is VRFConsumerBaseV2 { + + VRFCoordinatorV2Interface COORDINATOR; + uint64 s_subscriptionId; + address vrfCoordinator = 0x6168499c0cFfCaCD319c818142124B7A15E857ab; + bytes32 keyHash = 0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc; + uint32 callbackGasLimit = 100000; + uint16 requestConfirmations = 3; + uint32 numWords = 1; + uint256 public s_requestId; + address s_owner; + //create new variable to store random numbers uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; - // 1. Define the `keyHash`, `fee`, and `randomResult` variables. Don't forget to make them `public`. - struct Zombie { string name; uint dna; @@ -23,11 +34,8 @@ material: Zombie[] public zombies; - constructor() VRFConsumerBase( - 0x6168499c0cFfCaCD319c818142124B7A15E857ab, // VRF Coordinator - 0x01BE23585060835E02B77ef475b0Cc51aA1e0709 // LINK Token - ) public{ - // 2. Fill in the body + constructor(uint64 subscriptionId) VRFConsumerBaseV2(vrfCoordinator) { + //complete the constructor body } function _createZombie(string memory _name, uint _dna) private { @@ -41,19 +49,27 @@ material: } answer: > - pragma solidity ^0.6.6; + pragma solidity ^0.8.7; + + import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; + import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"; - import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol"; + contract ZombieFactory is VRFConsumerBaseV2 { - contract ZombieFactory is VRFConsumerBase { + VRFCoordinatorV2Interface COORDINATOR; + uint64 s_subscriptionId; + address vrfCoordinator = 0x6168499c0cFfCaCD319c818142124B7A15E857ab; + bytes32 keyHash = 0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc; + uint32 callbackGasLimit = 100000; + uint16 requestConfirmations = 3; + uint32 numWords = 1; + uint256 public s_requestId; + address s_owner; + uint256[] public s_randomWords; uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; - bytes32 public keyHash; - uint256 public fee; - uint256 public randomResult; - struct Zombie { string name; uint dna; @@ -61,13 +77,10 @@ material: Zombie[] public zombies; - constructor() VRFConsumerBase( - 0x6168499c0cFfCaCD319c818142124B7A15E857ab, // VRF Coordinator - 0x01BE23585060835E02B77ef475b0Cc51aA1e0709 // LINK Token - ) public{ - keyHash = 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311; - fee = 100000000000000000; - + constructor(uint64 subscriptionId) VRFConsumerBaseV2(vrfCoordinator) { + COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); + s_owner = msg.sender; + s_subscriptionId = subscriptionId; } function _createZombie(string memory _name, uint _dna) private { @@ -82,16 +95,14 @@ material: } --- -Now this part is pretty easy, we just need to define our `keyHash` and `fee` in our constructor. We also should make a global variable called `randomResult` that will store the most recent return of a Chainlink VRF. +Now this next part is pretty easy, we just need to set our `coordinator`, `owner` and `subscription ID` in our constructor. We also should also create an array of integers called `s_randomWords` that will store the returned random numbers. ## Putting it to the test -1. Define three `public` variables: - - `keyHash`, a `bytes32` - - `fee`, a `uint256` - - `randomResult`, a `uint256` +1. Define a new `public` variable `s_randomWords`, an array of `uint256` + + +2. Fill in the body of the newly created constructor, by seting the COORDINATOR variable to the result of calling `VRFCoordinatorV2Interface(vrfCoordinator)`. Then set the values of s_owner to `msg.sender`, and s_subscriptionId to the `subscriptionId` input parameter. -2. Fill in the body of the newly created constructor, by setting the `keyHash` variable to `0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311`, and the `fee` to `100000000000000000` -> Wondering where these values came from? We did all the legwork for you, and pulled them from the Chainlink VRF Contract Addresses page of the Chainlink documentation. diff --git a/en/19/10.md b/en/19/10.md index 59c26c3f7e..67dd6cf185 100644 --- a/en/19/10.md +++ b/en/19/10.md @@ -1,23 +1,32 @@ --- -title: The requestRandomness and fulfillRandomness functions +title: The requestRandomWords and fulfillRandomWords functions actions: ['checkAnswer', 'hints'] requireLogin: true material: editor: language: sol startingCode: | - pragma solidity ^0.6.6; - import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol"; + pragma solidity ^0.8.7; - contract ZombieFactory is VRFConsumerBase { + import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; + import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"; + + contract ZombieFactory is VRFConsumerBaseV2 { + + VRFCoordinatorV2Interface COORDINATOR; + uint64 s_subscriptionId; + address vrfCoordinator = 0x6168499c0cFfCaCD319c818142124B7A15E857ab; + bytes32 keyHash = 0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc; + uint32 callbackGasLimit = 100000; + uint16 requestConfirmations = 3; + uint32 numWords = 1; + uint256 public s_requestId; + address s_owner; + uint256[] public s_randomWords; uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; - bytes32 public keyHash; - uint256 public fee; - uint256 public randomResult; - struct Zombie { string name; uint dna; @@ -25,23 +34,22 @@ material: Zombie[] public zombies; - constructor() VRFConsumerBase( - 0x6168499c0cFfCaCD319c818142124B7A15E857ab, // VRF Coordinator - 0x01BE23585060835E02B77ef475b0Cc51aA1e0709 // LINK Token - ) public{ - keyHash = 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311; - fee = 100000000000000000; - + constructor(uint64 subscriptionId) VRFConsumerBaseV2(vrfCoordinator) { + COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); + s_owner = msg.sender; + s_subscriptionId = subscriptionId; } + //create requestRandomWords function + + //create fulfillRandomWords function + + //create onlyOwner modifier + function _createZombie(string memory _name, uint _dna) private { zombies.push(Zombie(_name, _dna)); } - // 1. Create the `getRandomNumber` function - - // 2. Create the `fulfillRandomness` function - function _generatePseudoRandomDna(string memory _str) private view returns (uint) { uint rand = uint(keccak256(abi.encodePacked(_str))); return rand % dnaModulus; @@ -49,20 +57,27 @@ material: } answer: > - pragma solidity ^0.6.6; + pragma solidity ^0.8.7; - import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol"; + import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; + import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"; + contract ZombieFactory is VRFConsumerBaseV2 { - contract ZombieFactory is VRFConsumerBase { + VRFCoordinatorV2Interface COORDINATOR; + uint64 s_subscriptionId; + address vrfCoordinator = 0x6168499c0cFfCaCD319c818142124B7A15E857ab; + bytes32 keyHash = 0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc; + uint32 callbackGasLimit = 100000; + uint16 requestConfirmations = 3; + uint32 numWords = 1; + uint256 public s_requestId; + address s_owner; + uint256[] public s_randomWords; uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; - bytes32 public keyHash; - uint256 public fee; - uint256 public randomResult; - struct Zombie { string name; uint dna; @@ -70,27 +85,34 @@ material: Zombie[] public zombies; - constructor() VRFConsumerBase( - 0x6168499c0cFfCaCD319c818142124B7A15E857ab, // VRF Coordinator - 0x01BE23585060835E02B77ef475b0Cc51aA1e0709 // LINK Token - ) public{ - keyHash = 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311; - fee = 100000000000000000; - + constructor(uint64 subscriptionId) VRFConsumerBaseV2(vrfCoordinator) { + COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); + s_owner = msg.sender; + s_subscriptionId = subscriptionId; } - function _createZombie(string memory _name, uint _dna) private { - zombies.push(Zombie(_name, _dna)); + function requestRandomWords() external onlyOwner { + s_requestId = COORDINATOR.requestRandomWords( + keyHash, + s_subscriptionId, + requestConfirmations, + callbackGasLimit, + numWords + ); } - - function getRandomNumber() public returns (bytes32 requestId) { - return requestRandomness(keyHash, fee); + function fulfillRandomWords(uint256, uint256[] memory randomWords + ) internal override { + s_randomWords = randomWords; } + modifier onlyOwner() { + require(msg.sender == s_owner); + _; + } - function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override { - randomResult = randomness; + function _createZombie(string memory _name, uint _dna) private { + zombies.push(Zombie(_name, _dna)); } function _generatePseudoRandomDna(string memory _str) private view returns (uint) { @@ -104,7 +126,7 @@ material: Perfect! Now we can create our function that calls the Chainlink node. -Remember, the Chainlink VRF follows the basic request model, so we need to define: +Remember, Chainlink VRF follows the basic request model, so we need to define: 1. A function to request the random number @@ -112,27 +134,42 @@ Remember, the Chainlink VRF follows the basic request model, so we need to defin Remember, the Chainlink node is actually going to call the VRF Coordinator first to verify the number is random, then the VRF Coordinator will be the one to call our `ZombieFactory` contract. -Since we are importing the `VRFConsumerBase` contract, we can use the two built in functions that do both of these! +Since we are importing the `VRFConsumerBaseV2` contract, we can use the two built in functions that do both of these! + +A. `requestRandomWords` + 1. This function interacts with the VRFV2 coordinator contract, and tells it that it wants to make a request for randomness, passing in all the relevant details such as the keyhash, subscription ID, number of confirmations, callback gas limit, and how many random numbers to return. + 2. The result of this request is a request ID which is stored in the `s_requestId` variable. -A. `requestRandomness` - 1. This function checks to see that our contract has `LINK` tokens to pay a Chainlink node - 2. Then, it sends some `LINK` tokens to the Chainlink node - 3. Emits an event that the Chainlink node is looking for - 4. Assigns a `requestId` to our request for a random number on-chain +B. `fulfillRandomWords` + 1. The Chainlink node first calls a function on the VRF Coordinator and includes the random numbers. + 2. The VRF Coordinator verifies that the random number(s) are truly random. + 3. Then, it returns the random number(s) the Chainlink node created, along with the original requestID from our request to the smart contract that requested the randomness. -B. `fulfillRandomness` - 1. The Chainlink node first calls a function on the VRF Coordinator and includes a random number - 2. The VRF Coordinator checks to see if the number is random - 3. Then, it returns the random number the Chainlink node created, along with the original requestID from our request +C. We want to ensure only the contract owner can do a request for randomness, so we'll create a modifier `onlyOwner` to enforce this. -So, let's create these two functions in our contract. +So, let's create these functions in our contract. ## Putting it to the test -1. Create a function named `getRandomNumber`. It is a `public` function that returns a variable named `requestId` of type `bytes32`. The body of the function returns the value you get from calling the `requestRandomness` function, passing it two parameters: `keyHash` and `fee`. Let's do it in one line of code to keep things clean. For more details, see the see the Get a Random Number page of the Chainlink documentation. +1. Create a function named `requestRandomWords`. It is an `external` function with no return value. The function definition should also specify the `onlyOwner` modifier after the `external` keyword. The body of the function should set the value of `s_request_id` to the result of calling the `COORDINATOR.requestRandomWords()` function, passing in the following input paramters: + - keyHash, + - s_subscriptionId, + - requestConfirmations, + - callbackGasLimit, + - numWords +For more details, see the see the Get a Random Number page of the Chainlink documentation. -2. Create an `internal override` function named `fulfillRandomness` that takes two arguments: `requestId` of type `bytes32` and `randomness` of type `uint256`. This function assigns the response of the Chainlink (the `randomness` argument) node to the `randomResult` variable. +2. Create an `internal override` function named `fulfillRandomWords` that takes two arguments: the first should be of type `uint256` and not have a name specified (as we don't have any logic that uses it in the function body), and an array of uint256 called `randomWords`. The second parameter should be specifically stored in memory by specifying the `memory` keyword before the variable name. Finally, the function body should assign the response of the VRF request (the `randomWords` argument) to the `s_randomWords` variable. > Note: Why did we make this function `internal override`? That's because only the VRF Coordinator contract calls this function. + +3. Create a new modifier called `onlyOwner()`, that should run an assertion check to require that `(msg.sender == s_owner);`, then if the result is true, continue with function execution `_;`. This is the same as the modifier we created earlier in Chapter 3: + + ``` + modifier onlyOwner() { + require(msg.sender == owner); + _; + } + ``` \ No newline at end of file diff --git a/en/19/11.md b/en/19/11.md index bf0af45d6b..bf6dbf5a0f 100644 --- a/en/19/11.md +++ b/en/19/11.md @@ -6,18 +6,27 @@ material: editor: language: sol startingCode: | - pragma solidity ^0.6.6; - import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol"; + pragma solidity ^0.8.7; - contract ZombieFactory is VRFConsumerBase { + import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; + import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"; + + contract ZombieFactory is VRFConsumerBaseV2 { + + VRFCoordinatorV2Interface COORDINATOR; + uint64 s_subscriptionId; + address vrfCoordinator = 0x6168499c0cFfCaCD319c818142124B7A15E857ab; + bytes32 keyHash = 0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc; + uint32 callbackGasLimit = 100000; + uint16 requestConfirmations = 3; + uint32 numWords = 1; + uint256 public s_requestId; + address s_owner; + uint256[] public s_randomWords; uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; - bytes32 public keyHash; - uint256 public fee; - uint256 public randomResult; - struct Zombie { string name; uint dna; @@ -25,26 +34,34 @@ material: Zombie[] public zombies; - constructor() VRFConsumerBase( - 0x6168499c0cFfCaCD319c818142124B7A15E857ab, // VRF Coordinator - 0x01BE23585060835E02B77ef475b0Cc51aA1e0709 // LINK Token - ) public{ - keyHash = 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311; - fee = 100000000000000000; - + constructor(uint64 subscriptionId) VRFConsumerBaseV2(vrfCoordinator) { + COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); + s_owner = msg.sender; + s_subscriptionId = subscriptionId; } - function _createZombie(string memory _name, uint _dna) private { - zombies.push(Zombie(_name, _dna)); + function requestRandomWords() external onlyOwner { + s_requestId = COORDINATOR.requestRandomWords( + keyHash, + s_subscriptionId, + requestConfirmations, + callbackGasLimit, + numWords + ); } + function fulfillRandomWords(uint256, uint256[] memory randomWords + ) internal override { + s_randomWords = randomWords; + } - function getRandomNumber() public returns (bytes32 requestId) { - return requestRandomness(keyHash, fee); + modifier onlyOwner() { + require(msg.sender == s_owner); + _; } - function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override { - randomResult = randomness; + function _createZombie(string memory _name, uint _dna) private { + zombies.push(Zombie(_name, _dna)); } // Delete the function below @@ -55,19 +72,27 @@ material: } answer: > - pragma solidity ^0.6.6; + pragma solidity ^0.8.7; - import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol"; + import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; + import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"; - contract ZombieFactory is VRFConsumerBase { + contract ZombieFactory is VRFConsumerBaseV2 { + + VRFCoordinatorV2Interface COORDINATOR; + uint64 s_subscriptionId; + address vrfCoordinator = 0x6168499c0cFfCaCD319c818142124B7A15E857ab; + bytes32 keyHash = 0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc; + uint32 callbackGasLimit = 100000; + uint16 requestConfirmations = 3; + uint32 numWords = 1; + uint256 public s_requestId; + address s_owner; + uint256[] public s_randomWords; uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; - bytes32 public keyHash; - uint256 public fee; - uint256 public randomResult; - struct Zombie { string name; uint dna; @@ -75,32 +100,41 @@ material: Zombie[] public zombies; - constructor() VRFConsumerBase( - 0x6168499c0cFfCaCD319c818142124B7A15E857ab, // VRF Coordinator - 0x01BE23585060835E02B77ef475b0Cc51aA1e0709 // LINK Token - ) public{ - keyHash = 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311; - fee = 100000000000000000; - + constructor(uint64 subscriptionId) VRFConsumerBaseV2(vrfCoordinator) { + COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); + s_owner = msg.sender; + s_subscriptionId = subscriptionId; } - function _createZombie(string memory _name, uint _dna) private { - zombies.push(Zombie(_name, _dna)); + function requestRandomWords() external onlyOwner { + s_requestId = COORDINATOR.requestRandomWords( + keyHash, + s_subscriptionId, + requestConfirmations, + callbackGasLimit, + numWords + ); } + function fulfillRandomWords(uint256, uint256[] memory randomWords + ) internal override { + s_randomWords = randomWords; + } - function getRandomNumber() public returns (bytes32 requestId) { - return requestRandomness(keyHash, fee); + modifier onlyOwner() { + require(msg.sender == s_owner); + _; } - function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override { - randomResult = randomness; + function _createZombie(string memory _name, uint _dna) private { + zombies.push(Zombie(_name, _dna)); } + } --- -Boom! You've done it! Your contract now can get a random number! +Boom! You've done it! Your contract now can get a verifiable random number! Now let's delete the old pseudo-random number generator! We are using TRUE randomness now! From 2197934033f1ad374dfff82115672c68f79b4923 Mon Sep 17 00:00:00 2001 From: pappas999 Date: Tue, 31 May 2022 14:44:05 +0930 Subject: [PATCH 2/4] updated previous chapters to use 0.8 Solidity --- en/19/01.md | 4 ++-- en/19/02.md | 42 +++++++++++------------------------------- en/19/03.md | 10 +++++----- en/19/04.md | 8 ++++---- en/19/05.md | 12 ++++++------ en/19/06.md | 8 ++++---- 6 files changed, 32 insertions(+), 52 deletions(-) diff --git a/en/19/01.md b/en/19/01.md index 70a3d253fc..9a7d79d512 100644 --- a/en/19/01.md +++ b/en/19/01.md @@ -12,7 +12,7 @@ material: //2. Create the `PriceConsumerV3`contract answer: | - pragma solidity ^0.6.7; + pragma solidity ^0.8.7; contract PriceConsumerV3 { @@ -47,6 +47,6 @@ The first thing we want to do, is start our contract and import the Chainlink co Some of this should be pretty familiar to you, the only real different part is the implications! -1. In the box to the right, declare our `pragma` version at the top of the file to `^0.6.7`. +1. In the box to the right, declare our `pragma` version at the top of the file to `^0.8.7`. 2. Now, you would want to declare a new contract named `PriceConsumer`. The current version of the Chainlink aggregator interface is v3. However, as Chainlink adds more features to the interface, this version change over time. When you name your contract, let's append this version to the name itself for later reference. Declare a new contract named `PriceConsumerV3`, and leave the body of the contract empty for now. diff --git a/en/19/02.md b/en/19/02.md index 4874e44be3..167668b695 100644 --- a/en/19/02.md +++ b/en/19/02.md @@ -7,7 +7,7 @@ material: language: sol startingCode: "PriceConsumerV3.sol": | - pragma solidity ^0.6.7; + pragma solidity ^0.8.7; // Start here @@ -15,9 +15,9 @@ material: } answer: | - pragma solidity ^0.6.7; + pragma solidity ^0.8.7; - import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol"; + import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; contract PriceConsumerV3 { @@ -34,38 +34,19 @@ We want to import the `AggregatorV3Interface` from the GitHub repo for AggregatorV3Interface. +And we can find it in the GitHub repo for AggregatorV3Interface. We can either import directly from GitHub, or from NPM packages to get this contract. The framework you're using (like Truffle, Brownie, Remix, Hardhat) will determine whether or not to use GitHub or NPM packages, but the syntax will be approximately the same! ## Putting it to the Test -1. To work with the Chainlink Data Feeds contracts, import the `"@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol"` contract. +1. To work with the Chainlink Data Feeds contracts, import the `"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"` contract. -> NOTE: We've imported `v0.6` to match the version of Solidity we are using! +> NOTE: We've imported `v0.8` to match the version of Solidity we are using! diff --git a/en/19/03.md b/en/19/03.md index b8de8482d1..f8d69f4c7e 100644 --- a/en/19/03.md +++ b/en/19/03.md @@ -7,9 +7,9 @@ material: language: sol startingCode: "PriceConsumerV3.sol": | - pragma solidity ^0.6.7; + pragma solidity ^0.8.7; - import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol"; + import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; contract PriceConsumerV3 { @@ -21,9 +21,9 @@ material: } answer: | - pragma solidity ^0.6.7; + pragma solidity ^0.8.7; - import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol"; + import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; contract PriceConsumerV3 { AggregatorV3Interface public priceFeed; @@ -47,6 +47,6 @@ We will use the data feed of Rinkeby for this demo, you can find all the address 1. Create a `public` global variable (a variable outside any function) named `priceFeed` of type `AggregatorV3Interface`. 2. Create a `constructor`. -3. Instantiate the `AggregatorV3Interface` contract, passing it the Mainnet Ethereum `ETH/USD` contract address (`0x8A753747A1Fa494EC906cE90E9f37563A8AF630e`) as a parameter, and store the result in the `priceFeed` variable. +3. Instantiate the `AggregatorV3Interface` contract, passing it the Rinkeby Ethereum `ETH/USD` contract address (`0x8A753747A1Fa494EC906cE90E9f37563A8AF630e`) as a parameter, and store the result in the `priceFeed` variable. > Hint: To verify that you've specified the correct address of the `ETH/USD` data feed contract deployed to the Rinkeby network, you can check the Ethereum Data Feeds page of the Chainlink documentation. diff --git a/en/19/04.md b/en/19/04.md index b71f67083f..df935488ef 100644 --- a/en/19/04.md +++ b/en/19/04.md @@ -7,9 +7,9 @@ material: language: sol startingCode: "PriceConsumerV3.sol": | - pragma solidity ^0.6.7; + pragma solidity ^0.8.7; - import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol"; + import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; contract PriceConsumerV3 { AggregatorV3Interface public priceFeed; @@ -22,9 +22,9 @@ material: } answer: | - pragma solidity ^0.6.7; + pragma solidity ^0.8.7; - import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol"; + import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; contract PriceConsumerV3 { AggregatorV3Interface public priceFeed; diff --git a/en/19/05.md b/en/19/05.md index a2dfb03601..669706c858 100644 --- a/en/19/05.md +++ b/en/19/05.md @@ -7,9 +7,9 @@ material: language: sol startingCode: "PriceConsumerV3.sol": | - pragma solidity ^0.6.7; + pragma solidity ^0.8.7; - import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol"; + import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; contract PriceConsumerV3 { AggregatorV3Interface public priceFeed; @@ -27,9 +27,9 @@ material: } answer: | - pragma solidity ^0.6.7; + pragma solidity ^0.8.7; - import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol"; + import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; contract PriceConsumerV3 { AggregatorV3Interface public priceFeed; @@ -57,12 +57,12 @@ Now, as you know, decimals places don't work so well in Solidity, so what happen Well, to save you time, if we were to call this function, we'd get a response similar to this: ``` -310523971888 +200523971888 ``` Wait... is Ethereum really that expensive? -Well, maybe sometime in the distant future it might be, but at the time of writing, it's not even close. Right now, the price is about `$3,105.52`. +Well, maybe sometime in the distant future it might be, but at the time of writing, it's not even close. Right now, the price is about `$2000.00`. So how do we know where the decimal place goes? The answer is simple: there is a `decimals` function that tells us! diff --git a/en/19/06.md b/en/19/06.md index 068589905b..3e3e104024 100644 --- a/en/19/06.md +++ b/en/19/06.md @@ -7,9 +7,9 @@ material: language: sol startingCode: "PriceConsumerV3.sol": | - pragma solidity ^0.6.7; + pragma solidity ^0.8.7; - import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol"; + import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; contract PriceConsumerV3 { AggregatorV3Interface public priceFeed; @@ -29,9 +29,9 @@ material: } } answer: | - pragma solidity ^0.6.7; + pragma solidity ^0.8.7; - import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol"; + import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; contract PriceConsumerV3 { AggregatorV3Interface public priceFeed; From 8895aade456b79882e5e2a29c51a00cf7faa6998 Mon Sep 17 00:00:00 2001 From: pappas999 Date: Mon, 13 Jun 2022 11:53:50 +0930 Subject: [PATCH 3/4] incorporated feedback --- en/19/02.md | 2 +- en/19/04.md | 6 +++--- en/19/05.md | 6 +++--- en/19/08.md | 12 ++++++------ 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/en/19/02.md b/en/19/02.md index 167668b695..d566079632 100644 --- a/en/19/02.md +++ b/en/19/02.md @@ -9,7 +9,7 @@ material: "PriceConsumerV3.sol": | pragma solidity ^0.8.7; - // Start here + // 1. Import the AggregatorV3Interface contract here contract PriceConsumerV3 { diff --git a/en/19/04.md b/en/19/04.md index df935488ef..05c630a79d 100644 --- a/en/19/04.md +++ b/en/19/04.md @@ -18,7 +18,7 @@ material: priceFeed = AggregatorV3Interface(0x8A753747A1Fa494EC906cE90E9f37563A8AF630e); } - // Start here + // 1. Create getLatestPrice function here } answer: | @@ -95,6 +95,6 @@ This way, we can easily see what variables are important to us, and not declare 1. Create a `public view` function named `getLatestPrice`. The function returns an `int`. -2. Call the `latestRoundData` function of the`priceFeed` contract, and store only the `answer` in an `int` variable named `price`. Exclude any other variables from being declared in our function call. If you can't remember the syntax for doing this, check the example from above. But first, try to do it without peeking. +2. In the new `getLatestPrice` function, Call the `latestRoundData` function of the`priceFeed` contract, and store only the `answer` in an `int` variable named `price`. Exclude any other variables from being declared in our function call. If you can't remember the syntax for doing this, check the example from above. But first, try to do it without peeking. -3. Return the `price` variable. +3. On the next line of the `getLatestPrice` function body, return the `price` variable. diff --git a/en/19/05.md b/en/19/05.md index 669706c858..e000a9b77d 100644 --- a/en/19/05.md +++ b/en/19/05.md @@ -23,7 +23,7 @@ material: return price; } - // Start here + // 1. Create getDecimals function here } answer: | @@ -72,7 +72,7 @@ Can you make a `getDecimals` function that returns how many decimals this contra 1. Create a `public view` function called `getDecimals` that returns the result of the `decimals` function from the `AggregatorV3Interface`. When you declare the function, keep in mind that the return value is a `uint8`. -2. The first line of code should call the `priceFeed.decimals()` function, and store the result in a variable named `decimals` of type `uint8`. +2. The first line of code in the `getDecimals` function should call the `priceFeed.decimals()` function, and store the result in a variable named `decimals` of type `uint8`. -3. The second line of the function should return the `decimals` variable. +3. The second line of the `getDecimals` function should return the `decimals` variable. diff --git a/en/19/08.md b/en/19/08.md index c57aa3d53c..fa5d773911 100644 --- a/en/19/08.md +++ b/en/19/08.md @@ -11,9 +11,9 @@ material: import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"; - contract ZombieFactory /*inherit functionality of VRFConsumerBaseV2*/ { + contract ZombieFactory /*1. inherit functionality of VRFConsumerBaseV2*/ { - //create all the new variables required by Chainlink VRF + //2. Create all the new variables required by Chainlink VRF uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; @@ -25,7 +25,7 @@ material: Zombie[] public zombies; - //create constructor + //3. Create a constructor function _createZombie(string memory _name, uint _dna) private { zombies.push(Zombie(_name, _dna)); @@ -96,7 +96,7 @@ Now, as we said, to interact with a Chainlink node to request randomness, we nee -You can find some these variables in the Chainlink VRF Contract addresses documentation page. Once again, the addresses will be different across networks, but for the scope of this lesson we will again be working with the Rinkeby network. For the ones that aren't found in the Chainlink docs, you need to decide and set them yourself. +You can find some of these variables in the Chainlink VRF Contract addresses documentation page. Once again, the addresses will be different across networks, but for the scope of this lesson we will again be working with the Rinkeby network. For the ones that aren't found in the Chainlink docs, you need to decide and set them yourself. As you look at them all, you might ask 'where do I get the subscription ID from'? The answer is, you need to go to the Chainlink Subscription Manager, and create a new subscription that you can use. Once you've created one, you'll be given a subscription ID. This is the number you would put in your smart contract as the subscription ID. You don't need to do this step now for this example as we're just focusing on the smart contract code, but just be aware of it for when you try Chainlink VRF on a live public network. @@ -139,7 +139,7 @@ constructor() VRFConsumerBaseV2(vrfCoordinator) { } ``` -2. Add the following variables in the ZombieFactory contract. They will be required for the call to VRF +2. Add the following variables in the ZombieFactory contract. They will be required for the call to VRF: - `COORDINATOR`, of type `VRFCoordinatorV2Interface` - `s_subscriptionId`, a `uint64`. - `vrfCoordinator`, an `address` hard-coded to the Rinkeby coordinator address of `0x6168499c0cFfCaCD319c818142124B7A15E857ab` @@ -151,7 +151,7 @@ constructor() VRFConsumerBaseV2(vrfCoordinator) { - `s_owner`, an `address` -3. Create a constructor for the `ZombieFactory` contract that takes a uint64 input parameter called '`subscriptionId`. The construction should call the constructor of the `VRFConsumerBaseV2` contract, passing with addresses of the VRF Coordinator from the Rinkeby network as an argument. Again, if you don't remember the syntax, check the example we provided above. Leave the body of the constructor blank for now. +3. Create a constructor for the `ZombieFactory` contract that takes a uint64 input parameter called '`subscriptionId`. The construction should call the constructor of the `VRFConsumerBaseV2` contract, passing the addresses of the VRF Coordinator from the Rinkeby network as an argument. Again, if you don't remember the syntax, check the example we provided above. Leave the body of the constructor blank for now. > Wondering where some of these values came from? We did all the legwork for you, and pulled them from the Chainlink VRF Contract Addresses page of the Chainlink documentation. From feb8cf76354982040cba628da1170bb2df1470f1 Mon Sep 17 00:00:00 2001 From: pappas999 Date: Fri, 24 Jun 2022 11:47:50 +0930 Subject: [PATCH 4/4] added index's to comments --- en/19/09.md | 4 ++-- en/19/10.md | 6 +++--- en/19/11.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/en/19/09.md b/en/19/09.md index c7794e7454..4280b684de 100644 --- a/en/19/09.md +++ b/en/19/09.md @@ -22,7 +22,7 @@ material: uint32 numWords = 1; uint256 public s_requestId; address s_owner; - //create new variable to store random numbers + //1. create new variable to store random numbers uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; @@ -35,7 +35,7 @@ material: Zombie[] public zombies; constructor(uint64 subscriptionId) VRFConsumerBaseV2(vrfCoordinator) { - //complete the constructor body + //2. complete the constructor body } function _createZombie(string memory _name, uint _dna) private { diff --git a/en/19/10.md b/en/19/10.md index 67dd6cf185..8365e6e480 100644 --- a/en/19/10.md +++ b/en/19/10.md @@ -40,11 +40,11 @@ material: s_subscriptionId = subscriptionId; } - //create requestRandomWords function + //1. Create requestRandomWords function - //create fulfillRandomWords function + //2. Create fulfillRandomWords function - //create onlyOwner modifier + //3. Create onlyOwner modifier function _createZombie(string memory _name, uint _dna) private { zombies.push(Zombie(_name, _dna)); diff --git a/en/19/11.md b/en/19/11.md index bf6dbf5a0f..e1980f1774 100644 --- a/en/19/11.md +++ b/en/19/11.md @@ -64,7 +64,7 @@ material: zombies.push(Zombie(_name, _dna)); } - // Delete the function below + // 1. Delete the function below function _generatePseudoRandomDna(string memory _str) private view returns (uint) { uint rand = uint(keccak256(abi.encodePacked(_str))); return rand % dnaModulus;