pragma solidity ^0.8.30;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
contract RaiseBoxFaucet is ERC20, Ownable {
mapping(address => uint256) private lastClaimTime;
mapping(address => bool) private hasClaimedEth;
address public faucetClaimer;
uint256 public constant CLAIM_COOLDOWN = 3 days;
uint256 public dailyClaimLimit = 100;
uint256 public faucetDrip;
uint256 public constant INITIAL_SUPPLY = 1000000000 * 10 ** 18;
uint256 public lastDripDay;
uint256 public lastFaucetDripDay;
uint256 public dailyDrips;
uint256 public sepEthAmountToDrip;
bool public sepEthDripsPaused;
uint256 public dailyClaimCount;
uint256 public dailySepEthCap;
uint256 public blockTime = block.timestamp;
constructor(
string memory name_,
string memory symbol_,
uint256 faucetDrip_,
uint256 sepEthDrip_,
uint256 dailySepEthCap_
) ERC20(name_, symbol_) Ownable(msg.sender) {
faucetDrip = faucetDrip_;
sepEthAmountToDrip = sepEthDrip_;
dailySepEthCap = dailySepEthCap_;
_mint(address(this), INITIAL_SUPPLY);
}
event SepEthDripped(address indexed claimant, uint256 amount);
event SepEthDripSkipped(address indexed claimant, string reason);
event SepEthRefilled(address indexed refiller, uint256 amount);
event SepEthDonated(address indexed donor, uint256 amount);
event SepEthDripsPaused(bool paused);
event Claimed(address indexed user, uint256 amount);
event MintedNewFaucetTokens(address indexed user, uint256 amount);
error RaiseBoxFaucet_EthTransferFailed();
error RaiseBoxFaucet_CannotClaimAnymoreFaucetToday();
error RaiseBoxFaucet_FaucetNotOutOfTokens();
error RaiseBoxFaucet_MiningToNonContractAddressFailed();
error RaiseBoxFaucet_CurrentClaimLimitIsLessThanBy();
error RaiseBoxFaucet_ClaimCooldownOn();
error RaiseBoxFaucet_OwnerOrZeroOrContractAddressCannotCallClaim();
error RaiseBoxFaucet_DailyClaimLimitReached();
error RaiseBoxFaucet_InsufficientContractBalance();
function mintFaucetTokens(address to, uint256 amount) public onlyOwner {
if (to != address(this)) {
revert RaiseBoxFaucet_MiningToNonContractAddressFailed();
}
if (balanceOf(address(to)) > 1000 * 10 ** 18) {
revert RaiseBoxFaucet_FaucetNotOutOfTokens();
}
_mint(to, amount);
emit MintedNewFaucetTokens(to, amount);
}
function burnFaucetTokens(uint256 amountToBurn) public onlyOwner {
require(amountToBurn <= balanceOf(address(this)), "Faucet Token Balance: Insufficient");
_transfer(address(this), msg.sender, balanceOf(address(this)));
_burn(msg.sender, amountToBurn);
}
function adjustDailyClaimLimit(uint256 by, bool increaseClaimLimit) public onlyOwner {
if (increaseClaimLimit) {
dailyClaimLimit += by;
} else {
if (by > dailyClaimLimit) {
revert RaiseBoxFaucet_CurrentClaimLimitIsLessThanBy();
}
dailyClaimLimit -= by;
}
}
function claimFaucetTokens() public {
faucetClaimer = msg.sender;
if (block.timestamp < (lastClaimTime[faucetClaimer] + CLAIM_COOLDOWN)) {
revert RaiseBoxFaucet_ClaimCooldownOn();
}
if (faucetClaimer == address(0) || faucetClaimer == address(this) || faucetClaimer == Ownable.owner()) {
revert RaiseBoxFaucet_OwnerOrZeroOrContractAddressCannotCallClaim();
}
if (balanceOf(address(this)) <= faucetDrip) {
revert RaiseBoxFaucet_InsufficientContractBalance();
}
if (dailyClaimCount >= dailyClaimLimit) {
revert RaiseBoxFaucet_DailyClaimLimitReached();
}
if (!hasClaimedEth[faucetClaimer] && !sepEthDripsPaused) {
uint256 currentDay = block.timestamp / 24 hours;
if (currentDay > lastDripDay) {
lastDripDay = currentDay;
dailyDrips = 0;
}
if (dailyDrips + sepEthAmountToDrip <= dailySepEthCap && address(this).balance >= sepEthAmountToDrip) {
hasClaimedEth[faucetClaimer] = true;
dailyDrips += sepEthAmountToDrip;
(bool success,) = faucetClaimer.call{value: sepEthAmountToDrip}("");
if (success) {
emit SepEthDripped(faucetClaimer, sepEthAmountToDrip);
} else {
revert RaiseBoxFaucet_EthTransferFailed();
}
} else {
emit SepEthDripSkipped(
faucetClaimer,
address(this).balance < sepEthAmountToDrip ? "Faucet out of ETH" : "Daily ETH cap reached"
);
}
} else {
dailyDrips = 0;
}
*
* @param lastFaucetDripDay tracks the last day a claim was made
* @notice resets the @param dailyClaimCount every 24 hours
*/
if (block.timestamp > lastFaucetDripDay + 1 days) {
lastFaucetDripDay = block.timestamp;
dailyClaimCount = 0;
}
lastClaimTime[faucetClaimer] = block.timestamp;
dailyClaimCount++;
_transfer(address(this), faucetClaimer, faucetDrip);
emit Claimed(msg.sender, faucetDrip);
}
function refillSepEth(uint256 amountToRefill) external payable onlyOwner {
require(amountToRefill > 0, "invalid eth amount");
require(msg.value == amountToRefill, "Refill amount must be same as value sent.");
emit SepEthRefilled(msg.sender, amountToRefill);
}
function toggleEthDripPause(bool _paused) external onlyOwner {
sepEthDripsPaused = _paused;
emit SepEthDripsPaused(_paused);
}
receive() external payable {
emit SepEthDonated(msg.sender, msg.value);
}
fallback() external payable {
emit SepEthDonated(msg.sender, msg.value);
}
function getBalance(address user) public view returns (uint256) {
return balanceOf(user);
}
function getClaimer() public view returns (address) {
return faucetClaimer;
}
function getHasClaimedEth(address user) public view returns (bool) {
return hasClaimedEth[user];
}
function getUserLastClaimTime(address user) public view returns (uint256) {
return lastClaimTime[user];
}
function getFaucetTotalSupply() public view returns (uint256) {
return balanceOf(address(this));
}
function getContractSepEthBalance() public view returns (uint256) {
return address(this).balance;
}
function getOwner() public view returns (address) {
return Ownable.owner();
}
}