
Call the VRF
本文最后更新于 2024-05-10,本文发布时间距今超过 90 天, 文章内容可能已经过时。最新内容请以官方内容为准
Call the VRF By subscription method
Description
This is a sample record that calls the VRF contract to get a random number.
Steps
Deploy the VRF contract
- Go to the vrf.Chain.link website and create a new subsription.
- Get the SubId of subsription.
- Fund the subscription with Link/ETH. (Native payment is supported for VRF2.5 now)
- Deploy the VRF contract to the sepolia testnet.
- Set the SubId, Coordinator address, and the gas lane.
- Get the contract address from the deployment transaction.
- Add the contract address as consumer to the chainlink VRF subscription.
Call the VRF contract
- Go back to contract to call the
requestRandomWords
function.- numWords: The number of random words to generate.
- nativePayment: Use Link or native token (ETH) as payment.
- True: Use Link as payment.
- False: Use native token (ETH) as payment.
- 因为我这个 contract 用的是 subscription 的方式,所以 Link/ETH 我已经充到了 subscription 里了,所以这里就不用你们给 Token 了。只用给 Gas Fee 来进行 Tx.
- 另外如果长时间没有得到 randomWords, 可以去 chainlink explorer 看看有没有报错。
- 如果是 Out of gas, 你们可以尝试增加 Gas limit.
- Set the gas limit to 500_000 or higher by setCallbackGaslimit.
- Call the
lastRequestId
function to get the latest request ID. - Call the
getRequestStatus
function to get the status of request ID.- fulfilled:
- True for the request has been fulfilled with Chainlink VRF.
- False for the request is still pending.
- randomWords:
- The random words generated by the VRF contract.
- Each random word is separated by a comma.
Call records
VRF contract address: 0x82E674688842b2d88D3968A932291Ef68107aC81
VRF2.5 SubId: 24513078928439205270705449808212503718043530537999925440364011035234675106258
Link to subscription
Define Cost as: (Minimum Subscription balance/ Actual cost of token)
Link & ETH Balance | Gas limit | Random numberWorks | Request ID | Request time | Native Payment | Link cost | ETH cost | Success | Reason |
---|---|---|---|---|---|---|---|---|---|
50 LINK, 2 ETH | 100_000 | 1 | 64640565757539305184616226494538017610428182412170512002707068032277042614977 | 25s | False | 1.9727367053959264 LINK / 1.2635988206642421 LINK | 0 | True | None |
48.73640117933575 LINK, 2 ETH | 100_000 | 3 | 23509800222172998149588604742646403030500326947966701635589066827235529729009 | 60s | True | 0 | 0.009 ETH / 0.005555675699588659 ETH | False | Out of gas |
48.73640117933575 LINK, 1.9944443243004113 ETH | 500_000 | 3 | 2847458936441791565998258733460833143987747806603640619668152855622424462984 | 60s | True | 0 | 0.021 ETH / 0.00531652985517589 ETH | True | None |
48.73640117933575 LINK, 1.9891277944452355 ETH | 500_000 | 6 | 61788080077363145477311103163736935217766399414760631692212034910474595590988 | 45s | False | 4.603052312590495 LINK / 1.7460505752181679 LINK | 0 | True | None |
46.99035060411759 LINK, 1.9891277944452355 ETH | 500_000 | 10 | 112282426175383503677443689536875535439589644453268384906743245662470004949219 | 120s | True | 0 | 0.021 ETH / 0.013168056 ETH | True | None |
46.99035060411759 LINK, 1.9759597384452354 ETH | 500_000 | 10 | 4670612677263360579520229416748962361626139289621463888525616048998534866685 | 600s | False | 4.596683277304416 LINK / 2.959053132874994 LINK | 0 | True | None |
VRF Contract
// SPDX-License-Identifier: MIT
// Layout of Contract:
// version
// imports
// errors
// interfaces, libraries, contracts
// Type declarations
// State variables
// Events
// Modifiers
// Functions
// Layout of Functions:
// constructor
// receive function (if exists)
// fallback function (if exists)
// external
// public
// internal
// private
// internal & private view & pure functions
// external & public view & pure functions
pragma solidity ^0.8.6;
import {IVRFCoordinatorV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/interfaces/IVRFCoordinatorV2Plus.sol";
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";
/**
* Request testnet LINK and ETH here: https://faucets.chain.link/
* Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
*/
/* Errors */
error VRFConsumerV2_5__NoRequestFound();
error VRFConsumerV2_5__SubscriptionNotFunded();
error VRFConsumerV2_5__OutofRange();
error VRFConsumerV2_5__CollaboratorExists();
error VRFConsumerV2_5__CollaboratorNotExists();
/* Interfaces, Libraries, and Contracts */
contract VRFConsumerV2_5 is VRFConsumerBaseV2Plus {
event RequestSent(uint256 indexed requestId, uint32 numWords);
event RequestFulfilled(uint256 indexed requestId, uint256[] randomWords);
event CollaboratorAdded(address indexed collaborator);
event CollaboratorRemoved(address indexed collaborator);
modifier onlyOwnerOrCollaborators() {
if (msg.sender != owner() && s_collaborators[msg.sender]) {
revert VRFConsumerV2_5__CollaboratorNotExists();
}
_;
}
// The RequestStatus struct stores the status of a request.
struct RequestStatus {
bool fulfilled; // whether the request has been successfully fulfilled
bool exists; // whether a requestId exists
uint256[] randomWords;
}
IVRFCoordinatorV2Plus COORDINATOR;
// Your subscription ID.
uint256 s_subscriptionId;
// The Coordinators address.
/**
* HARDCODED FOR SEPOLIA
* COORDINATOR: 0x9DdfaCa8183c41ad55329BdeeD9F6A8d53168B1B
*/
address immutable s_coordinatorAddr;
// 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/v2-5/supported-networks
// 30 gwei keyhash: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae
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 private callbackGasLimit = 100_000;
// The default is 3, but you can set this higher.
// Cannot exceed VRFCoordinatorV2_5.MAX_REQUEST_CONFIRMATIONS. [3, 200].
uint16 private requestConfirmations = 3;
// For this example, retrieve 2 random values in one request.
// Cannot exceed VRFCoordinatorV2_5.MAX_NUM_WORDS. 500
uint32 private numWords = 2;
mapping(uint256 => RequestStatus) public s_requests; /* requestId --> requestStatus */
// past requests Id.
uint256[] public requestIds;
uint256 public lastRequestId;
// Collaborators can be added or removed by the owner.
// Collaborators can request random words and receive the results.
mapping(address => bool) public s_collaborators;
constructor(uint256 subscriptionId, address coordinatorAddr, bytes32 keyhash)
VRFConsumerBaseV2Plus(coordinatorAddr)
{
s_subscriptionId = subscriptionId;
s_coordinatorAddr = coordinatorAddr;
s_keyhash = keyhash;
COORDINATOR = IVRFCoordinatorV2Plus(s_coordinatorAddr);
}
// Assumes the subscription is funded sufficiently.
function requestRandomWords(uint32 _numWords, bool _nativePayment)
external
onlyOwnerOrCollaborators
returns (uint256 requestId)
{
// Will revert if subscription is not set and funded.
// To enable payment in native tokens, set nativePayment to true.
if (_numWords > 500) {
// "Number of words requested is out of range. Cannot exceed 500."
revert VRFConsumerV2_5__OutofRange();
}
requestId = COORDINATOR.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: s_keyhash,
subId: s_subscriptionId,
requestConfirmations: requestConfirmations,
callbackGasLimit: callbackGasLimit,
numWords: _numWords,
extraArgs: VRFV2PlusClient._argsToBytes(VRFV2PlusClient.ExtraArgsV1({nativePayment: _nativePayment}))
})
);
s_requests[requestId] = RequestStatus({randomWords: new uint256[](0), exists: true, fulfilled: false});
requestIds.push(requestId);
lastRequestId = requestId;
numWords = _numWords;
emit RequestSent(requestId, _numWords);
return requestId;
}
function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override {
if (!s_requests[_requestId].exists) {
revert VRFConsumerV2_5__NoRequestFound();
}
s_requests[_requestId].fulfilled = true;
s_requests[_requestId].randomWords = _randomWords;
emit RequestFulfilled(_requestId, _randomWords);
}
function getRequestStatus(uint256 _requestId)
external
view
returns (bool fulfilled, uint256[] memory randomWords)
{
if (!s_requests[_requestId].exists) {
revert VRFConsumerV2_5__NoRequestFound();
}
RequestStatus memory request = s_requests[_requestId];
return (request.fulfilled, request.randomWords);
}
/* Functions */
function setRequestConfirmations(uint16 _requestConfirmations) external onlyOwnerOrCollaborators {
requestConfirmations = _requestConfirmations;
}
function setCallbackGasLimit(uint32 _callbackGasLimit) external onlyOwnerOrCollaborators {
callbackGasLimit = _callbackGasLimit;
}
function getRequestConfirmations() external view returns (uint16) {
return requestConfirmations;
}
function getCallbackGasLimit() external view returns (uint32) {
return callbackGasLimit;
}
function getLastRequestNumWords() external view returns (uint32) {
return numWords;
}
/* Collaborators */
function addCollaborator(address _collaborator) external onlyOwnerOrCollaborators {
if (s_collaborators[_collaborator]) {
revert VRFConsumerV2_5__CollaboratorExists();
}
s_collaborators[_collaborator] = true;
emit CollaboratorAdded(_collaborator);
}
function addCollaborators(address[] memory _collaborators) external onlyOwnerOrCollaborators {
for (uint256 i = 0; i < _collaborators.length; i++) {
address collaborator = _collaborators[i];
// require(!s_collaborators[collaborator], "Collaborator already exists");
if (s_collaborators[collaborator]) {
continue;
}
s_collaborators[collaborator] = true;
emit CollaboratorAdded(collaborator);
}
}
function removeCollaborator(address _collaborator) external onlyOwnerOrCollaborators {
if (!s_collaborators[_collaborator]) {
revert VRFConsumerV2_5__CollaboratorNotExists();
}
delete s_collaborators[_collaborator];
emit CollaboratorRemoved(_collaborator);
}
function removeCollaborators(address[] memory _collaborators) external onlyOwnerOrCollaborators {
for (uint256 i = 0; i < _collaborators.length; i++) {
address collaborator = _collaborators[i];
// require(!s_collaborators[collaborator], "Collaborator already exists");
if (!s_collaborators[collaborator]) {
continue;
}
delete s_collaborators[collaborator];
emit CollaboratorRemoved(collaborator);
}
}
}
ABI
[
{
"inputs": [],
"name": "acceptOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_collaborator",
"type": "address"
}
],
"name": "addCollaborator",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "_collaborators",
"type": "address[]"
}
],
"name": "addCollaborators",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "subscriptionId",
"type": "uint256"
},
{
"internalType": "address",
"name": "coordinatorAddr",
"type": "address"
},
{
"internalType": "bytes32",
"name": "keyhash",
"type": "bytes32"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address",
"name": "have",
"type": "address"
},
{
"internalType": "address",
"name": "want",
"type": "address"
}
],
"name": "OnlyCoordinatorCanFulfill",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "have",
"type": "address"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "coordinator",
"type": "address"
}
],
"name": "OnlyOwnerOrCoordinator",
"type": "error"
},
{
"inputs": [],
"name": "VRFConsumerV2_5__CollaboratorExists",
"type": "error"
},
{
"inputs": [],
"name": "VRFConsumerV2_5__CollaboratorNotExists",
"type": "error"
},
{
"inputs": [],
"name": "VRFConsumerV2_5__NoRequestFound",
"type": "error"
},
{
"inputs": [],
"name": "VRFConsumerV2_5__OutofRange",
"type": "error"
},
{
"inputs": [],
"name": "ZeroAddress",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "collaborator",
"type": "address"
}
],
"name": "CollaboratorAdded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "collaborator",
"type": "address"
}
],
"name": "CollaboratorRemoved",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "vrfCoordinator",
"type": "address"
}
],
"name": "CoordinatorSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
}
],
"name": "OwnershipTransferRequested",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "requestId",
"type": "uint256"
},
{
"internalType": "uint256[]",
"name": "randomWords",
"type": "uint256[]"
}
],
"name": "rawFulfillRandomWords",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_collaborator",
"type": "address"
}
],
"name": "removeCollaborator",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "_collaborators",
"type": "address[]"
}
],
"name": "removeCollaborators",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "requestId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256[]",
"name": "randomWords",
"type": "uint256[]"
}
],
"name": "RequestFulfilled",
"type": "event"
},
{
"inputs": [
{
"internalType": "uint32",
"name": "_numWords",
"type": "uint32"
},
{
"internalType": "bool",
"name": "_nativePayment",
"type": "bool"
}
],
"name": "requestRandomWords",
"outputs": [
{
"internalType": "uint256",
"name": "requestId",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "requestId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint32",
"name": "numWords",
"type": "uint32"
}
],
"name": "RequestSent",
"type": "event"
},
{
"inputs": [
{
"internalType": "uint32",
"name": "_callbackGasLimit",
"type": "uint32"
}
],
"name": "setCallbackGasLimit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_vrfCoordinator",
"type": "address"
}
],
"name": "setCoordinator",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint16",
"name": "_requestConfirmations",
"type": "uint16"
}
],
"name": "setRequestConfirmations",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "getCallbackGasLimit",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getLastRequestNumWords",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getRequestConfirmations",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_requestId",
"type": "uint256"
}
],
"name": "getRequestStatus",
"outputs": [
{
"internalType": "bool",
"name": "fulfilled",
"type": "bool"
},
{
"internalType": "uint256[]",
"name": "randomWords",
"type": "uint256[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "lastRequestId",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "requestIds",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "s_collaborators",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "s_requests",
"outputs": [
{
"internalType": "bool",
"name": "fulfilled",
"type": "bool"
},
{
"internalType": "bool",
"name": "exists",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "s_vrfCoordinator",
"outputs": [
{
"internalType": "contract IVRFCoordinatorV2Plus",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
]
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 Unic
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果