Skip to content

Commit d0819ba

Browse files
dwightjlthedriftofwordskhadni
authored
VRF 2.5 (#1917)
* VRF 2.5 Co-authored-by: Karim H. <[email protected]> Co-authored-by: Dwight Lyle <[email protected]> Co-authored-by: Crystal Gomes <[email protected]> * Empty commit to bump builds * Migration page - minor corrections * Add release note * Address more review comments and fix a link --------- Co-authored-by: thedriftofwords <[email protected]> Co-authored-by: Karim H. <[email protected]> Co-authored-by: Crystal Gomes <[email protected]>
1 parent 1c048f0 commit d0819ba

20 files changed

+2819
-38
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
// SPDX-License-Identifier: MIT
2+
// An example of a consumer contract that directly pays for each request.
3+
pragma solidity 0.8.19;
4+
5+
import {ConfirmedOwner} from "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol";
6+
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol";
7+
import {VRFV2PlusClient} from "@chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";
8+
9+
/**
10+
* Request testnet LINK and ETH here: https://faucets.chain.link/
11+
* Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
12+
*/
13+
14+
/**
15+
* THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
16+
* THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
17+
* DO NOT USE THIS CODE IN PRODUCTION.
18+
*/
19+
20+
/**
21+
* Import IVRFV2PlusWrapper which is not yet available in the chainlink/contracts package.
22+
* https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/dev/interfaces/IVRFV2PlusWrapper.sol
23+
*/
24+
25+
interface IVRFV2PlusWrapper {
26+
/**
27+
* @return the request ID of the most recent VRF V2 request made by this wrapper. This should only
28+
* be relied option within the same transaction that the request was made.
29+
*/
30+
function lastRequestId() external view returns (uint256);
31+
32+
/**
33+
* @notice Calculates the price of a VRF request with the given callbackGasLimit at the current
34+
* @notice block.
35+
*
36+
* @dev This function relies on the transaction gas price which is not automatically set during
37+
* @dev simulation. To estimate the price at a specific gas price, use the estimatePrice function.
38+
*
39+
* @param _callbackGasLimit is the gas limit used to estimate the price.
40+
* @param _numWords is the number of words to request.
41+
*/
42+
function calculateRequestPrice(
43+
uint32 _callbackGasLimit,
44+
uint32 _numWords
45+
) external view returns (uint256);
46+
47+
/**
48+
* @notice Calculates the price of a VRF request in native with the given callbackGasLimit at the current
49+
* @notice block.
50+
*
51+
* @dev This function relies on the transaction gas price which is not automatically set during
52+
* @dev simulation. To estimate the price at a specific gas price, use the estimatePrice function.
53+
*
54+
* @param _callbackGasLimit is the gas limit used to estimate the price.
55+
* @param _numWords is the number of words to request.
56+
*/
57+
function calculateRequestPriceNative(
58+
uint32 _callbackGasLimit,
59+
uint32 _numWords
60+
) external view returns (uint256);
61+
62+
/**
63+
* @notice Estimates the price of a VRF request with a specific gas limit and gas price.
64+
*
65+
* @dev This is a convenience function that can be called in simulation to better understand
66+
* @dev pricing.
67+
*
68+
* @param _callbackGasLimit is the gas limit used to estimate the price.
69+
* @param _numWords is the number of words to request.
70+
* @param _requestGasPriceWei is the gas price in wei used for the estimation.
71+
*/
72+
function estimateRequestPrice(
73+
uint32 _callbackGasLimit,
74+
uint32 _numWords,
75+
uint256 _requestGasPriceWei
76+
) external view returns (uint256);
77+
78+
/**
79+
* @notice Estimates the price of a VRF request in native with a specific gas limit and gas price.
80+
*
81+
* @dev This is a convenience function that can be called in simulation to better understand
82+
* @dev pricing.
83+
*
84+
* @param _callbackGasLimit is the gas limit used to estimate the price.
85+
* @param _numWords is the number of words to request.
86+
* @param _requestGasPriceWei is the gas price in wei used for the estimation.
87+
*/
88+
function estimateRequestPriceNative(
89+
uint32 _callbackGasLimit,
90+
uint32 _numWords,
91+
uint256 _requestGasPriceWei
92+
) external view returns (uint256);
93+
94+
/**
95+
* @notice Requests randomness from the VRF V2 wrapper, paying in native token.
96+
*
97+
* @param _callbackGasLimit is the gas limit for the request.
98+
* @param _requestConfirmations number of request confirmations to wait before serving a request.
99+
* @param _numWords is the number of words to request.
100+
*/
101+
function requestRandomWordsInNative(
102+
uint32 _callbackGasLimit,
103+
uint16 _requestConfirmations,
104+
uint32 _numWords,
105+
bytes calldata extraArgs
106+
) external payable returns (uint256 requestId);
107+
108+
function link() external view returns (address);
109+
110+
function linkNativeFeed() external view returns (address);
111+
}
112+
113+
/**
114+
* Import VRFV2PlusWrapperConsumerBase which is not yet available in the chainlink/contracts package.
115+
* https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapperConsumerBase.sol
116+
*/
117+
118+
abstract contract VRFV2PlusWrapperConsumerBase {
119+
error OnlyVRFWrapperCanFulfill(address have, address want);
120+
121+
LinkTokenInterface internal immutable i_linkToken;
122+
IVRFV2PlusWrapper public immutable i_vrfV2PlusWrapper;
123+
124+
/**
125+
* @param _vrfV2PlusWrapper is the address of the VRFV2Wrapper contract
126+
*/
127+
constructor(address _vrfV2PlusWrapper) {
128+
IVRFV2PlusWrapper vrfV2PlusWrapper = IVRFV2PlusWrapper(
129+
_vrfV2PlusWrapper
130+
);
131+
132+
i_linkToken = LinkTokenInterface(vrfV2PlusWrapper.link());
133+
i_vrfV2PlusWrapper = vrfV2PlusWrapper;
134+
}
135+
136+
/**
137+
* @dev Requests randomness from the VRF V2+ wrapper.
138+
*
139+
* @param _callbackGasLimit is the gas limit that should be used when calling the consumer's
140+
* fulfillRandomWords function.
141+
* @param _requestConfirmations is the number of confirmations to wait before fulfilling the
142+
* request. A higher number of confirmations increases security by reducing the likelihood
143+
* that a chain re-org changes a published randomness outcome.
144+
* @param _numWords is the number of random words to request.
145+
*
146+
* @return requestId is the VRF V2+ request ID of the newly created randomness request.
147+
*/
148+
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
149+
function requestRandomness(
150+
uint32 _callbackGasLimit,
151+
uint16 _requestConfirmations,
152+
uint32 _numWords,
153+
bytes memory extraArgs
154+
) internal returns (uint256 requestId, uint256 reqPrice) {
155+
reqPrice = i_vrfV2PlusWrapper.calculateRequestPrice(
156+
_callbackGasLimit,
157+
_numWords
158+
);
159+
i_linkToken.transferAndCall(
160+
address(i_vrfV2PlusWrapper),
161+
reqPrice,
162+
abi.encode(
163+
_callbackGasLimit,
164+
_requestConfirmations,
165+
_numWords,
166+
extraArgs
167+
)
168+
);
169+
return (i_vrfV2PlusWrapper.lastRequestId(), reqPrice);
170+
}
171+
172+
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
173+
function requestRandomnessPayInNative(
174+
uint32 _callbackGasLimit,
175+
uint16 _requestConfirmations,
176+
uint32 _numWords,
177+
bytes memory extraArgs
178+
) internal returns (uint256 requestId, uint256 requestPrice) {
179+
requestPrice = i_vrfV2PlusWrapper.calculateRequestPriceNative(
180+
_callbackGasLimit,
181+
_numWords
182+
);
183+
return (
184+
i_vrfV2PlusWrapper.requestRandomWordsInNative{value: requestPrice}(
185+
_callbackGasLimit,
186+
_requestConfirmations,
187+
_numWords,
188+
extraArgs
189+
),
190+
requestPrice
191+
);
192+
}
193+
194+
/**
195+
* @notice fulfillRandomWords handles the VRF V2 wrapper response. The consuming contract must
196+
* @notice implement it.
197+
*
198+
* @param _requestId is the VRF V2 request ID.
199+
* @param _randomWords is the randomness result.
200+
*/
201+
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
202+
function fulfillRandomWords(
203+
uint256 _requestId,
204+
uint256[] memory _randomWords
205+
) internal virtual;
206+
207+
function rawFulfillRandomWords(
208+
uint256 _requestId,
209+
uint256[] memory _randomWords
210+
) external {
211+
address vrfWrapperAddr = address(i_vrfV2PlusWrapper);
212+
if (msg.sender != vrfWrapperAddr) {
213+
revert OnlyVRFWrapperCanFulfill(msg.sender, vrfWrapperAddr);
214+
}
215+
fulfillRandomWords(_requestId, _randomWords);
216+
}
217+
218+
/// @notice getBalance returns the native balance of the consumer contract
219+
function getBalance() public view returns (uint256) {
220+
return address(this).balance;
221+
}
222+
223+
/// @notice getLinkToken returns the link token contract
224+
function getLinkToken() public view returns (LinkTokenInterface) {
225+
return i_linkToken;
226+
}
227+
}
228+
229+
contract DirectFundingConsumer is VRFV2PlusWrapperConsumerBase, ConfirmedOwner {
230+
event RequestSent(uint256 requestId, uint32 numWords);
231+
event RequestFulfilled(
232+
uint256 requestId,
233+
uint256[] randomWords,
234+
uint256 payment
235+
);
236+
237+
struct RequestStatus {
238+
uint256 paid; // amount paid in link
239+
bool fulfilled; // whether the request has been successfully fulfilled
240+
uint256[] randomWords;
241+
}
242+
mapping(uint256 => RequestStatus)
243+
public s_requests; /* requestId --> requestStatus */
244+
245+
// past requests Id.
246+
uint256[] public requestIds;
247+
uint256 public lastRequestId;
248+
249+
// Depends on the number of requested values that you want sent to the
250+
// fulfillRandomWords() function. Test and adjust
251+
// this limit based on the network that you select, the size of the request,
252+
// and the processing of the callback request in the fulfillRandomWords()
253+
// function.
254+
uint32 callbackGasLimit = 100000;
255+
256+
// The default is 3, but you can set this higher.
257+
uint16 requestConfirmations = 3;
258+
259+
// For this example, retrieve 2 random values in one request.
260+
// Cannot exceed VRFV2Wrapper.getConfig().maxNumWords.
261+
uint32 numWords = 2;
262+
263+
// Address LINK - hardcoded for Arbitrum Sepolia
264+
address linkAddress = 0xb1D4538B4571d411F07960EF2838Ce337FE1E80E;
265+
266+
// address WRAPPER - hardcoded for Arbitrum Sepolia
267+
address wrapperAddress = 0x29576aB8152A09b9DC634804e4aDE73dA1f3a3CC;
268+
269+
constructor()
270+
ConfirmedOwner(msg.sender)
271+
VRFV2PlusWrapperConsumerBase(wrapperAddress)
272+
{}
273+
274+
function requestRandomWords() external onlyOwner returns (uint256) {
275+
bytes memory extraArgs = VRFV2PlusClient._argsToBytes(
276+
VRFV2PlusClient.ExtraArgsV1({nativePayment: false})
277+
);
278+
(uint256 requestId, uint256 reqPrice) = requestRandomness(
279+
callbackGasLimit,
280+
requestConfirmations,
281+
numWords,
282+
extraArgs
283+
);
284+
s_requests[requestId] = RequestStatus({
285+
paid: reqPrice,
286+
randomWords: new uint256[](0),
287+
fulfilled: false
288+
});
289+
requestIds.push(requestId);
290+
lastRequestId = requestId;
291+
emit RequestSent(requestId, numWords);
292+
return requestId;
293+
}
294+
295+
function fulfillRandomWords(
296+
uint256 _requestId,
297+
uint256[] memory _randomWords
298+
) internal override {
299+
require(s_requests[_requestId].paid > 0, "request not found");
300+
s_requests[_requestId].fulfilled = true;
301+
s_requests[_requestId].randomWords = _randomWords;
302+
emit RequestFulfilled(
303+
_requestId,
304+
_randomWords,
305+
s_requests[_requestId].paid
306+
);
307+
}
308+
309+
function getRequestStatus(
310+
uint256 _requestId
311+
)
312+
external
313+
view
314+
returns (uint256 paid, bool fulfilled, uint256[] memory randomWords)
315+
{
316+
require(s_requests[_requestId].paid > 0, "request not found");
317+
RequestStatus memory request = s_requests[_requestId];
318+
return (request.paid, request.fulfilled, request.randomWords);
319+
}
320+
321+
/**
322+
* Allow withdraw of Link tokens from the contract
323+
*/
324+
function withdrawLink() public onlyOwner {
325+
LinkTokenInterface link = LinkTokenInterface(linkAddress);
326+
require(
327+
link.transfer(msg.sender, link.balanceOf(address(this))),
328+
"Unable to transfer"
329+
);
330+
}
331+
}

0 commit comments

Comments
 (0)