// File: contracts/EthClaim.sol



//SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./libraries/MerkleProof.sol";
import "./libraries/Math.sol";

contract EthClaim {
    using Math for uint256;
    using MerkleProof for bytes32;

    address public owner;
    bytes32 public merkleRoot;
    uint256 public claimEnds;
    bool public isActive;

    uint256 private _totalBalance;
    uint256 private _totalAmount;
    
    mapping(address => bool) private _claims;

    constructor(bytes32 _merkleRoot, uint256 totalAmount) payable {
        merkleRoot = _merkleRoot;
        owner = msg.sender;
        claimEnds = block.timestamp + (365 * 86400);
        _totalAmount = totalAmount;
    }

    receive() external payable {}
    fallback() external payable {}

    function setActive() public {
        require(msg.sender == owner, "Not owner");
        require(!isActive, "Contract already active");

        _totalBalance = address(this).balance;
    }

    function checkAmount(uint256 amount) public view returns (uint256) {
        uint256 percentOfTotal = amount.mulDivDown(1e18, _totalAmount);
        uint256 amountToSend = _totalBalance.mulDivDown(percentOfTotal, 1e18);

        return amountToSend;
    }

    function claim(bytes32[] calldata proof, uint256 amount) external returns (uint256)
    {
        require(checkProof(msg.sender, proof, amount));
        require(block.timestamp < claimEnds, "Claiming is over");

        uint256 amountToSend = checkAmount(amount);

        require(amountToSend < address(this).balance, "Amount exceeds contract balance");
        require(amountToSend > 0, "Amount cannot be 0");

        (bool sent, ) = payable(msg.sender).call{value: amountToSend}("");

        require(sent, "Failed to send ETH");

        _claims[msg.sender] = true;

        return amountToSend;
    }

    function checkProof(address account, bytes32[] calldata proof, uint256 amount) public view returns (bool) {
        require(!checkClaimed(account), "Already claimed");

        bytes32 leaf = keccak256(abi.encode(account, amount));

        require(MerkleProof.verify(proof, merkleRoot, leaf), "Proof failed");

        return true;
    }

    function checkClaimed(address account) public view returns (bool)
    {
        return _claims[account];
    }

    function withdrawLeftover() public
    {
        require(msg.sender == owner, "Not owner");
        require(block.timestamp > claimEnds, "Claiming not done yet");

        isActive = false;

        (bool sent, ) = payable(msg.sender).call{value: address(this).balance}("");
        
        require(sent, "Failed to send ETH");
    }
}

// File: contracts/libraries/Math.sol



// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity >=0.8.0;

library Math {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
                revert(0, 0)
            }

            // Divide z by the denominator.
            z := div(z, denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
                revert(0, 0)
            }

            // First, divide z - 1 by the denominator and add 1.
            // We allow z - 1 to underflow if z is 0, because we multiply the
            // end result by 0 if z is zero, ensuring we return 0 if z is zero.
            z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))
        }
    }
}
// File: contracts/libraries/MerkleProof.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Trees proofs.
 *
 * The proofs can be generated using the JavaScript library
 * https://github.com/miguelmota/merkletreejs[merkletreejs].
 * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
 *
 * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
 */
library MerkleProof {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merklee tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     *
     * _Available since v4.4._
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            bytes32 proofElement = proof[i];
            if (computedHash <= proofElement) {
                // Hash(current computed hash + current element of the proof)
                computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
            } else {
                // Hash(current element of the proof + current computed hash)
                computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
            }
        }
        return computedHash;
    }
}