uint256 public constant minimumMintInterval = 365 days;
uint256 public constant mintCap = 200; // 2%
bytes32 public merkleRoot;
uint256 public nextMint; // Timestamp
uint256 public claimPeriodEnds; // Timestamp
BitMaps.BitMap private claimed;
event MerkleRootChanged(bytes32 merkleRoot);
event Claim(address indexed claimant, uint256 amount);
/**
* @dev Constructor.
* @param freeSupply The number of tokens to issue to the contract deployer.
* @param airdropSupply The number of tokens to reserve for the airdrop.
* @param _claimPeriodEnds The timestamp at which tokens are no longer claimable.
*/
constructor(
uint256 freeSupply,
uint256 airdropSupply,
uint256 _claimPeriodEnds
)
ERC20("Ethereum Name Service", "ENS")
ERC20Permit("Ethereum Name Service")
{
_mint(msg.sender, freeSupply);
_mint(address(this), airdropSupply);
claimPeriodEnds = _claimPeriodEnds;
nextMint = block.timestamp + minimumMintInterval;
}
/**
* @dev Claims airdropped tokens.
* @param amount The amount of the claim being made.
* @param delegate The address the tokenholder wants to delegate their votes to.
* @param merkleProof A merkle proof proving the claim is valid.
*/
function claimTokens(uint256 amount, address delegate, bytes32[] calldata merkleProof) external {
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, amount));
(bool valid, uint256 index) = MerkleProof.verify(merkleProof, merkleRoot, leaf);
require(valid, "ENS: Valid proof required.");
require(!isClaimed(index), "ENS: Tokens already claimed.");
claimed.set(index);
emit Claim(msg.sender, amount);
_delegate(msg.sender, delegate);
_transfer(address(this), msg.sender, amount);
}
/**
* @dev Allows the owner to sweep unclaimed tokens after the claim period ends.
* @param dest The address to sweep the tokens to.
*/
function sweep(address dest) external onlyOwner {
require(block.timestamp > claimPeriodEnds, "ENS: Claim period not yet ended");
_transfer(address(this), dest, balanceOf(address(this)));
}
/**
* @dev Returns true if the claim at the given index in the merkle tree has already been made.
* @param index The index into the merkle tree.
*/
function isClaimed(uint256 index) public view returns (bool) {
return claimed.get(index);
}
/**
* @dev Sets the merkle root. Only callable if the root is not yet set.
* @param _merkleRoot The merkle root to set.
*/
function setMerkleRoot(bytes32 _merkleRoot) external onlyOwner {
require(merkleRoot == bytes32(0), "ENS: Merkle root already set");
merkleRoot = _merkleRoot;
emit MerkleRootChanged(_merkleRoot);
}
/**
* @dev Mints new tokens. Can only be executed every `minimumMintInterval`, by the owner, and cannot
* exceed `mintCap / 10000` fraction of the current total supply.
* @param dest The address to mint the new tokens to.
* @param amount The quantity of tokens to mint.
*/
function mint(address dest, uint256 amount) external onlyOwner {
require(amount <= (totalSupply() * mintCap) / 10000, "ENS: Mint exceeds maximum amount");
require(block.timestamp >= nextMint, "ENS: Cannot mint yet");
nextMint = block.timestamp + minimumMintInterval;
_mint(dest, amount);
}
// The following functions are overrides required by Solidity.
function _afterTokenTransfer(address from, address to, uint256 amount)
internal
override(ERC20, ERC20Votes)
{
super._afterTokenTransfer(from, to, amount);
}
function _mint(address to, uint256 amount)
internal
override(ERC20, ERC20Votes)
{
super._mint(to, amount);
}
function _burn(address account, uint256 amount)
internal
override(ERC20, ERC20Votes)
{
super._burn(account, amount);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "./MerkleProof.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
import "@openzeppelin/contracts/utils/structs/BitMaps.sol";
/**
*/
contract ENSToken is ERC20, ERC20Permit, ERC20Votes, Ownable {
using BitMaps for BitMaps.BitMap;
}