eth-faucet/solidity/EthFaucet.sol

220 lines
5.7 KiB
Solidity
Raw Normal View History

2023-03-25 15:28:38 +01:00
pragma solidity >=0.8.0;
2023-03-25 15:28:38 +01:00
// SPDX-License-Identifier: AGPL-3.0-or-later
2023-03-25 15:28:38 +01:00
contract EthFaucet {
2023-03-25 15:28:38 +01:00
// Implements ERC173
address public owner;
address public registry;
address public periodChecker;
2023-03-25 15:28:38 +01:00
// Implements Faucet
address constant public token = address(0);
// Implements Seal
uint256 public sealState;
2023-03-25 15:28:38 +01:00
2023-03-20 10:03:20 +01:00
uint256 amount;
uint8 constant REGISTRY_STATE = 1;
uint8 constant PERIODCHECKER_STATE = 2;
uint8 constant VALUE_STATE = 4;
2023-03-25 15:28:38 +01:00
// Implements Seal
uint256 constant public maxSealState = 7;
2023-03-25 15:28:38 +01:00
// Implements Faucet
2023-03-20 10:34:40 +01:00
event Give(address indexed _recipient, address indexed _token, uint256 _amount);
2023-03-25 15:28:38 +01:00
// Implements Faucet
2023-02-03 12:41:48 +01:00
event FaucetAmountChange(uint256 _amount);
2023-03-25 15:28:38 +01:00
// Implements Seal
2023-03-20 10:34:40 +01:00
event SealStateChange(uint256 indexed _sealState, address _registry, address _periodChecker);
constructor() {
owner = msg.sender;
}
2023-03-25 15:28:38 +01:00
// Set the given seal bits.
// Reverts if any bits are already set, if bit value is out of bounds.
function seal(uint256 _state) public returns(uint256) {
require(_state < 8, 'ERR_INVALID_STATE');
require(_state & sealState == 0, 'ERR_ALREADY_LOCKED');
sealState |= _state;
2023-03-20 10:34:40 +01:00
emit SealStateChange(sealState, registry, periodChecker);
return uint256(sealState);
}
2023-03-25 15:28:38 +01:00
// Change faucet amount.
// Reverts if VALUE_STATE seal is set.
2023-02-03 12:41:48 +01:00
function setAmount(uint256 _v) public returns(uint256) {
require(msg.sender == owner, 'ERR_NOT_OWNER');
require(sealState & VALUE_STATE == 0, 'ERR_SEALED');
2023-02-03 12:41:48 +01:00
amount = _v;
emit FaucetAmountChange(amount);
return amount;
}
2023-03-25 15:28:38 +01:00
// Set period checker contract backend.
// Reverts if PERIODCHECKER_STATE seal is set
function setPeriodChecker(address _checker) public {
require(msg.sender == owner, 'ERR_NOT_OWNER');
require(sealState & PERIODCHECKER_STATE == 0, 'ERR_SEALED');
periodChecker = _checker;
2023-03-20 10:34:40 +01:00
emit SealStateChange(sealState, registry, periodChecker);
}
2023-03-25 15:28:38 +01:00
// Set accounts index (Access Control List - ACL) backend.
// Reverts if REGISTRY_STATE seal is set
function setRegistry(address _registry) public {
require(msg.sender == owner, 'ERR_NOT_OWNER');
require(sealState & REGISTRY_STATE == 0, 'ERR_SEALED');
registry = _registry;
2023-03-20 10:34:40 +01:00
emit SealStateChange(sealState, registry, periodChecker);
}
2023-03-25 15:28:38 +01:00
// Return true if period checker backend allows usage of the faucet.
// Will always return true if period checker contract address has not been set.
function checkPeriod(address _recipient) private returns(bool) {
bool _ok;
bytes memory _result;
2023-03-20 11:49:20 +01:00
if (periodChecker == address(0)) {
return true;
}
2023-02-03 16:04:44 +01:00
(_ok, _result) = periodChecker.call(abi.encodeWithSignature("have(address)", _recipient));
if (!_ok) {
2023-03-17 23:06:30 +01:00
revert('ERR_PERIOD_BACKEND');
}
2023-03-20 11:49:20 +01:00
return _result[31] == 0x01;
}
2023-03-25 15:28:38 +01:00
// Return true if recipient has been added to the ACL.
// Will always return true if ACL contract address has not been set.
function checkRegistry(address _recipient) private returns(bool) {
bool _ok;
bytes memory _result;
2023-03-20 11:49:20 +01:00
if (registry == address(0)) {
return true;
}
(_ok, _result) = registry.call(abi.encodeWithSignature("have(address)", _recipient));
if (!_ok) {
2023-03-17 23:06:30 +01:00
revert('ERR_REGISTRY_BACKEND');
}
2023-03-20 11:49:20 +01:00
return _result[31] == 0x01;
}
2023-03-25 15:28:38 +01:00
// Return false if contract does not have sufficient gas token balance to cover a single use.
// Used as backend for check.
2023-03-20 11:49:20 +01:00
function checkBalance() private view returns(bool) {
return amount <= address(this).balance;
2023-02-05 11:16:23 +01:00
}
2023-03-25 15:28:38 +01:00
// Check if a faucet usage attempt would succeed for the given recipient in the current contract state.
2023-03-20 11:49:20 +01:00
function check(address _recipient) public returns(bool) {
if (!checkPeriod(_recipient)) {
return false;
}
2023-03-20 11:49:20 +01:00
if (!checkRegistry(_recipient)) {
return false;
}
2023-03-20 11:49:20 +01:00
return checkBalance();
}
2023-03-25 15:28:38 +01:00
// Execute a single faucet usage for recipient.
2023-03-20 11:49:20 +01:00
function checkAndPoke(address _recipient) private returns(bool){
2023-03-17 23:06:30 +01:00
bool _ok;
bytes memory _result;
2023-03-20 11:49:20 +01:00
if (!checkBalance()) {
revert('ERR_INSUFFICIENT_BALANCE');
}
if (!checkRegistry(_recipient)) {
revert('ERR_NOT_IN_WHITELIST');
}
2023-02-05 11:16:23 +01:00
2023-03-20 11:49:20 +01:00
if (periodChecker == address(0)) {
return true;
}
2023-03-20 11:49:20 +01:00
2023-03-17 23:06:30 +01:00
(_ok, _result) = periodChecker.call(abi.encodeWithSignature("poke(address)", _recipient));
if (!_ok) {
revert('ERR_PERIOD_BACKEND');
2023-03-20 11:49:20 +01:00
}
if (_result[31] == 0) {
revert('ERR_PERIOD_CHECK');
2023-03-17 23:06:30 +01:00
}
return true;
}
2023-03-25 15:28:38 +01:00
// Implements Faucet
2023-02-03 12:41:48 +01:00
function gimme() public returns(uint256) {
2023-03-20 11:49:20 +01:00
require(checkAndPoke(msg.sender));
2023-02-03 12:41:48 +01:00
payable(msg.sender).transfer(amount);
2023-03-20 11:49:20 +01:00
emit Give(msg.sender, address(0), amount);
2023-02-03 12:41:48 +01:00
return amount;
}
2023-03-25 15:28:38 +01:00
// Implements Faucet
2023-02-03 12:41:48 +01:00
function giveTo(address _recipient) public returns(uint256) {
2023-03-20 11:49:20 +01:00
require(checkAndPoke(_recipient));
2023-02-03 12:41:48 +01:00
payable(_recipient).transfer(amount);
2023-03-20 11:49:20 +01:00
emit Give(_recipient, address(0), amount);
2023-02-03 12:41:48 +01:00
return amount;
}
2023-03-25 15:28:38 +01:00
// Implements Faucet
2023-03-20 10:03:20 +01:00
function nextTime(address _subject) public returns(uint256) {
bool _ok;
bytes memory _result;
(_ok, _result) = periodChecker.call(abi.encodeWithSignature("next(address)", _subject));
if (!_ok) {
revert('ERR_PERIOD_BACKEND_ERROR');
}
return uint256(bytes32(_result));
}
2023-03-25 15:28:38 +01:00
// Implements Faucet
function nextBalance(address _subject) public returns(uint256) {
bool _ok;
bytes memory _result;
(_ok, _result) = periodChecker.call(abi.encodeWithSignature("balanceThreshold()", _subject));
if (!_ok) {
revert('ERR_PERIOD_BACKEND_ERROR');
}
return uint256(bytes32(_result));
}
// Implements Faucet
2023-03-20 10:03:20 +01:00
function tokenAmount() public view returns(uint256) {
2023-02-03 12:41:48 +01:00
return amount;
}
2023-03-25 15:28:38 +01:00
receive () payable external {
2023-03-20 10:03:20 +01:00
}
2023-03-25 15:28:38 +01:00
// Implements ERC165
function supportsInterface(bytes4 _sum) public pure returns (bool) {
if (_sum == 0x01ffc9a7) { // ERC165
return true;
}
if (_sum == 0x9493f8b2) { // ERC173
return true;
}
if (_sum == 0x1a3ac634) { // Faucet
return true;
}
if (_sum == 0x0d7491f8) { // Seal
return true;
}
return false;
}
}