-
Notifications
You must be signed in to change notification settings - Fork 0
/
EeseeRandom.sol
137 lines (112 loc) · 5.53 KB
/
EeseeRandom.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
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import "@chainlink/contracts/src/v0.8/AutomationCompatible.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/IEeseeRandom.sol";
import "../libraries/RandomArray.sol";
contract EeseeRandom is IEeseeRandom, AutomationCompatibleInterface, VRFConsumerBaseV2 {
using RandomArray for Random[];
using SafeERC20 for IERC20;
///@dev Random words and timestamps received from Chainlink.
Random[] public random;
///@dev Access manager for Eesee contract ecosystem.
IEeseeAccessManager public immutable accessManager;
///@dev Admin role af defined in {accessManager}.
bytes32 private immutable ADMIN_ROLE;
///@dev PERFORM_UPKEEP_ROLE role af defined in {accessManager}.
bytes32 private constant PERFORM_UPKEEP_ROLE = keccak256("PERFORM_UPKEEP_ROLE");
///@dev Last performUpkeep call timestamp.
uint64 public performUpkeepTimestamp;
///@dev Chainlink VRF V2 subscription ID.
uint64 immutable public subscriptionID;
///@dev Chainlink Keeper performUpkeep() call frequency.
uint32 public automationInterval = 12 hours;
///@dev In case Chainlink VRF request fails to get delivered 24 hours after the lot was closed, unlock Reclaim functions.
uint48 public constant returnInterval = 24 hours;
///@dev Chainlink VRF V2 gas limit to call fulfillRandomWords().
uint32 immutable private callbackGasLimit;
///@dev Chainlink VRF V2 request confirmations.
uint16 immutable private minimumRequestConfirmations;
///@dev Chainlink VRF V2 coordinator.
VRFCoordinatorV2Interface immutable public vrfCoordinator;
///@dev Chainlink VRF V2 key hash to call requestRandomWords() with.
bytes32 immutable private keyHash;
modifier onlyRole(bytes32 role){
if(!accessManager.hasRole(role, msg.sender)) revert CallerNotAuthorized();
_;
}
constructor(
ChainlinkContructorArgs memory chainlinkArgs,
IEeseeAccessManager _accessManager
) VRFConsumerBaseV2(chainlinkArgs.vrfCoordinator) {
if(address(_accessManager) == address(0)) revert InvalidAccessManager();
// ChainLink stuff. Create subscription for VRF V2.
vrfCoordinator = VRFCoordinatorV2Interface(chainlinkArgs.vrfCoordinator);
subscriptionID = vrfCoordinator.createSubscription();
vrfCoordinator.addConsumer(subscriptionID, address(this));
keyHash = chainlinkArgs.keyHash;
minimumRequestConfirmations = chainlinkArgs.minimumRequestConfirmations;
callbackGasLimit = chainlinkArgs.callbackGasLimit;
accessManager = _accessManager;
ADMIN_ROLE = _accessManager.ADMIN_ROLE();
}
// ============ External Methods ============
/**
* @dev Calls Chainlink's requestRandomWords.
*/
function performUpkeep(bytes calldata /*performData*/) external override {
(bool upkeepNeeded, ) = checkUpkeep("");
if(!upkeepNeeded) revert UpkeepNotNeeded();
_performUpkeep();
}
/**
* @dev This function is called by Chainlink. Writes random word to storage. Emits {FulfillRandomWords} event.
* @param randomWords - Random values sent by Chainlink.
*/
function fulfillRandomWords(uint256 /*requestID*/, uint256[] memory randomWords) internal override {
uint256 randomLength = random.length;
unchecked { if(randomLength > 0 && random[randomLength - 1].timestamp == block.timestamp) revert CallOnTheSameBlock(); }
random.push(Random({
word: randomWords[0],
timestamp: block.timestamp
}));
emit FulfillRandomWords(randomWords[0]);
}
// ============ View Methods ============
function getRandomFromTimestamp(uint256 _timestamp) external view returns (uint256 word, uint256 timestamp){
Random memory _random = random.findUpperBound(_timestamp);
return (_random.word, _random.timestamp);
}
/**
* @dev This function is called by Chainlink Keeper to determine if performUpkeep() needs to be called.
*/
function checkUpkeep(bytes memory /* checkData */) public view override returns (bool upkeepNeeded, bytes memory /* performData */){
unchecked { if(block.timestamp - performUpkeepTimestamp >= automationInterval) upkeepNeeded = true; }
}
// ============ Internal Methods ============
function _performUpkeep() internal {
uint256 requestID = vrfCoordinator.requestRandomWords(keyHash, subscriptionID, minimumRequestConfirmations, callbackGasLimit, 1);
performUpkeepTimestamp = uint64(block.timestamp);
emit RequestWords(requestID);
}
// ============ Admin Methods ============
/**
* @dev Calls Chainlink's requestRandomWords.
* Note: This function can only be called by PERFORM_UPKEEP_ROLE.
*/
function performUpkeep() external onlyRole(PERFORM_UPKEEP_ROLE) {
_performUpkeep();
}
/**
* @dev Changes automationInterval. Emits {ChangeAutomationInterval} event.
* @param _automationInterval - New automationInterval.
* Note: This function can only be called by ADMIN_ROLE.
*/
function changeAutomationInterval(uint32 _automationInterval) external onlyRole(ADMIN_ROLE) {
if(_automationInterval > returnInterval) revert InvalidAutomationInterval();
emit ChangeAutomationInterval(automationInterval, _automationInterval);
automationInterval = _automationInterval;
}
}