-
Notifications
You must be signed in to change notification settings - Fork 29
/
Copy pathFaucet.sol
116 lines (99 loc) · 3.73 KB
/
Faucet.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import {IFaucet} from './interfaces/IFaucet.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
import {SignatureChecker} from '@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol';
/**
* @title Faucet
* @dev The Kinto Faucet gives a bit of ETH to users to pay for gas fees
*/
contract Faucet is Ownable, IFaucet{
using ECDSA for bytes32;
using SignatureChecker for address;
/* ============ Events ============ */
event Claim(address indexed _to, uint256 _timestamp);
/* ============ Constants ============ */
uint public constant CLAIM_AMOUNT = 1 ether / 200;
uint public constant FAUCET_AMOUNT = 1 ether;
/* ============ State Variables ============ */
mapping(address => bool) public override claimed;
bool public active;
/// @dev We include a nonce in every hashed message, and increment the nonce as part of a
/// state-changing operation, so as to prevent replay attacks, i.e. the reuse of a signature.
mapping(address => uint256) public override nonces;
constructor(){}
/**
* @dev Allows users to claim KintoETH from the smart contract's faucet once per account
*/
function claimKintoETH() external override {
_privateClaim(msg.sender);
}
/**
* @dev Claim via meta tx on behalf of a new account by the owner
* @param _signatureData Signature data
*/
function claimOnBehalf(IFaucet.SignatureData calldata _signatureData) external
onlyOwner onlySignerVerified(_signatureData) {
_privateClaim(_signatureData.account);
nonces[_signatureData.account]++;
}
/**
* @dev Function to withdraw all eth by owner
*/
function withdrawAll() external override onlyOwner {
payable(msg.sender).transfer(address(this).balance);
}
/**
* @dev Function to start the faucet
*/
function startFaucet() payable external override onlyOwner {
require(msg.value >= FAUCET_AMOUNT, 'Not enough ETH to start faucet');
active = true;
}
/**
* @dev Allows the contract to receive Ether
*/
receive() external payable {}
/* ============ Private functions ============ */
function _privateClaim(address _receiver) private {
require(active, 'Faucet is not active');
require(!claimed[_receiver], 'You have already claimed your KintoETH');
claimed[_receiver] = true;
payable(_receiver).transfer(CLAIM_AMOUNT);
if (address(this).balance < CLAIM_AMOUNT) {
active = false;
}
emit Claim(_receiver, block.timestamp);
}
/* ============ Signature Recovery ============ */
/**
* @dev Check that the signature is valid and the address has not claimed yet.
* @param _signature signature to be recovered.
*/
modifier onlySignerVerified(
IFaucet.SignatureData calldata _signature
) {
require(block.timestamp < _signature.expiresAt, 'Signature has expired');
require(nonces[_signature.signer] == _signature.nonce, 'Invalid Nonce');
require(owner() == msg.sender, 'Invalid Sender');
bytes32 hash = keccak256(
abi.encodePacked(
'\x19\x01', // EIP-191 header
keccak256(abi.encode(
_signature.signer,
address(this),
_signature.account,
_signature.expiresAt,
nonces[_signature.signer],
bytes32(block.chainid)
))
)
).toEthSignedMessageHash();
require(
_signature.signer.isValidSignatureNow(hash, _signature.signature),
'Invalid Signer'
);
_;
}
}