Skip to content

0xc18360217d8f7ab5e7c516566761ea12ce7f9d72 #23

@teheather070

Description

@teheather070

// 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";

/**

  • @dev An ERC20 token for ENS.
  •  Besides the addition of voting capabilities, we make a couple of customisations:
    
  •   - Airdrop claim functionality via `claimTokens`. At creation time the tokens that
    
  •     should be available for the airdrop are transferred to the token contract address;
    
  •     airdrop claims are made from this balance.
    
  •   - Support for the owner (the DAO) to mint new tokens, at up to 2% PA.
    

*/
contract ENSToken is ERC20, ERC20Permit, ERC20Votes, Ownable {
using BitMaps for BitMaps.BitMap;

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);
}

}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions