diff --git a/public/images/vrf/v2-5/mock/add-consumer.png b/public/images/vrf/v2-5/mock/add-consumer.png new file mode 100644 index 00000000000..fdd0bf0458e Binary files /dev/null and b/public/images/vrf/v2-5/mock/add-consumer.png differ diff --git a/public/images/vrf/v2-5/mock/deploy-consumer.png b/public/images/vrf/v2-5/mock/deploy-consumer.png new file mode 100644 index 00000000000..c517b53e8c2 Binary files /dev/null and b/public/images/vrf/v2-5/mock/deploy-consumer.png differ diff --git a/public/images/vrf/v2-5/mock/deployed-mock.png b/public/images/vrf/v2-5/mock/deployed-mock.png new file mode 100644 index 00000000000..e8348addf26 Binary files /dev/null and b/public/images/vrf/v2-5/mock/deployed-mock.png differ diff --git a/public/images/vrf/v2-5/mock/deployment-contracts-list.png b/public/images/vrf/v2-5/mock/deployment-contracts-list.png new file mode 100644 index 00000000000..ae82ee1113f Binary files /dev/null and b/public/images/vrf/v2-5/mock/deployment-contracts-list.png differ diff --git a/public/images/vrf/v2-5/mock/enable-optimization.png b/public/images/vrf/v2-5/mock/enable-optimization.png new file mode 100644 index 00000000000..8bfd9230fac Binary files /dev/null and b/public/images/vrf/v2-5/mock/enable-optimization.png differ diff --git a/public/images/vrf/v2-5/mock/example-output-sub-id.png b/public/images/vrf/v2-5/mock/example-output-sub-id.png new file mode 100644 index 00000000000..cb6ae80e8dc Binary files /dev/null and b/public/images/vrf/v2-5/mock/example-output-sub-id.png differ diff --git a/public/images/vrf/v2-5/mock/file-explorer.png b/public/images/vrf/v2-5/mock/file-explorer.png new file mode 100644 index 00000000000..62740d04eb0 Binary files /dev/null and b/public/images/vrf/v2-5/mock/file-explorer.png differ diff --git a/public/images/vrf/v2-5/mock/manual-fulfill-request.png b/public/images/vrf/v2-5/mock/manual-fulfill-request.png new file mode 100644 index 00000000000..b4bfa6ff74d Binary files /dev/null and b/public/images/vrf/v2-5/mock/manual-fulfill-request.png differ diff --git a/public/images/vrf/v2-5/mock/mock-deployment-parameters.png b/public/images/vrf/v2-5/mock/mock-deployment-parameters.png new file mode 100644 index 00000000000..c5171e2d1c7 Binary files /dev/null and b/public/images/vrf/v2-5/mock/mock-deployment-parameters.png differ diff --git a/public/images/vrf/v2-5/mock/show-last-request-id.png b/public/images/vrf/v2-5/mock/show-last-request-id.png new file mode 100644 index 00000000000..7f2540e719d Binary files /dev/null and b/public/images/vrf/v2-5/mock/show-last-request-id.png differ diff --git a/public/images/vrf/v2-5/mock/show-random-word.png b/public/images/vrf/v2-5/mock/show-random-word.png new file mode 100644 index 00000000000..6f6b292ad53 Binary files /dev/null and b/public/images/vrf/v2-5/mock/show-random-word.png differ diff --git a/public/samples/VRF/mock/VRFCoordinatorV2_5Mock.sol b/public/samples/VRF/mock/VRFCoordinatorV2_5Mock.sol new file mode 100644 index 00000000000..669582c14d4 --- /dev/null +++ b/public/samples/VRF/mock/VRFCoordinatorV2_5Mock.sol @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; +import "@chainlink/contracts/src/v0.8/vrf/mocks/VRFCoordinatorV2_5Mock.sol"; diff --git a/public/samples/VRF/mock/VRFv2_5Consumer.sol b/public/samples/VRF/mock/VRFv2_5Consumer.sol new file mode 100644 index 00000000000..5638a6221c7 --- /dev/null +++ b/public/samples/VRF/mock/VRFv2_5Consumer.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: MIT +// An example of a consumer contract that relies on a subscription for funding. +pragma solidity 0.8.19; + +import {VRFConsumerBaseV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol"; +import {VRFV2PlusClient} from "@chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol"; + +/** + * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY. + * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. + * DO NOT USE THIS CODE IN PRODUCTION. + */ + +/** + * @title The RandomNumberConsumerV2_5 contract + * @notice A contract that gets random values from Chainlink VRF V2_5 + */ +contract RandomNumberConsumerV2_5 is VRFConsumerBaseV2Plus { + // Your subscription ID. + uint256 immutable s_subscriptionId; + + // The gas lane to use, which specifies the maximum gas price to bump to. + // For a list of available gas lanes on each network, + // see https://docs.chain.link/docs/vrf-contracts/#configurations + bytes32 immutable s_keyHash; + + // Depends on the number of requested values that you want sent to the + // fulfillRandomWords() function. Storing each word costs about 20,000 gas, + // so 100,000 is a safe default for this example contract. Test and adjust + // this limit based on the network that you select, the size of the request, + // and the processing of the callback request in the fulfillRandomWords() + // function. + uint32 constant CALLBACK_GAS_LIMIT = 100000; + + // The default is 3, but you can set this higher. + uint16 constant REQUEST_CONFIRMATIONS = 3; + + // For this example, retrieve 2 random values in one request. + // Cannot exceed VRFCoordinatorV2_5.MAX_NUM_WORDS. + uint32 constant NUM_WORDS = 2; + + uint256[] public s_randomWords; + uint256 public s_requestId; + + event ReturnedRandomness(uint256[] randomWords); + + /** + * @notice Constructor inherits VRFConsumerBaseV2Plus + * + * @param subscriptionId - the subscription ID that this contract uses for funding requests + * @param vrfCoordinator - coordinator, check https://docs.chain.link/vrf/v2-5/supported-networks + * @param keyHash - the gas lane to use, which specifies the maximum gas price to bump to + */ + constructor( + uint256 subscriptionId, + address vrfCoordinator, + bytes32 keyHash + ) VRFConsumerBaseV2Plus(vrfCoordinator) { + s_keyHash = keyHash; + s_subscriptionId = subscriptionId; + } + + /** + * @notice Requests randomness + * Assumes the subscription is funded sufficiently; "Words" refers to unit of data in Computer Science + */ + function requestRandomWords() external onlyOwner { + // Will revert if subscription is not set and funded. + s_requestId = s_vrfCoordinator.requestRandomWords( + VRFV2PlusClient.RandomWordsRequest({ + keyHash: s_keyHash, + subId: s_subscriptionId, + requestConfirmations: REQUEST_CONFIRMATIONS, + callbackGasLimit: CALLBACK_GAS_LIMIT, + numWords: NUM_WORDS, + extraArgs: VRFV2PlusClient._argsToBytes( + VRFV2PlusClient.ExtraArgsV1({nativePayment: false}) + ) + }) + ); + } + + /** + * @notice Callback function used by VRF Coordinator + * + * @param - id of the request + * @param randomWords - array of random results from VRF Coordinator + */ + function fulfillRandomWords( + uint256 /* requestId */, + uint256[] calldata randomWords + ) internal override { + s_randomWords = randomWords; + emit ReturnedRandomness(randomWords); + } +} diff --git a/public/samples/VRF/v2-5/DirectFundingConsumer.sol b/public/samples/VRF/v2-5/DirectFundingConsumer.sol index df3f958b10b..4193b9f6343 100644 --- a/public/samples/VRF/v2-5/DirectFundingConsumer.sol +++ b/public/samples/VRF/v2-5/DirectFundingConsumer.sol @@ -43,20 +43,20 @@ contract DirectFundingConsumer is VRFV2PlusWrapperConsumerBase, ConfirmedOwner { // this limit based on the network that you select, the size of the request, // and the processing of the callback request in the fulfillRandomWords() // function. - uint32 callbackGasLimit = 100000; + uint32 public callbackGasLimit = 100000; // The default is 3, but you can set this higher. - uint16 requestConfirmations = 3; + uint16 public requestConfirmations = 3; // For this example, retrieve 2 random values in one request. // Cannot exceed VRFV2Wrapper.getConfig().maxNumWords. - uint32 numWords = 2; + uint32 public numWords = 2; // Address LINK - hardcoded for Sepolia - address linkAddress = 0x779877A7B0D9E8603169DdbD7836e478b4624789; + address public linkAddress = 0x779877A7B0D9E8603169DdbD7836e478b4624789; // address WRAPPER - hardcoded for Sepolia - address wrapperAddress = 0x195f15F2d49d693cE265b4fB0fdDbE15b1850Cc1; + address public wrapperAddress = 0x195f15F2d49d693cE265b4fB0fdDbE15b1850Cc1; constructor() ConfirmedOwner(msg.sender) diff --git a/src/config/sidebar.ts b/src/config/sidebar.ts index e54502b894f..e322d37c803 100644 --- a/src/config/sidebar.ts +++ b/src/config/sidebar.ts @@ -808,6 +808,10 @@ export const SIDEBAR: Partial> = { title: "Get a random number with subscription", url: "vrf/v2-5/subscription/get-a-random-number", }, + { + title: "Test locally with a subscription mock contract", + url: "vrf/v2-5/subscription/test-locally", + }, { title: "Get a random number with direct funding", url: "vrf/v2-5/direct-funding/get-a-random-number", diff --git a/src/content/vrf/v2-5/subscription/test-locally.mdx b/src/content/vrf/v2-5/subscription/test-locally.mdx new file mode 100644 index 00000000000..38d565442e7 --- /dev/null +++ b/src/content/vrf/v2-5/subscription/test-locally.mdx @@ -0,0 +1,168 @@ +--- +section: vrf +title: "Local testing using a mock subscription contract" +metadata: + description: "Example contract for generating random words using the VRF v2.5 subscription method on your local blockchain using a mock contract." +--- + +import VrfCommon from "@features/vrf/v2/common/VrfCommon.astro" +import ContentCommon from "@features/common/ContentCommon.astro" +import { Aside, ClickToZoom, CodeSample, CopyText, Icon } from "@components" +import { linkEth } from "@features/data" +import { LatestPrice } from "@features/feeds" +import button from "@chainlink/design-system/button.module.css" + +This guide explains how to test Chainlink VRF v2.5 on a [Remix IDE](https://remix-ide.readthedocs.io/en/latest/run.html#environment) sandbox blockchain environment. **Note**: You can reuse the same logic on another development environment, such as Hardhat or Foundry. For example, read the Hardhat Starter Kit [RandomNumberConsumer unit tests](https://github.com/smartcontractkit/hardhat-starter-kit/blob/main/test/unit/RandomNumberConsumer.spec.js). + + + +## Benefits of local testing + + + +## Testing logic + +Complete the following tasks to test your VRF v2.5 consumer locally: + +1. Deploy the [VRFCoordinatorV2_5Mock](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/mocks/VRFCoordinatorV2_5Mock.sol). This contract is a mock of the [VRFCoordinatorV2_5](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/dev/VRFCoordinatorV2_5.sol) contract. +1. Call the [createSubscription function](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/dev/interfaces/IVRFSubscriptionV2Plus.sol#L56) (which `VRFCoordinatorV2_5Mock` inherits) to create a new subscription. +1. Call the VRFCoordinatorV2_5Mock [`fundSubscription` function](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/mocks/VRFCoordinatorV2_5Mock.sol#L174) to fund your newly created subscription. **Note**: You can fund with an arbitrary amount. +1. Deploy your VRF consumer contract. +1. Call the [addConsumer function](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/dev/interfaces/IVRFSubscriptionV2Plus.sol#L12) (which `VRFCoordinatorV2_5Mock` inherits) to add your consumer contract to your subscription. +1. Request random words from your consumer contract. +1. Call the VRFCoordinatorV2_5Mock [fulfillRandomWords function](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/mocks/VRFCoordinatorV2_5Mock.sol#L101) to fulfill your consumer contract request. + +## Testing + +### Open the contracts on Remix IDE + +For local testing, use the default "Remix VM" environment. + +Open _VRFv2_5Consumer_ and compile in Remix: + + + +Open _VRFCoordinatorV2_5Mock_ in Remix: + + + +On the _Solidity Compiler_ tab, expand the _Advanced Configurations_ section and check the _Enable optimization_ box before you compile the _VRFCoordinatorV2_5Mock_ contract: + + + +Your Remix IDE file explorer should display _VRFCoordinatorV2_5Mock.sol_ and _VRFv2_5Consumer.sol_: + + + +### Deploy VRFCoordinatorV2_5Mock + +1. Open _VRFCoordinatorV2_5Mock.sol_. + +1. Under _DEPLOY & RUN TRANSACTIONS_, select _VRFCoordinatorV2_5Mock_. + + + +1. Under _DEPLOY_, fill in the `_BASEFEE`, `_GASPRICELINK` and `_WEIPERUNITLINK`. These variables are used in the _VRFCoordinatorV2_5Mock_ contract to represent the base fee, the gas price (in LINK tokens), and the current LINK/ETH price for the VRF requests. + + + + You can set: + + - `_BASEFEE` to + - `_GASPRICELINK` to + - `_WEIPERUNITLINK` to the current LINK/ETH price. Click the "Latest Price" button to view it: + {" "} + + +1. Click _transact_ to deploy the _VRFCoordinatorV2_5Mock_ contract. + +1. Once deployed, you should see the _VRFCoordinatorV2_5Mock_ contract under _Deployed Contracts_. + + + +1. Note the address of the deployed contract. + +### Create and fund a subscription + +1. Click `createSubscription` to create a new subscription. + +1. In the Remix IDE console, read your transaction decoded output to find the subscription ID. Note the subscription ID, which is required for multiple steps in this tutorial. + + + +1. Click on `fundSubscription` to fund your subscription. Fill in your subscription ID for `_subid` and set the `_amount` to . This mocks funding your subscription with 100 LINK. + +### Deploy the VRF consumer contract + +1. In the file explorer, open _VRFv2_5Consumer.sol_. + +1. Under _DEPLOY & RUN TRANSACTIONS_, select _RandomNumberConsumerV2_5_. + + + +1. Under _DEPLOY_, fill in the following parameters: + + - `SUBSCRIPTIONID` with your subscription ID + - `VRFCOORDINATOR` with the deployed _VRFCoordinatorV2_5Mock_ address + - `_KEYHASH_` with an arbitrary `bytes32` (In this example, you can set the _KEYHASH_ to ). + +1. Click _transact_ to deploy the _RandomNumberConsumerV2_5_ contract. + +1. After the consumer contract is deployed, you should see the _RandomNumberConsumerV2_5_ contract under _Deployed Contracts_. Note the address of the deployed contract. + +### Add the consumer contract to your subscription + +1. Under _Deployed Contracts_, open the functions list of your deployed _VRFCoordinatorV2_5Mock_ contract. + +1. Click _addConsumer_ and fill in the `_subid` with your subscription ID and `_consumer` with your deployed consumer contract address. + + + +1. Click _transact_. + +### Request random words + +1. Under _Deployed Contracts_, open the functions list of your deployed _RandomNumberConsumerV2_5_ contract. + +1. Click `requestRandomWords`. + + + +1. Click `s_requestId` to display the last request ID. In this example, the output is _1_. + + + +1. Note your request ID. + +### Fulfill the VRF request + +Because you are testing on a local blockchain environment, you must fulfill the VRF request yourself. + +1. Under _Deployed Contracts_, open the functions list of your deployed _VRFCoordinatorV2_5Mock_ contract. + +1. Click `fulfillRandomWords` and fill in `_requestId` with your VRF request ID and `_consumer` with your consumer contract address. + + + +1. Click `transact`. + +### Check the results + +1. Under _Deployed Contracts_, open the functions list of your deployed _RandomNumberConsumerV2_5_ contract. + +1. For each VRF request, your consumer contract requests two random words. After a request is fulfilled, the two random words are stored in the `s_randomWords` array. You can check the stored random words by reading the two first indexes of the `s_randomWords` array. To do so, click the _s_randomWords_ function and: + + 1. Fill in the index with _0_ then click _call_ to read the first random word. + + {" "} + + + + 1. You can read the second random word in a similar way: fill in the index with _1_ then click _call_ to display the second random word. + +## Next steps + +This guide demonstrated how to test a VRF v2.5 consumer contract on your local blockchain. The guide uses the Remix IDE for learning purposes, but you can reuse the same [testing logic](#testing-logic) in another development environment, such as Hardhat. For example, see the Hardhat Starter Kit [RandomNumberConsumer unit tests](https://github.com/smartcontractkit/hardhat-starter-kit/blob/main/test/unit/RandomNumberConsumer.spec.js). diff --git a/src/features/data/index.ts b/src/features/data/index.ts index a78da0d032c..d5596a0a8c8 100644 --- a/src/features/data/index.ts +++ b/src/features/data/index.ts @@ -13,6 +13,17 @@ export const priceFeedAddresses = { }, } +export const linkEth = { + link: { + eth: { + sepolia: { + address: "0x42585eD362B3f1BCa95c640FdFf35Ef899212734", + historicalRound: "36893488147419106612", + }, + }, + }, +} + export const registryAddresses = { "link-usd": { baseSymbol: "LINK",