contract MerkleClaimERC20 is ERC20 {
    /// for airdrop
    uint256 public constant AMOUNT_AIRDROP = 1.55455e11 ether;

    uint256 public constant MAX_SUPPLY = AMOUNT_AIRDROP * 2;

    /// for DAO
    uint256 public constant AMOUNT_DAO = MAX_SUPPLY/ 100 * 20;
    address public constant ADDR_DAO = 0x1E09eEF8f405C7edCa46b08B236854EC4A5ae213;

    /// for liquidity incentives
    uint256 public constant AMOUNT_LP = MAX_SUPPLY / 100 * 25;
    address public constant ADDR_LP = 0xf4Ed582037f5B1218d2ec65b7F2cbEDad7eF8977;

    /// for team
    uint256 public constant AMOUNT_TEAM = MAX_SUPPLY / 100 * 5;
    address public constant ADDR_TEAM = 0x944BbC959003196C10C0C989804DF27aE388300C;

    /// ============ Immutable storage ============

    /// @notice ERC20-claimee inclusion root
    bytes32 public immutable merkleRoot;

    /// ============ Mutable storage ============

    /// @notice Mapping of addresses who have claimed tokens
    mapping(address => bool) public hasClaimed;


    /// ============ Constructor ============

    /// @notice Creates a new MerkleClaimERC20 contract
    /// @param _name of token
    /// @param _symbol of token
    /// @param _decimals of token
    /// @param _merkleRoot of claimees
    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals,
        bytes32 _merkleRoot
    ) ERC20(_name, _symbol, _decimals) {
        merkleRoot = _merkleRoot; // Update root

        _mint(ADDR_DAO, AMOUNT_DAO);
        _mint(ADDR_LP, AMOUNT_LP);
        _mint(ADDR_TEAM, AMOUNT_TEAM);
    }

    /// ============ Events ============

    /// @notice Emitted after a successful token claim
    /// @param to recipient of claim
    /// @param amount of tokens claimed
    event Claim(address indexed to, uint256 amount);

    /// ============ Functions ============

    /// @notice Allows claiming tokens if address is part of merkle tree
    /// @param to address of claimee
    /// @param amount of tokens owed to claimee
    /// @param proof merkle proof to prove address and amount are in tree
    function claim(address to, uint256 amount, bytes32[] calldata proof) external {
        // Throw if address has already claimed tokens
        require(!hasClaimed[to], "AlreadyClaimed");

        // Verify merkle proof, or revert if not in tree
        bytes32 leaf = keccak256(abi.encodePacked(to, amount));
        bool isValidLeaf = MerkleProof.verify(proof, merkleRoot, leaf);
        require(isValidLeaf, "NotInMerkle");

        // Set address to claimed
        hasClaimed[to] = true;

        // Mint tokens to address
        _mint(to, amount);

        // Emit claim event
        emit Claim(to, amount);
    }
}
function claim(address to, uint256 amount, bytes32[] calldata proof) external {
    // Throw if address has already claimed tokens
    require(!hasClaimed[to], "AlreadyClaimed");

    // Verify merkle proof, or revert if not in tree
    bytes32 leaf = keccak256(abi.encodePacked(to, amount));
    bool isValidLeaf = MerkleProof.verify(proof, merkleRoot, leaf);
    require(isValidLeaf, "NotInMerkle");

    // Set address to claimed
    hasClaimed[to] = true;

    // Mint tokens to address
    _mint(to, amount);

    // Emit claim event
    emit Claim(to, amount);
}

/// ============ Functions ============
/// @notice Allows claiming tokens if address is part of merkle tree
/// @param to address of claimee
/// @param amount of tokens owed to claimee
/// @param proof merkle proof to prove address and amount are in tree