diff --git a/hardhat.config.ts b/hardhat.config.ts index 64d86908e90..21f61a5db3e 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -3,6 +3,13 @@ import { HardhatUserConfig } from "hardhat/config" import "@nomicfoundation/hardhat-toolbox" +const compilerSettings = { + optimizer: { + enabled: true, + runs: 1000, + }, +} + const config: HardhatUserConfig = { paths: { root: "./", diff --git a/public/images/vrf/v2-5/mock/injected-metamask.png b/public/images/vrf/v2-5/mock/injected-metamask.png new file mode 100644 index 00000000000..b42136b5a61 Binary files /dev/null and b/public/images/vrf/v2-5/mock/injected-metamask.png differ diff --git a/public/images/vrf/v2-5/mock/metamask-connect-imported-account.png b/public/images/vrf/v2-5/mock/metamask-connect-imported-account.png new file mode 100644 index 00000000000..2eeec6b0c33 Binary files /dev/null and b/public/images/vrf/v2-5/mock/metamask-connect-imported-account.png differ diff --git a/public/images/vrf/v2-5/mock/metamask-imported-account.png b/public/images/vrf/v2-5/mock/metamask-imported-account.png new file mode 100644 index 00000000000..4e2d8ff0f57 Binary files /dev/null and b/public/images/vrf/v2-5/mock/metamask-imported-account.png differ diff --git a/public/images/vrf/v2-5/mock/metamask-local-settings.png b/public/images/vrf/v2-5/mock/metamask-local-settings.png new file mode 100644 index 00000000000..c22944c0bd5 Binary files /dev/null and b/public/images/vrf/v2-5/mock/metamask-local-settings.png differ diff --git a/public/images/vrf/v2-5/mock/mock-subdirectory-delete-sub.png b/public/images/vrf/v2-5/mock/mock-subdirectory-delete-sub.png new file mode 100644 index 00000000000..ab1b5cc6502 Binary files /dev/null and b/public/images/vrf/v2-5/mock/mock-subdirectory-delete-sub.png differ diff --git a/public/images/vrf/v2-5/mock/mock-subdirectory-df.png b/public/images/vrf/v2-5/mock/mock-subdirectory-df.png new file mode 100644 index 00000000000..26caec265f4 Binary files /dev/null and b/public/images/vrf/v2-5/mock/mock-subdirectory-df.png differ diff --git a/public/images/vrf/v2-5/mock/remix-compiler-select-0-8-19.png b/public/images/vrf/v2-5/mock/remix-compiler-select-0-8-19.png new file mode 100644 index 00000000000..7ae994fa404 Binary files /dev/null and b/public/images/vrf/v2-5/mock/remix-compiler-select-0-8-19.png differ diff --git a/public/images/vrf/v2-5/mock/remix-coordinator-mock-selected.png b/public/images/vrf/v2-5/mock/remix-coordinator-mock-selected.png new file mode 100644 index 00000000000..30de204f9c0 Binary files /dev/null and b/public/images/vrf/v2-5/mock/remix-coordinator-mock-selected.png differ diff --git a/public/images/vrf/v2-5/mock/remix-folder-upload-confirmation.png b/public/images/vrf/v2-5/mock/remix-folder-upload-confirmation.png new file mode 100644 index 00000000000..57a8728018e Binary files /dev/null and b/public/images/vrf/v2-5/mock/remix-folder-upload-confirmation.png differ diff --git a/public/images/vrf/v2-5/mock/remix-get-active-sub-ids.png b/public/images/vrf/v2-5/mock/remix-get-active-sub-ids.png new file mode 100644 index 00000000000..216d23a4899 Binary files /dev/null and b/public/images/vrf/v2-5/mock/remix-get-active-sub-ids.png differ diff --git a/public/images/vrf/v2-5/mock/remix-imported-account-connected.png b/public/images/vrf/v2-5/mock/remix-imported-account-connected.png new file mode 100644 index 00000000000..8b403d961f9 Binary files /dev/null and b/public/images/vrf/v2-5/mock/remix-imported-account-connected.png differ diff --git a/public/images/vrf/v2-5/mock/remix-ovm-gaspriceoracle.png b/public/images/vrf/v2-5/mock/remix-ovm-gaspriceoracle.png new file mode 100644 index 00000000000..e29235aab57 Binary files /dev/null and b/public/images/vrf/v2-5/mock/remix-ovm-gaspriceoracle.png differ diff --git a/public/images/vrf/v2-5/mock/remix-upload-folder.png b/public/images/vrf/v2-5/mock/remix-upload-folder.png new file mode 100644 index 00000000000..29e207fcf23 Binary files /dev/null and b/public/images/vrf/v2-5/mock/remix-upload-folder.png differ diff --git a/public/images/vrf/v2-5/mock/wrapper-deployment-params.png b/public/images/vrf/v2-5/mock/wrapper-deployment-params.png new file mode 100644 index 00000000000..e0cdd73170d Binary files /dev/null and b/public/images/vrf/v2-5/mock/wrapper-deployment-params.png differ diff --git a/public/images/vrf/v2-5/mock/wrapper-set-config.png b/public/images/vrf/v2-5/mock/wrapper-set-config.png new file mode 100644 index 00000000000..93761f64ae7 Binary files /dev/null and b/public/images/vrf/v2-5/mock/wrapper-set-config.png differ diff --git a/public/samples/VRF/mock/v2-5/DirectFundingConsumerV2_5.sol b/public/samples/VRF/mock/v2-5/DirectFundingConsumerV2_5.sol new file mode 100644 index 00000000000..1f77b509cc6 --- /dev/null +++ b/public/samples/VRF/mock/v2-5/DirectFundingConsumerV2_5.sol @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: MIT +// An example of a consumer contract that directly pays for each request. +pragma solidity 0.8.19; + +import {ConfirmedOwner} from "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol"; +import {VRFV2PlusWrapperConsumerBase} from "@chainlink/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapperConsumerBase.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 UNAUDITED CODE. + * DO NOT USE THIS CODE IN PRODUCTION. + */ + +contract DirectFundingConsumerV2_5 is + VRFV2PlusWrapperConsumerBase, + ConfirmedOwner +{ + event RequestSent(uint256 requestId, uint32 numWords, uint256 paid); + event RequestFulfilled( + uint256 requestId, + uint256[] randomWords, + uint256 payment + ); + error InsufficientFunds(uint256 balance, uint256 calculatedRequestPrice); + error RequestNotFound(uint256 requestId); + error LinkTransferError(address sender, address receiver, uint256 amount); + error FailedToWithdrawEth(address sender, address receiver, uint256 amount); + error NothingToWithdraw(); + + struct RequestStatus { + uint256 paid; // amount paid in link + bool fulfilled; // whether the request has been successfully fulfilled + uint256[] randomWords; + } + mapping(uint256 => RequestStatus) + public s_requests; /* requestId --> requestStatus */ + + // past requests Id. + uint256[] public requestIds; + uint256 public lastRequestId; + + // configuration: https://docs.chain.link/vrf/v2-5/supported-networks + constructor( + address _wrapperAddress + ) + ConfirmedOwner(msg.sender) + VRFV2PlusWrapperConsumerBase(_wrapperAddress) + {} + + // Depends on the number of requested values that you want sent to the + // fulfillRandomWords() function. 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. + // The default is 3, but you can set this higher. + // For this example, retrieve 2 random values in one request. + // Cannot exceed VRFV2PlusWrapper.getConfig().maxNumWords. + function requestRandomWords( + uint32 _callbackGasLimit, + uint16 _requestConfirmations, + uint32 _numWords, + bool enableNativePayment + ) external onlyOwner returns (uint256) { + bytes memory extraArgs = VRFV2PlusClient._argsToBytes( + VRFV2PlusClient.ExtraArgsV1({nativePayment: enableNativePayment}) + ); + uint256 requestId; + uint256 reqPrice; + if (enableNativePayment) { + uint256 calculatedRequestPrice = i_vrfV2PlusWrapper + .calculateRequestPriceNative(_callbackGasLimit, _numWords); + uint256 balance = address(this).balance; + if (calculatedRequestPrice > balance) + revert InsufficientFunds(balance, calculatedRequestPrice); + (requestId, reqPrice) = requestRandomnessPayInNative( + _callbackGasLimit, + _requestConfirmations, + _numWords, + extraArgs + ); + } else { + uint256 calculatedRequestPrice = i_vrfV2PlusWrapper + .calculateRequestPrice(_callbackGasLimit, _numWords); + uint256 balance = i_linkToken.balanceOf(address(this)); + if (calculatedRequestPrice > balance) + revert InsufficientFunds(balance, calculatedRequestPrice); + (requestId, reqPrice) = requestRandomness( + _callbackGasLimit, + _requestConfirmations, + _numWords, + extraArgs + ); + } + + s_requests[requestId] = RequestStatus({ + paid: reqPrice, + randomWords: new uint256[](0), + fulfilled: false + }); + requestIds.push(requestId); + lastRequestId = requestId; + emit RequestSent(requestId, _numWords, reqPrice); + return requestId; + } + + function fulfillRandomWords( + uint256 _requestId, + uint256[] memory _randomWords + ) internal override { + RequestStatus storage request = s_requests[_requestId]; + if (request.paid == 0) revert RequestNotFound(_requestId); + request.fulfilled = true; + request.randomWords = _randomWords; + emit RequestFulfilled(_requestId, _randomWords, request.paid); + } + + function getNumberOfRequests() external view returns (uint256) { + return requestIds.length; + } + + function getRequestStatus( + uint256 _requestId + ) + external + view + returns (uint256 paid, bool fulfilled, uint256[] memory randomWords) + { + RequestStatus memory request = s_requests[_requestId]; + if (request.paid == 0) revert RequestNotFound(_requestId); + return (request.paid, request.fulfilled, request.randomWords); + } + + /** + * Allow withdraw of Link tokens from the contract + */ + function withdrawLink(address _receiver) public onlyOwner { + uint256 amount = i_linkToken.balanceOf(address(this)); + // Revert if there is nothing to withdraw + if (amount == 0) revert NothingToWithdraw(); + + bool success = i_linkToken.transfer(_receiver, amount); + if (!success) + revert LinkTransferError( + msg.sender, + _receiver, + i_linkToken.balanceOf(address(this)) + ); + } + + function withdraw(address _receiver) public onlyOwner { + // Retrieve the balance of this contract + uint256 amount = address(this).balance; + + // Revert if there is nothing to withdraw + if (amount == 0) revert NothingToWithdraw(); + + // Attempt to send the funds, capturing the success status and discarding any return data + (bool sent, ) = _receiver.call{value: amount}(""); + + // Revert if the send failed, with information about the attempted transfer + if (!sent) revert FailedToWithdrawEth(msg.sender, _receiver, amount); + } + + receive() external payable {} +} diff --git a/public/samples/VRF/mock/v2-5/LinkToken.sol b/public/samples/VRF/mock/v2-5/LinkToken.sol new file mode 100644 index 00000000000..76d9d30adae --- /dev/null +++ b/public/samples/VRF/mock/v2-5/LinkToken.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "@chainlink/contracts/src/v0.8/mocks/MockLinkToken.sol"; diff --git a/public/samples/VRF/mock/v2-5/MockV3Aggregator.sol b/public/samples/VRF/mock/v2-5/MockV3Aggregator.sol new file mode 100644 index 00000000000..d48b644401c --- /dev/null +++ b/public/samples/VRF/mock/v2-5/MockV3Aggregator.sol @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; +import "@chainlink/contracts/src/v0.8/tests/MockV3Aggregator.sol"; diff --git a/public/samples/VRF/mock/VRFCoordinatorV2_5Mock.sol b/public/samples/VRF/mock/v2-5/VRFCoordinatorV2_5Mock.sol similarity index 100% rename from public/samples/VRF/mock/VRFCoordinatorV2_5Mock.sol rename to public/samples/VRF/mock/v2-5/VRFCoordinatorV2_5Mock.sol diff --git a/public/samples/VRF/mock/v2-5/VRFV2PlusWrapper.sol b/public/samples/VRF/mock/v2-5/VRFV2PlusWrapper.sol new file mode 100644 index 00000000000..ecd07e3f09b --- /dev/null +++ b/public/samples/VRF/mock/v2-5/VRFV2PlusWrapper.sol @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; +import "@chainlink/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol"; diff --git a/public/samples/VRF/mock/VRFv2_5Consumer.sol b/public/samples/VRF/mock/v2-5/VRFv2_5Consumer.sol similarity index 100% rename from public/samples/VRF/mock/VRFv2_5Consumer.sol rename to public/samples/VRF/mock/v2-5/VRFv2_5Consumer.sol diff --git a/public/samples/VRF/mock/LinkToken.sol b/public/samples/VRF/mock/v2/LinkToken.sol similarity index 100% rename from public/samples/VRF/mock/LinkToken.sol rename to public/samples/VRF/mock/v2/LinkToken.sol diff --git a/public/samples/VRF/mock/MockV3Aggregator.sol b/public/samples/VRF/mock/v2/MockV3Aggregator.sol similarity index 100% rename from public/samples/VRF/mock/MockV3Aggregator.sol rename to public/samples/VRF/mock/v2/MockV3Aggregator.sol diff --git a/public/samples/VRF/mock/RandomNumberDirectFundingConsumerV2.sol b/public/samples/VRF/mock/v2/RandomNumberDirectFundingConsumerV2.sol similarity index 100% rename from public/samples/VRF/mock/RandomNumberDirectFundingConsumerV2.sol rename to public/samples/VRF/mock/v2/RandomNumberDirectFundingConsumerV2.sol diff --git a/public/samples/VRF/mock/VRFCoordinatorV2Mock.sol b/public/samples/VRF/mock/v2/VRFCoordinatorV2Mock.sol similarity index 100% rename from public/samples/VRF/mock/VRFCoordinatorV2Mock.sol rename to public/samples/VRF/mock/v2/VRFCoordinatorV2Mock.sol diff --git a/public/samples/VRF/mock/VRFV2Wrapper.sol b/public/samples/VRF/mock/v2/VRFV2Wrapper.sol similarity index 100% rename from public/samples/VRF/mock/VRFV2Wrapper.sol rename to public/samples/VRF/mock/v2/VRFV2Wrapper.sol diff --git a/public/samples/VRF/mock/VRFv2Consumer.sol b/public/samples/VRF/mock/v2/VRFv2Consumer.sol similarity index 100% rename from public/samples/VRF/mock/VRFv2Consumer.sol rename to public/samples/VRF/mock/v2/VRFv2Consumer.sol diff --git a/src/content/vrf/v2-5/direct-funding/test-locally.mdx b/src/content/vrf/v2-5/direct-funding/test-locally.mdx new file mode 100644 index 00000000000..6f21c144e52 --- /dev/null +++ b/src/content/vrf/v2-5/direct-funding/test-locally.mdx @@ -0,0 +1,501 @@ +--- +section: vrf +title: "Local testing using a Mock contract" +metadata: + description: "Example contract for generating random words using the VRF v2 direct funding 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 { CodeSample, CopyText, ClickToZoom, Aside } 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. For example, read the Hardhat Starter Kit [RandomNumberDirectFundingConsumer unit tests](https://github.com/smartcontractkit/hardhat-starter-kit/blob/main/test/unit/RandomNumberDirectFundingConsumer.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. Deploy the [MockV3Aggregator](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/tests/MockV3Aggregator.sol) contract. +1. Deploy the [LinkToken](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/shared/token/ERC677/LinkToken.sol) contract. +1. Deploy the [VRFV2PlusWrapper](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol) contract. +1. Call the VRFV2PlusWrapper [setConfig function](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol#L235) to set wrapper specific parameters. +1. Fund the VRFV2PlusWrapper subscription. +1. Call the [addConsumer function](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/dev/interfaces/IVRFSubscriptionV2Plus.sol#L12) to add the wrapper contract to your subscription. (VRFCoordinatorV2_5Mock inherits this function from the IVRFSubscriptionV2Plus interface.) +1. Deploy your VRF consumer contract. +1. Fund your consumer contract with LINK tokens. +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#L102) to fulfill your consumer contract request. + +### Preview the code (optional) + +You can preview the code for the contracts you will deploy in this tutorial, or skip this step and proceed to [setting up your test environment](#prerequisites). + +_DirectFundingConsumerV2_5_ is the consuming contract that requests and directly pays the VRF service for random values: + + + +The other contracts are simply imported from their source in the `chainlink/contracts` npm package: + +- The _MockV3Aggregator_ contract allows you to simulate an oracle price feed without interacting with the existing Chainlink network. + {" "} + +- Deploying _LinkToken_ in your test environment allows you to mock transferring LINK to test contracts. + {" "} + +- As the VRF v2.5 direct funding [end-to-end diagram](/vrf/v2-5/overview/direct-funding#request-and-receive-data) explains, the _VRFV2PlusWrapper_ acts as a wrapper for the coordinator contract. + {" "} + +- This contract mocks the VRF V2.5 Coordinator contract. + {" "} + + +## Prerequisites + +This guide will require you to finetune the gas limit when fulfilling requests. When writing, manually setting up the gas limits on Remix IDE is not supported, so you will use Remix IDE in conjunction with [MetaMask](https://metamask.io/). + +Hardhat lets you run an Ethereum blockchain locally for testing. Use the [Hardhat Starter Kit](https://github.com/smartcontractkit/hardhat-starter-kit) to quickly fire up a personal Ethereum blockchain. + +1. Install the Hardhat Starter Kit and its dependencies: + + ``` + git clone https://github.com/smartcontractkit/hardhat-starter-kit/ + cd hardhat-starter-kit && npm install + ``` + +1. At the root directory of `hardhat-starter-kit`, start an Ethereum blockchain locally: + + ``` + npx hardhat node + ``` + + The output includes the RPC server and the addresses and private keys for 20 test accounts. The first one is shown below. First, make sure to note the RPC server. In this example, the RPC server is `http://127.0.0.1:8545/`: + + ``` + Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/ + + Accounts + ======== + + WARNING: These accounts, and their private keys, are publicly known. + Any funds sent to them on Mainnet or any other live network WILL BE LOST. + + Account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH) + Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + + ... + + WARNING: These accounts, and their private keys, are publicly known. + Any funds sent to them on Mainnet or any other live network WILL BE LOST. + ``` + + After the test account information, you will see output indicating that the Ethereum blockchain is running locally: + + ``` + eth_blockNumber (14) + eth_chainId (6) + eth_blockNumber (2) + eth_getBalance (3) + eth_getBlockByNumber + eth_blockNumber + net_version (2) + eth_chainId + ``` + +1. Follow the MetaMask [official guide](https://support.metamask.io/hc/en-us/articles/360043227612-How-to-add-a-custom-network-RPC#h_01G63GGJ83DGDRCS2ZWXM37CV5) to add a custom network manually. + + + +1. Import the first Hardhat test account into MetaMask. + + 1. In the terminal window where you're running the local Ethereum blockchain, scroll up in the output to the test account information. Copy the private key for the first account: + + ``` + Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/ + + Accounts + ======== + + WARNING: These accounts, and their private keys, are publicly known. + Any funds sent to them on Mainnet or any other live network WILL BE LOST. + + Account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH) + Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + + ... + ``` + + 1. Follow the MetaMask [official guide](https://support.metamask.io/hc/en-us/articles/360015489331-How-to-import-an-account) to import an account using a private key. + + 1. Your MetaMask is connected to the Hardhat test account, and you should have access to the newly imported account. + + {" "} + +## Testing + +### Open the contracts in Remix IDE + +For this tutorial, there are five contracts to open within the same Remix workspace. The fastest way to do this is to clone the documentation repo locally, and then in Remix, upload the specific subdirectory of sample code used for both local testing tutorials (`public/samples/VRF/mock/v2-5`). (Alternatively, you can open one of the contracts in Remix, and then add the other four contracts manually within the same directory. All the code is shown in the optional [code preview](#preview-the-code-optional).) + +1. Clone the documentation repo: + + ```sh + git clone https://github.com/smartcontractkit/documentation.git + ``` + +1. [Open Remix](https://remix.ethereum.org/). +1. On the _FILE EXPLORER_ tab, create a new Remix workspace for this tutorial, or use the default workspace. +1. Click the **Upload folder** button, navigate to where you cloned the `documentation` repo, and then select the `public/samples/VRF/mock/v2-5` subdirectory. + +1. Remix prompts you to confirm that you would like to upload 6 files from `v2-5`. Click **Upload**. + +1. Your Remix file explorer should show the `v2-5` directory containing all the contracts you need for this tutorial. The _VRFv2_5Consumer.sol_ file is not used in this tutorial, so you can delete it from your Remix workspace: + +1. Your Remix file explorer shows only the five contracts you need for this tutorial: + +1. Click the name of each file to open the code in your Remix file explorer. Remix flags some errors that we resolve in the next section. + +### Compile the contracts + +In this section, you will make a few adjustments to compile all of the contracts. Remix indicates that each contract is compiled successfully with a green checkmark icon on the _SOLIDITY COMPILER_ tab. Otherwise, it shows a yellow "warning" icon or a red "error" icon. + +1. In Remix, within the _FILE EXPLORER_ tab, select the _DirectFundingConsumerV2_5_ contract. +1. Navigate to the _SOLIDITY COMPILER_ tab and select version `0.8.19`: + +1. Compile _DirectFundingConsumerV2_5.sol_: click the blue **Compile DirectFundingConsumerV2_5.sol** button or use your keyboard shortcut for "save" (Ctrl+S or Command+S). +1. In the _FILE EXPLORER_ tab, select the _VRFCoordinatorV2_5Mock.sol_ contract. +1. In the _Solidity Compiler_ tab, you will see a warning indicating that the contract size is too large to deploy. Expand the _Advanced Configurations_ section and check the _Enable optimization_ box, using the default `200` runs: + +1. Compile the _VRFCoordinatorV2_5Mock_ contract. +1. In the _FILE EXPLORER_ tab, select the _MockV3Aggregator.sol_ contract, and then compile it using your "save" keyboard shortcut. +1. Select _LinkToken.sol_ and compile it using your "save" keyboard shortcut. +1. Compiling _VRFV2PlusWrapper.sol_ currently requires a workaround to resolve its Solidity version mismatch with `Ownable` using `0.8.20`: + 1. In the _FILE EXPLORER_ tab, navigate to `OVM_GasPriceOracle.sol`. The full path is `@chainlink/contracts/src/v0.8/vendor/@eth-optimism/contracts/v0.8.9/contracts/L2/predeploys/OVM_GasPriceOracle.sol`: + + 1. At the top of this file, change the import statement to: + ``` + import {Ownable} from "@openzeppelin/contracts@4.9.6/access/Ownable.sol"; + ``` + 1. _VRFV2PlusWrapper.sol_ successfully compiles when this dependency uses an earlier version of `Ownable.sol`. + +### Select the correct Remix IDE environment + +Under _DEPLOY & RUN TRANSACTIONS_: + +1. Set the Environment to _Injected Provider - MetaMask_: + + + +1. On MetaMask, connect your Hardhat test account to the Remix IDE. + + + +1. Click Connect. The Remix IDE environment should be set to the correct environment, and the account should be the Hardhat test account. + + + +### 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 _VRFCoordinatorV2Mock_ contract. + +1. A MetaMask popup will open. Click _Confirm_. + +1. Once deployed, you should see the _VRFCoordinatorV2Mock_ contract under _Deployed Contracts_. + +1. Note the address of the deployed contract. + +#### Create a subscription + +1. In the _Deployed Contracts_ section, expand your deployed _VRFCoordinatorV2Mock_ contract. +1. Click the orange **createSubscription** button and confirm the transaction in MetaMask. +1. The output or logs for this function may not show the resulting subscription ID in Remix. Click the blue **getActiveSubscriptionIds** button and fill in `0` for `startIndex` and `1` for `maxCount`. This will display the subscription ID for the subscription you just created: + + +### Deploy MockV3Aggregator + +The _MockV3Aggregator_ contract is designed for testing purposes, allowing you to simulate an oracle price feed without interacting with the existing Chainlink network. + +1. Open _MockV3Aggregator.sol_. + +1. Under _DEPLOY & RUN TRANSACTIONS_, select _MockV3Aggregator_. + + + +1. Under _DEPLOY_, fill in `_DECIMALS` and `_INITIALANSWER`. These variables are used in the _MockV3Aggregator_ contract to represent the number of decimals the aggregator's answer should have and the most recent price feed answer. You can set: `_DECIMALS=18` and `_INITIALANSWER=3000000000000000` (We are considering that `1 LINK = 0.003 native gas tokens`). + +1. Click _transact_ to deploy the _MockV3Aggregator_ contract. + +1. A MetaMask popup will open. Click _Confirm_. + +1. Once deployed, you should see the _MockV3Aggregator_ contract under _Deployed Contracts_. + +1. Note the address of the deployed contract. + +### Deploy LinkToken + +The Chainlink VRF v2.5 direct funding method requires your consumer contract to pay for VRF requests in LINK. Therefore, you have to deploy the _LinkToken_ contract to your local blockchain. + +1. Open _LinkToken.sol_. + +1. Under _DEPLOY & RUN TRANSACTIONS_, select _LinkToken_. + + + +1. Under _DEPLOY_, click _transact_ to deploy the _LinkToken_ contract. + +1. A MetaMask popup will open. Click _Confirm_. + +1. Once deployed, you should see the _LinkToken_ contract under _Deployed Contracts_. + +1. Note the address of the deployed contract. + +### Deploy VRFV2PlusWrapper + +As the VRF v2.5 direct funding [end-to-end diagram](/vrf/v2-5/overview/direct-funding#request-and-receive-data) explains, the _VRFV2PlusWrapper_ acts as a wrapper for the coordinator contract. + +Unlike in VRF v2, the v2.5 wrapper contract requires an existing subscription ID for deployment, so you'll need to create a subscription first. If you missed it, [create a subscription](#create-a-subscription) and then return to this section. + +1. Open _VRFV2PlusWrapper.sol_. + +1. Under _DEPLOY & RUN TRANSACTIONS_, select _VRFV2PlusWrapper_. + +1. Under _DEPLOY_, fill in the following parameters: + + - `_LINK` with the _LinkToken_ contract address + - `_LINKETHFEED` with the _MockV3Aggregator_ contract address + - `_COORDINATOR` with the _VRFCoordinatorV2_5Mock_ contract address + - `_SUBID` with the subscription ID + + {" "} + + + +1. Click _transact_ to deploy the _VRFV2PlusWrapper_ contract. + +1. A MetaMask popup will open. Click _Confirm_. + +1. Once deployed, you should see the _VRFV2PlusWrapper_ contract under _Deployed Contracts_. + +1. Note the address of the deployed contract. + +1. Add your newly deployed wrapper contract as a consumer to the subscription: + 1. In the _Deployed Contracts_ section, expand your deployed _VRFCoordinatorV2Mock_ contract. + 1. Click the **addConsumer** button and provide your subscription ID and the address of the wrapper contract you just deployed. + 1. Click _transact_ and confirm the transaction in MetaMask. + +### Configure the VRFV2PlusWrapper + +1. Under _Deployed Contracts_, open the functions list of your deployed _VRFV2PlusWrapper_ contract. + +1. Click `setConfig` and fill in the following parameters. You can find the wrapper addresses for all supported networks on the [Supported Networks](/vrf/v2-5/supported-networks) page with links to the block explorer. In the block explorer, select **Contract** and **Read Contract**, then expand the `getConfig` function and click **Query**. For Sepolia, the config values are found [here](https://sepolia.etherscan.io/address/0x195f15F2d49d693cE265b4fB0fdDbE15b1850Cc1#readContract#F7) in Sepolia Etherscan and are also listed below: + + - `_wrapperGasOverhead` with `13400` + - `_coordinatorGasOverheadNative` with `90000` + - `_coordinatorGasOverheadLink` with `112000` + - `_coordinatorGasOverheadPerWord` with `435` + - `_coordinatorNativePremiumPercentage` with `24` + - `_coordinatorLinkPremiumPercentage` with `20` + - `_keyHash` with `0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae` + - `_maxNumWords` with `10` + - `stalenessSeconds` with `172800` + - `fallbackWeiPerUnitLink` with `5347462396894712` + - `fulfillmentFlatFeeNativePPM` with `0` + - `fulfillmentFlatFeeLinkDiscountPPM` with `0` + **Note** on these variables: + + 1. `_wrapperGasOverhead`: This variable reflects the gas overhead of the wrapper's fulfillRandomWords function. The cost for this gas is passed to the user. + + 1. Coordinator gas overhead: These variables reflect the gas overhead of the coordinator's `fulfillRandomWords` function. The cost for this gas is billed to the _VRFV2PlusWrapper_ subscription and must, therefore, be included in the VRF v2.5 direct funding requests pricing. + + - `_coordinatorGasOverheadLink`: Coordinator gas overhead when paying for VRF requests with LINK. + - `_coordinatorGasOverheadNative`: Coordinator gas overhead when paying for VRF requests with native tokens (in this case, Sepolia ETH). + - `_coordinatorGasOverheadPerWord`: Additional coordinator gas overhead amount per random value ("word") requested from VRF. + + 1. Premium percentage: These variables are the premium ratio in percentage. For example, a value of 0 indicates no premium. A value of 15 indicates a 15 percent premium. + + - `_coordinatorLinkPremiumPercentage`: Premium percentage when paying with LINK. + - `_coordinatorNativePremiumPercentage`: Premium percentage when paying with native tokens. + + 1. `_keyHash`: The gas lane key hash value is the maximum gas price you are willing to pay for a request in wei. + + 1. `_maxNumWords`: This variable is the maximum number of words requested in a VRF v2.5 direct funding request. + + 1. `stalenessSeconds`: The time threshold past which the price feed is considered stale, and we must use the `fallbackWeiPerUnitLink` value instead. + + 1. `fallbackWeiPerUnitLink`: The conversion value used if the price feed is stale. + + 1. Fulfillment flat fees: Additional flat fees applied on top of the premium percentage. Currently, these parameters are both set to zero for both LINK payments and native token payments, respectively: + - `fulfillmentFlatFeeLinkDiscountPPM` + - `fulfillmentFlatFeeNativePPM` + + + +1. Click _transact_. + +1. A MetaMask popup will open. Click _Confirm_. + +### Fund the VRFV2PlusWrapper subscription + +When deployed, the _VRFV2PlusWrapper_ contract creates a new subscription and adds itself to the newly created subscription. + +1. Under _Deployed Contracts_, open the functions list of your deployed _VRFCoordinatorV2_5Mock_ contract. + +1. Click `fundSubscription` to fund the _VRFV2PlusWrapper_ subscription. Fill in your subscription ID for `_subid`, and set the `_amount` to `10000000000000000000` (10 LINK). + +1. A MetaMask popup will open. Click _Confirm_. + +### Deploy the VRF consumer contract + +_DirectFundingConsumerV2_5_ is the consuming contract that requests and directly pays the VRF service for random values. + +1. In the file explorer, open _DirectFundingConsumerV2_5.sol_. + +1. Under _DEPLOY & RUN TRANSACTIONS_, select _DirectFundingConsumerV2_5_. + + + +1. Under _DEPLOY_, fill in `_WRAPPERADDRESS_` with the deployed _VRFV2PlusWrapper_ address. + +1. Click _transact_ to deploy the _DirectFundingConsumerV2_5_ contract. + +1. A MetaMask popup will open. Click _Confirm_. + +1. Once deployed, you should see the _DirectFundingConsumerV2_5_ contract under _Deployed Contracts_. + + + +1. Note the address of the deployed contract. + +### Fund your VRF consumer contract + +Use the _LinkToken_ contract to transfer LINK to your consumer contract: + +1. Under _Deployed Contracts_, open the functions list of your deployed _LinkToken_ contract. + +1. Click _transfer_ and fill in the `_to` with your consumer contract address and `_value` with LINK tokens amount. For this example, you can set the `_value` to `10000000000000000000` (10 LINK). + + + +1. Click _transact_. + +1. A MetaMask popup will open. Click _Confirm_. + +### Request random words + +Request three random words: + +1. Under _Deployed Contracts_, open the functions list of your deployed _DirectFundingConsumerV2_5_ contract. + +1. In `requestRandomWords`, fill in `_callbackGasLimit` with `300000`, `_requestConfirmations` with `3` and `_numWords` with `3`. + + + +1. Click _transact_. + +1. A MetaMask popup will open. + + + +1. Click _Confirm_. + +1. In the Remix IDE console, read your transaction logs to find the VRF request ID. In this example, the request ID 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 the _VRFV2Wrapper_ contract address. + + + +1. Click `transact`. + +1. A MetaMask popup will open. + + + +1. Click _Confirm_. + +1. In the Remix IDE console, read your transaction logs to find the random words. + + + +### Check the results + +1. Under _Deployed Contracts_, open the functions list of your deployed _DirectFundingConsumerV2_5_ contract. + +1. Click `lastRequestId` to display the last request ID. In this example, the output is _1_. + + + +1. Click `getRequestStatus` with `_requestId` equal to `1`: + + + +1. You will get the amount paid, the status, and the random words. + + + +## Next steps + +This guide demonstrated how to test a VRF v2.5 consumer contract on your local blockchain. We made the guide on Remix IDE for learning purposes, but you can reuse the same [testing logic](#testing-logic) in other development environments. For example, you can test entirely with the Hardhat Starter Kit [RandomNumberDirectFundingConsumer unit tests](https://github.com/smartcontractkit/hardhat-starter-kit/blob/main/test/unit/RandomNumberDirectFundingConsumer.spec.js). diff --git a/src/content/vrf/v2-5/subscription/test-locally.mdx b/src/content/vrf/v2-5/subscription/test-locally.mdx index 38d565442e7..ebedd966fbd 100644 --- a/src/content/vrf/v2-5/subscription/test-locally.mdx +++ b/src/content/vrf/v2-5/subscription/test-locally.mdx @@ -43,11 +43,11 @@ 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: diff --git a/src/content/vrf/v2/direct-funding/examples/test-locally.mdx b/src/content/vrf/v2/direct-funding/examples/test-locally.mdx index 5ae14ca8b5d..78b5fc10b23 100644 --- a/src/content/vrf/v2/direct-funding/examples/test-locally.mdx +++ b/src/content/vrf/v2/direct-funding/examples/test-locally.mdx @@ -75,23 +75,23 @@ This guide will require you to finetune the gas limit when fulfilling requests. Open _VRFCoordinatorV2Mock_ and compile in Remix: - + Open _MockV3Aggregator_ and compile in Remix: - + Open _LinkToken_ and compile in Remix: - + Open _VRFV2Wrapper_ and compile in Remix: - + Open _RandomNumberDirectFundingConsumerV2_ and compile in Remix: - + Your RemixIDE file explorer should display the opened contracts: diff --git a/src/content/vrf/v2/subscription/examples/test-locally.mdx b/src/content/vrf/v2/subscription/examples/test-locally.mdx index a12659a2d58..525492d4dee 100644 --- a/src/content/vrf/v2/subscription/examples/test-locally.mdx +++ b/src/content/vrf/v2/subscription/examples/test-locally.mdx @@ -45,11 +45,11 @@ Complete the following tasks to test your VRF v2 consumer locally: Open _VRFCoordinatorV2Mock_ and compile in Remix: - + Open _VRFv2Consumer_ and compile in Remix: - + Your RemixIDE file explorer should display _VRFCoordinatorV2Mock.sol_ and _VRFv2Consumer.sol_: