EthFaucet.sol (5806B)
1 pragma solidity >=0.8.0; 2 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 5 contract EthFaucet { 6 7 // Implements ERC173 8 address public owner; 9 address public registry; 10 address public periodChecker; 11 12 // Implements Faucet 13 address constant public token = address(0); 14 15 // Implements Seal 16 uint256 public sealState; 17 18 uint256 amount; 19 20 uint8 constant REGISTRY_STATE = 1; 21 uint8 constant PERIODCHECKER_STATE = 2; 22 uint8 constant VALUE_STATE = 4; 23 // Implements Seal 24 uint256 constant public maxSealState = 7; 25 26 // Implements Faucet 27 event Give(address indexed _recipient, address indexed _token, uint256 _amount); 28 // Implements Faucet 29 event FaucetAmountChange(uint256 _amount); 30 31 // Implements Seal 32 event SealStateChange(uint256 indexed _sealState, address _registry, address _periodChecker); 33 34 constructor() { 35 owner = msg.sender; 36 } 37 38 // Set the given seal bits. 39 // Reverts if any bits are already set, if bit value is out of bounds. 40 function seal(uint256 _state) public returns(uint256) { 41 require(_state < 8, 'ERR_INVALID_STATE'); 42 require(_state & sealState == 0, 'ERR_ALREADY_LOCKED'); 43 sealState |= _state; 44 emit SealStateChange(sealState, registry, periodChecker); 45 return uint256(sealState); 46 } 47 48 // Change faucet amount. 49 // Reverts if VALUE_STATE seal is set. 50 function setAmount(uint256 _v) public returns(uint256) { 51 require(msg.sender == owner, 'ERR_NOT_OWNER'); 52 require(sealState & VALUE_STATE == 0, 'ERR_SEALED'); 53 amount = _v; 54 emit FaucetAmountChange(amount); 55 return amount; 56 } 57 58 // Set period checker contract backend. 59 // Reverts if PERIODCHECKER_STATE seal is set 60 function setPeriodChecker(address _checker) public { 61 require(msg.sender == owner, 'ERR_NOT_OWNER'); 62 require(sealState & PERIODCHECKER_STATE == 0, 'ERR_SEALED'); 63 periodChecker = _checker; 64 emit SealStateChange(sealState, registry, periodChecker); 65 } 66 67 // Set accounts index (Access Control List - ACL) backend. 68 // Reverts if REGISTRY_STATE seal is set 69 function setRegistry(address _registry) public { 70 require(msg.sender == owner, 'ERR_NOT_OWNER'); 71 require(sealState & REGISTRY_STATE == 0, 'ERR_SEALED'); 72 registry = _registry; 73 emit SealStateChange(sealState, registry, periodChecker); 74 } 75 76 // Return true if period checker backend allows usage of the faucet. 77 // Will always return true if period checker contract address has not been set. 78 function checkPeriod(address _recipient) private returns(bool) { 79 bool _ok; 80 bytes memory _result; 81 82 if (periodChecker == address(0)) { 83 return true; 84 } 85 86 (_ok, _result) = periodChecker.call(abi.encodeWithSignature("have(address)", _recipient)); 87 if (!_ok) { 88 revert('ERR_PERIOD_BACKEND'); 89 } 90 return _result[31] == 0x01; 91 } 92 93 // Return true if recipient has been added to the ACL. 94 // Will always return true if ACL contract address has not been set. 95 function checkRegistry(address _recipient) private returns(bool) { 96 bool _ok; 97 bytes memory _result; 98 99 if (registry == address(0)) { 100 return true; 101 } 102 103 (_ok, _result) = registry.call(abi.encodeWithSignature("have(address)", _recipient)); 104 if (!_ok) { 105 revert('ERR_REGISTRY_BACKEND'); 106 } 107 return _result[31] == 0x01; 108 } 109 110 // Return false if contract does not have sufficient gas token balance to cover a single use. 111 // Used as backend for check. 112 function checkBalance() private view returns(bool) { 113 return amount <= address(this).balance; 114 } 115 116 // Check if a faucet usage attempt would succeed for the given recipient in the current contract state. 117 function check(address _recipient) public returns(bool) { 118 if (!checkPeriod(_recipient)) { 119 return false; 120 } 121 if (!checkRegistry(_recipient)) { 122 return false; 123 } 124 return checkBalance(); 125 } 126 127 // Execute a single faucet usage for recipient. 128 function checkAndPoke(address _recipient) private returns(bool){ 129 bool _ok; 130 bytes memory _result; 131 132 if (!checkBalance()) { 133 revert('ERR_INSUFFICIENT_BALANCE'); 134 } 135 136 if (!checkRegistry(_recipient)) { 137 revert('ERR_NOT_IN_WHITELIST'); 138 } 139 140 if (periodChecker == address(0)) { 141 return true; 142 } 143 144 (_ok, _result) = periodChecker.call(abi.encodeWithSignature("poke(address)", _recipient)); 145 if (!_ok) { 146 revert('ERR_PERIOD_BACKEND'); 147 } 148 if (_result[31] == 0) { 149 revert('ERR_PERIOD_CHECK'); 150 } 151 return true; 152 } 153 154 // Implements Faucet 155 function gimme() public returns(uint256) { 156 require(checkAndPoke(msg.sender)); 157 payable(msg.sender).transfer(amount); 158 emit Give(msg.sender, address(0), amount); 159 return amount; 160 } 161 162 // Implements Faucet 163 function giveTo(address _recipient) public returns(uint256) { 164 require(checkAndPoke(_recipient)); 165 payable(_recipient).transfer(amount); 166 emit Give(_recipient, address(0), amount); 167 return amount; 168 } 169 170 // Implements Faucet 171 function nextTime(address _subject) public returns(uint256) { 172 bool _ok; 173 bytes memory _result; 174 175 (_ok, _result) = periodChecker.call(abi.encodeWithSignature("next(address)", _subject)); 176 if (!_ok) { 177 revert('ERR_PERIOD_BACKEND_ERROR'); 178 } 179 return uint256(bytes32(_result)); 180 } 181 182 // Implements Faucet 183 function nextBalance(address _subject) public returns(uint256) { 184 bool _ok; 185 bytes memory _result; 186 187 (_ok, _result) = periodChecker.call(abi.encodeWithSignature("balanceThreshold()", _subject)); 188 if (!_ok) { 189 revert('ERR_PERIOD_BACKEND_ERROR'); 190 } 191 return uint256(bytes32(_result)); 192 193 } 194 195 // Implements Faucet 196 function tokenAmount() public view returns(uint256) { 197 return amount; 198 } 199 200 receive () payable external { 201 } 202 203 // Implements ERC165 204 function supportsInterface(bytes4 _sum) public pure returns (bool) { 205 if (_sum == 0x01ffc9a7) { // ERC165 206 return true; 207 } 208 if (_sum == 0x9493f8b2) { // ERC173 209 return true; 210 } 211 if (_sum == 0x1a3ac634) { // Faucet 212 return true; 213 } 214 if (_sum == 0x0d7491f8) { // Seal 215 return true; 216 } 217 return false; 218 } 219 }