Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

update to VRFV2 #681

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions en/19/01.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ material:
//2. Create the `PriceConsumerV3`contract

answer: |
pragma solidity ^0.6.7;
pragma solidity ^0.8.7;

contract PriceConsumerV3 {

Expand Down Expand Up @@ -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.
44 changes: 12 additions & 32 deletions en/19/02.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ material:
language: sol
startingCode:
"PriceConsumerV3.sol": |
pragma solidity ^0.6.7;
pragma solidity ^0.8.7;

// Start here
// 1. Import the AggregatorV3Interface contract here

contract PriceConsumerV3 {

}
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 {

Expand All @@ -34,38 +34,19 @@ We want to import the `AggregatorV3Interface` from the <a href="https://github.c

Here is what the full interface looks like:
```javascript
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;

interface AggregatorV3Interface {
function decimals() external view returns (uint8);

function decimals()
external
view
returns (
uint8
);

function description()
external
view
returns (
string memory
);
function description() external view returns (string memory);

function version()
external
view
returns (
uint256
);
function version() external view returns (uint256);

// getRoundData and latestRoundData should both raise "No data present"
// if they do not have data to report, instead of returning unset values
// which could be misinterpreted as actual reported values.
function getRoundData(
uint80 _roundId
)
function getRoundData(uint80 _roundId)
external
view
returns (
Expand All @@ -86,15 +67,14 @@ interface AggregatorV3Interface {
uint256 updatedAt,
uint80 answeredInRound
);

}
```
And we can find it in the <a href="https://github.com/smartcontractkit/chainlink/blob/master/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol" target="_blank">GitHub repo for AggregatorV3Interface</a>.
And we can find it in the <a href="https://github.com/smartcontractkit/chainlink/blob/master/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol" target="_blank">GitHub repo for AggregatorV3Interface</a>.

We can either import directly from GitHub, or from <a href="https://www.npmjs.com/package/@chainlink/contracts" target="_blank">NPM packages</a> 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!
10 changes: 5 additions & 5 deletions en/19/03.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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;
Expand All @@ -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 <a href="https://docs.chain.link/docs/ethereum-addresses/#Rinkeby%20Testnet" target="_blank">Ethereum Data Feeds</a> page of the Chainlink documentation.
14 changes: 7 additions & 7 deletions en/19/04.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -18,13 +18,13 @@ material:
priceFeed = AggregatorV3Interface(0x8A753747A1Fa494EC906cE90E9f37563A8AF630e);
}

// Start here
// 1. Create getLatestPrice function here

}
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;
Expand Down Expand Up @@ -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.
18 changes: 9 additions & 9 deletions en/19/05.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -23,13 +23,13 @@ material:
return price;
}

// Start here
// 1. Create getDecimals function here

}
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;
Expand Down Expand Up @@ -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!

Expand All @@ -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.

8 changes: 4 additions & 4 deletions en/19/06.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
31 changes: 19 additions & 12 deletions en/19/07.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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 {

Expand Down Expand Up @@ -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.


<img src="/course/static/image/lesson-19/chainlink-vrf.png" alt="Chainlink VRF (Verifiable Randomness Function)" width="469">
<img src="/course/static/image/lesson-19/Chainlink-VRF2.png" alt="Chainlink VRF (Verifiable Randomness Function)" width="469">

### Basic Request Model

Expand All @@ -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 <a href="https://chain.link/" target="_blank">LINK or Chainlink token</a>. 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 <a href="https://chain.link/" target="_blank">LINK or Chainlink token</a>. 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 <a href="https://vrf.chain.link/" target="_blank">Subscription Manager</a>.

<img src="/course/static/image/lesson-19/request-model.gif" alt="Chainlink Basic Oracle Request Model" width="469">
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.



<img src="/course/static/image/lesson-19/Request_Receive_Model_V2.gif" alt="Chainlink Basic Oracle Request Model" width="469">

### Why didn't we do this with the data feeds?

Expand All @@ -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 <a href="https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.6/VRFCoordinator.sol" target="_blank">Chainlink VRF contracts</a> to see the exact functions the system is using.
You can check out the <a href="https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/VRFCoordinatorV2.sol" target="_blank">Chainlink VRF contracts</a> 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 <a href="https://github.com/smartcontractkit/chainlink" target="_blank">Chainlink NPM / GitHub</a>.
1. Import the `VRFCoordinatorV2Interface.sol` Solidity version `0.8` from the <a href="https://github.com/smartcontractkit/chainlink" target="_blank">Chainlink NPM / GitHub</a>.

2. Import the `VRFConsumerBaseV2.sol` Solidity version `0.8` from the <a href="https://github.com/smartcontractkit/chainlink" target="_blank">Chainlink NPM / GitHub</a>.

> 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 <a href="https://docs.chain.link/docs/get-a-random-number/" target="_blank">Chainlink Documentation</a>.
> 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 <a href="https://docs.chain.link/docs/get-a-random-number/" target="_blank">Chainlink Documentation</a>.

Loading