-
Notifications
You must be signed in to change notification settings - Fork 46
/
Copy pathVRFConsumerV2.sol
282 lines (249 loc) · 11.6 KB
/
VRFConsumerV2.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
//SPDX-License-Identifier: MIT
/**
* @authors: [@malatrax]
* @reviewers: []
* @auditors: []
* @bounties: []
* @deployments: []
*/
pragma solidity 0.8.18;
import "./RNG.sol";
import "./VRFConsumerBaseV2.sol";
import "./interfaces/VRFCoordinatorV2Interface.sol";
import "../proxy/UUPSProxiable.sol";
// Interface to call passPhase in the callback function
interface ISortitionModule {
function passPhase() external;
function rngFallbackTimeout() external view returns (uint256);
}
/**
* @title Random Number Generator using Chainlink Verifiable Resolution Mechanism v2 on Arbitrum - Subscription Method - Consumer
* @author Simon Malatrait <[email protected]>
* @dev This contract implements the RNG standard and inherits from VRFConsumerBaseV2 to use Chainlink Verifiable Randomness Mechanism.
* @dev It allows to store the random number associated to the requests made.
* @dev Chainlink Subscription Method Documentation: https://docs.chain.link/vrf/v2/subscription
* @dev Chainlink Subscription Method Network Parameters: https://docs.chain.link/vrf/v2/subscription/supported-networks#arbitrum-mainnet
* @dev For SECURITY CONSIDERATIONS, you might also have look to: https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol
* @dev For SECURITY CONSIDERATIONS, you might also have look to: https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol
*/
contract VRFConsumerV2 is VRFConsumerBaseV2, RNG, UUPSProxiable {
// ************************************* //
// * Events * //
// ************************************* //
/**
* @dev Emitted when a request is sent to the VRF Coordinator
* @param requestId The ID of the request
* @param numWords The number of random values requested
*/
event RequestSent(uint256 indexed requestId, uint32 numWords);
/**
* Emitted when a request has been fulfilled.
* @param requestId The ID of the request
* @param randomWords The random values answering the request.
*/
event RequestFulfilled(uint256 indexed requestId, uint256[] randomWords);
// ************************************* //
// * Storage * //
// ************************************* //
address public governor;
bytes32 public keyHash;
VRFCoordinatorV2Interface public vrfCoordinator;
uint64 public subscriptionId;
uint32 public callbackGasLimit;
ISortitionModule public sortitionModule;
uint32 public numWords;
uint16 public requestConfirmations;
uint256 public lastRequestId;
RNG public randomizerRNG;
RNG public blockhashRNG;
mapping(uint256 => uint256) public requestsToRandomWords; // s_requests[requestId] = randomWord
mapping(uint256 => uint256) public requestToFallbackBlock; // Maps a Chainlink request ID with the number of the block where fallback RNG was triggered.
// ************************************* //
// * Function Modifiers * //
// ************************************* //
modifier onlyBySortitionModule() {
require(msg.sender == address(sortitionModule), "Access not allowed: SortitionModule only");
_;
}
modifier onlyByGovernor() {
require(msg.sender == governor, "Access not allowed: Governor only");
_;
}
/// @dev Constructor, initializing the implementation to reduce attack surface.
constructor() {
_disableInitializers();
}
/**
* @dev Constructs the ChainlinkRNG contract.
* @param _governor The Governor of the contract.
* @param _vrfCoordinator The address of the VRFCoordinator contract.
* @param _sortitionModule The address of the SortitionModule contract.
* @param _keyHash The gas lane key hash value - Defines the maximum gas price you are willing to pay for a request in wei (ID of the off-chain VRF job).
* @param _subscriptionId The unique identifier of the subscription used for funding requests.
* @param _requestConfirmations How many confirmations the Chainlink node should wait before responding.
* @param _callbackGasLimit The limit for how much gas to use for the callback request to the contract's fulfillRandomWords() function.
* @param _numWords How many random values to request.
* @param _randomizerRNG Address of Randomizer RNG contract.
* @param _blockhashRNG Adress of Blockhash RNG contract.
* @dev https://docs.chain.link/vrf/v2/subscription/examples/get-a-random-number#analyzing-the-contract
*/
function initialize(
address _governor,
address _vrfCoordinator,
address _sortitionModule,
bytes32 _keyHash,
uint64 _subscriptionId,
uint16 _requestConfirmations,
uint32 _callbackGasLimit,
uint32 _numWords,
RNG _randomizerRNG,
RNG _blockhashRNG
) external reinitializer(1) {
vrfBase_init(_vrfCoordinator);
governor = _governor;
vrfCoordinator = VRFCoordinatorV2Interface(_vrfCoordinator);
sortitionModule = ISortitionModule(_sortitionModule);
keyHash = _keyHash;
subscriptionId = _subscriptionId;
requestConfirmations = _requestConfirmations;
callbackGasLimit = _callbackGasLimit;
numWords = _numWords;
randomizerRNG = _randomizerRNG;
blockhashRNG = _blockhashRNG;
}
// ************************************* //
// * Governance * //
// ************************************* //
/**
* @dev Access Control to perform implementation upgrades (UUPS Proxiable)
* @dev Only the governor can perform upgrades (`onlyByGovernor`)
*/
function _authorizeUpgrade(address) internal view override onlyByGovernor {}
/**
* @dev Changes the `vrfCoordinator` storage variable.
* @param _vrfCoordinator The new value for the `vrfCoordinator` storage variable.
*/
function changeVrfCoordinator(address _vrfCoordinator) external onlyByGovernor {
vrfCoordinator = VRFCoordinatorV2Interface(_vrfCoordinator);
}
/**
* @dev Changes the `sortitionModule` storage variable.
* @param _sortitionModule The new value for the `sortitionModule` storage variable.
*/
function changeSortitionModule(address _sortitionModule) external onlyByGovernor {
sortitionModule = ISortitionModule(_sortitionModule);
}
/**
* @dev Changes the `keyHash` storage variable.
* @param _keyHash The new value for the `keyHash` storage variable.
*/
function changeKeyHash(bytes32 _keyHash) external onlyByGovernor {
keyHash = _keyHash;
}
/**
* @dev Changes the `subscriptionId` storage variable.
* @param _subscriptionId The new value for the `subscriptionId` storage variable.
*/
function changeSubscriptionId(uint64 _subscriptionId) external onlyByGovernor {
subscriptionId = _subscriptionId;
}
/**
* @dev Changes the `requestConfirmations` storage variable.
* @param _requestConfirmations The new value for the `requestConfirmations` storage variable.
*/
function changeRequestConfirmations(uint16 _requestConfirmations) external onlyByGovernor {
requestConfirmations = _requestConfirmations;
}
/**
* @dev Changes the `callbackGasLimit` storage variable.
* @param _callbackGasLimit The new value for the `callbackGasLimit` storage variable.
*/
function changeCallbackGasLimit(uint32 _callbackGasLimit) external onlyByGovernor {
callbackGasLimit = _callbackGasLimit;
}
/**
* @dev Changes the `numWords` storage variable.
* @param _numWords The new value for the `numWords` storage variable.
*/
function changeNumWord(uint32 _numWords) external onlyByGovernor {
numWords = _numWords;
}
/**
* @dev Changes the `randomizerRNG` storage variable.
* @param _randomizerRNG The new value for the `randomizerRNG` storage variable.
*/
function changeRandomizerRNG(RNG _randomizerRNG) external onlyByGovernor {
randomizerRNG = _randomizerRNG;
}
/**
* @dev Changes the `blockhashRNG` storage variable.
* @param _blockhashRNG The new value for the `blockhashRNG` storage variable.
*/
function changeBlockhashRNG(RNG _blockhashRNG) external onlyByGovernor {
blockhashRNG = _blockhashRNG;
}
// ************************************* //
// * State Modifiers * //
// ************************************* //
/**
* @dev Submit a request to the VRF Coordinator contract with the specified parameters.
* @dev Assumes the subscription is funded sufficiently; "Words" refers to unit of data in Computer Science
* Note Buffer of one requestId, as in RandomizerRNG, which should be enough with the callback function.
*/
function requestRandomness(uint256 /* _block */) external onlyBySortitionModule {
// Will revert if subscription is not set and funded.
uint256 requestId = vrfCoordinator.requestRandomWords(
keyHash,
subscriptionId,
requestConfirmations,
callbackGasLimit,
numWords
);
lastRequestId = requestId;
emit RequestSent(requestId, numWords);
}
// ************************************* //
// * Internal * //
// ************************************* //
/**
* @dev Callback function used by VRF Coordinator
* @dev Stores the random number given by the VRF Coordinator.
* @param _requestId The same request Id initially returned by `vrfCoordinator.requestRandomWords` and stored in the `lastRequestId` storage variable.
* @param _randomWords - array of random results from VRF Coordinator
*/
function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override {
requestsToRandomWords[_requestId] = _randomWords[0];
emit RequestFulfilled(_requestId, _randomWords);
sortitionModule.passPhase();
}
// ************************************* //
// * Public Views * //
// ************************************* //
/**
* @dev Get the random value associated to `lastRequestId`
* @return randomNumber The random number. If the value is not ready or has not been required it returns 0.
*/
function receiveRandomness(uint256 /* _block */) external returns (uint256 randomNumber) {
if (requestsToRandomWords[lastRequestId] == 0) {
uint256 fallbackBlock = requestToFallbackBlock[lastRequestId];
if (fallbackBlock != 0) {
if (block.number <= fallbackBlock + sortitionModule.rngFallbackTimeout()) {
randomNumber = randomizerRNG.receiveRandomness(0);
} else {
// We can use fallback block since it already gives enough distance from the block that first requested randomness.
// Note that if RNG fallback timeout is set higher or close than 256 blocks then blockhash will use the hash of the previous block instead.
randomNumber = blockhashRNG.receiveRandomness(fallbackBlock);
}
}
} else {
randomNumber = requestsToRandomWords[lastRequestId];
}
}
function receiveRandomnessFallback(uint256 _block) external onlyBySortitionModule {
if (requestToFallbackBlock[lastRequestId] == 0) {
requestToFallbackBlock[lastRequestId] = _block;
// Block number is irrelevant for Randomizer.
randomizerRNG.requestRandomness(0);
}
}
}