Reputation: 11
I want to generate 10000 random numbers by using chainlink VRF V2,but the max NUM_WORDS is 500 in one request,and it fails every time! What should I do? My account has enough LINK,but what I have done below does't work!
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
contract VRFCoordinatorTest is VRFConsumerBaseV2 {
// Chainlink VRF Variables
address vrfCoordinatorV2 = 0x6168499c0cFfCaCD319c818142124B7A15E857ab;
uint64 subscriptionId = 14422;
bytes32 gasLane = 0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc;
uint32 public callbackGasLimit = 2500000; //already max gaslimit
//network coordinator
VRFCoordinatorV2Interface private immutable _vrfCoordinator;
// The default is 3, but you can set this higher.
uint16 public REQUEST_CONFIRMATIONS = 10;
// retrieve NUM_WORDS random values in one request.
uint32 public NUM_WORDS = 500;
//keep the randomWords from fulfillRandomWords() function.
uint256[][] public _randomWords = new uint256[][](0);
//uint256[] public _randomWords;
event RequestedRandomWords(uint256 requestId ,address requester);
constructor() VRFConsumerBaseV2(vrfCoordinatorV2) {
_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorV2);
}
function fulfillRandomWords(uint256, uint256[] memory randomWords) internal override{
_randomWords.push(randomWords);
// _randomWords = randomWords;
}
function requestRandomWords()public{
uint256 requestId = _vrfCoordinator.requestRandomWords(
gasLane,
subscriptionId,
REQUEST_CONFIRMATIONS,
callbackGasLimit,
NUM_WORDS
);
emit RequestedRandomWords(requestId, msg.sender);
}
function set_REQUEST_CONFIRMATIONS(uint16 comf)public{
REQUEST_CONFIRMATIONS = comf;
}
function set_NUM_WORDS(uint32 num)public{
NUM_WORDS = num;
}
function set_gasLimit(uint32 gasl) public{
callbackGasLimit = gasl;
}
function getMaxLengthAndNum()public view returns(uint256,uint256){
uint256 lth = _randomWords.length;
uint256[] memory tmpArray = _randomWords[lth-1];
uint256 lastNum = tmpArray[tmpArray.length-1];
//uint256 maxNum = _randomWords[_randomWords.length-1];
return (lth,lastNum);
}
}
Upvotes: 1
Views: 947
Reputation: 141
I see that you are testing on Rinkeby. I advise you to test on Goerli. Rinkeby is marked as deprecated (cf. https://docs.chain.link/docs/vrf/v2/supported-networks/).
I've tested your contract and remarked that fulfillRandomWords
runs out of gas. In fact, The maximum number of random words that can be received per request is 500, and the maximum gas limit supported for your callback function is 2,500,000. Therefore, you must ensure that the callback function will not run out of gas (meaning: your callback function cannot consume more than 2,500,000 of gas).
In your example, you are trying to store 500 random words. Setting a value from non-zero to zero takes about 20,000 of gas (cf. Ethereum yellow paper Appendix H). With 2,500,000 of gas, you can store up to 125 words. And that’s why the attached contract will not work with NUM_WORDS=500 (fulfillRandomWords runs out of gas).
If you want to store 10001 random values, I’d suggest making multiple requests. For instance: inside requestRandomWords
you can loop over
vrfCoordinator.requestRandomWords
multiple times (e.g.: with NUM_WORDS=100 for 100 runs. and 1 for the last run).
Your callback function will be called multiple times (=number of requestIds) so make sure to implement a logic that waits for all the requests to be fulfilled (e.g., you can update a state variable s_requestsFulfilled
each time a request is fulfilled).
Here is an example that you can test on Goerli, I modified your example (Important remark: this is a non-audited code with hardcoded values. Do not use it as a reference for production code. Please test it/change it according to your needs)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
contract VRFCoordinatorTest_2 is VRFConsumerBaseV2 {
// Chainlink VRF Variables
address vrfCoordinatorV2 = 0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D;
uint64 subscriptionId = 443;
bytes32 gasLane = 0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15;
uint32 public callbackGasLimit = 2500000; //already max gaslimit
uint256 public requestsFulfilled = 0; // count number of request fulfilled
//network coordinator
VRFCoordinatorV2Interface private immutable _vrfCoordinator;
// The default is 3, but you can set this higher.
uint16 public REQUEST_CONFIRMATIONS = 3;
//keep the randomWords from fulfillRandomWords() function.
struct RequestStatus {
bool fulfilled; // whether the request has been successfully fulfilled
bool exists; // whether a requestId exists
uint256[] randomWords;
}
mapping(uint256 => RequestStatus) public s_requests;
uint256[] public requestIds;
//uint256[] public _randomWords;
event ReceivedRandomWords(uint256 requestId ,uint256[] randomWords);
event RequestedRandomWords(uint256 requestId ,address requester);
event AllRequestsFulfilled();
constructor() VRFConsumerBaseV2(vrfCoordinatorV2) {
_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorV2);
}
function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override{
require(s_requests[_requestId].exists, 'request not found');
require(!s_requests[_requestId].fulfilled, 'request already fulfilled');
requestsFulfilled++;
s_requests[_requestId].fulfilled = true;
s_requests[_requestId].randomWords = _randomWords;
emit ReceivedRandomWords(_requestId,_randomWords);
if(requestsFulfilled==101){
emit AllRequestsFulfilled();
}
}
function requestRandomWords()public{
uint32 numWords = 100;
for(uint256 i=0;i<101;i++){
if(i==100){
numWords=1;
}
uint256 requestId = _vrfCoordinator.requestRandomWords(
gasLane,
subscriptionId,
REQUEST_CONFIRMATIONS,
callbackGasLimit,
numWords
);
s_requests[requestId]=RequestStatus({randomWords: new uint256[](0), exists: true, fulfilled: false});
requestIds.push(requestId);
emit RequestedRandomWords(requestId, msg.sender);
}
}
function getRequestStatus(uint256 requestId) external view returns (bool fulfilled, uint256[] memory randomWords) {
require(s_requests[requestId].exists, 'request not found');
RequestStatus memory request = s_requests[requestId];
return (request.fulfilled, request.randomWords);
}
function getRandomWordsAt(uint256 requestId,uint32 index) external view returns (uint256) {
require(s_requests[requestId].exists, 'request not found');
RequestStatus memory request = s_requests[requestId];
return request.randomWords[index];
}
function getRequestIdsLength() external view returns (uint256){
return requestIds.length;
}
function getRandomWords(uint256 requestId) external view returns (uint256[] memory){
require(s_requests[requestId].exists, 'request not found');
return s_requests[requestId].randomWords;
}
}
Upvotes: 5