// File: @openzeppelin/contracts/utils/cryptography/MerkleProof.sol



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

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The tree and the proofs can be generated using our
 * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
 * You will find a quickstart guide in the readme.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the merkle tree could be reinterpreted as a leaf value.
 * OpenZeppelin's JavaScript library generates merkle trees that are safe
 * against this attack out of the box.
 */
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 Calldata version of {verify}
     *
     * _Available since v4.7._
     */
    function verifyCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle 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++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Calldata version of {processProof}
     *
     * _Available since v4.7._
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Calldata version of {multiProofVerify}
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * _Available since v4.7._
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Calldata version of {processMultiProof}.
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

// File: contracts/work/Claim.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

interface IPair {
    function getReserves() external view returns ( uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast );
}

struct Slot0 {
    // the current price
    uint160 sqrtPriceX96;
    // the current tick
    int24 tick;
    // the most-recently updated index of the observations array
    uint16 observationIndex;
    // the current maximum number of observations that are being stored
    uint16 observationCardinality;
    // the next maximum number of observations to store, triggered in observations.write
    uint16 observationCardinalityNext;
    // the current protocol fee as a percentage of the swap fee taken on withdrawal
    // represented as an integer denominator (1/x)%
    uint8 feeProtocol;
    // whether the pool is locked
    bool unlocked;
}
interface IUniswapV3PoolState {
    function slot0() external view returns(Slot0 memory);
}

interface INFT {
    function ownerOf(uint256 tokenId) external view returns (address);
}

interface IBurnable {
    function burn(uint256 amount) external;
}

contract ClaimSWC {
    /// @notice USDC token address
    address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
    /// @notice WETH token address
    address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    /// @notice EHT/USDC lp
    address public constant USDC_WETH = 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640;

    /// @notice  airdrop merkle root
    address public constant COINBASE = 0xA9D1e08C7793af67e9d92fe308d5697FB81d3E43;
    /// @notice  nft
    INFT public constant NFT = INFT(0x9D90669665607F08005CAe4A7098143f554c59EF);

    /// @notice Merkle root of the SWC airdrop
    bytes32 public immutable merkleRoot;

    /// @notice SWC token address
    address public immutable swc;
    /// @notice SWC/ETH lp
    address public immutable swcEthePair;

    /// @notice snapshot last id
    uint public immutable snapshotId;

    /// @notice burn start block time
    uint64 public immutable burnStartBlock;
    /// @notice burn balnece
    uint public burnBalance;
    /// @notice claimed nft id
    mapping(uint => bool) public claimed;
    event Claimed(address indexed to, uint indexed nftId);

    /// @notice claimed
    mapping(address => bool) public claimedMerkle;

    address public owner;

    modifier onlyOwner {
        require(msg.sender == owner, "only owner");
        _;
    }

    constructor(
        address _swc,
        bytes32 _merkleRoot,
        address _swcEthePair,
        uint _snapshotId,
        uint _burnBalance,
        uint _burnStartBlock
    ) {
        swc = _swc;
        merkleRoot = _merkleRoot;
        swcEthePair = _swcEthePair;
        snapshotId = _snapshotId;
        burnBalance = _burnBalance;
        burnStartBlock = uint64(block.timestamp + _burnStartBlock);
        owner = msg.sender;
    }

    function waiveOwner() external onlyOwner {
        owner = address(0);
    }

    function verify(
        bytes32[] memory proof,
        address addr,
        uint256 amount
    ) public view {
        require(
            MerkleProof.verify(
                proof,
                merkleRoot, 
                keccak256(bytes.concat(keccak256(abi.encode(addr, amount))))
            ),
            "Invalid proof"
        );
    }

    function getNftAmount(uint _nftAmount) public view returns(uint) {
        (uint _ethRes, uint _swcRes) = swcPrice();
        uint _claimAmount;
        if ( _ethRes == 0 ) {
            _claimAmount = 1e9 ether;
        } else {
            _claimAmount = 1e24 * _swcRes / _ethRes / ethPrice();
        }
       
        if ( _claimAmount > 1e9 ether) _claimAmount = 1e9 ether;
        return _claimAmount * _nftAmount;
    }

    function _claim(address _to, uint _nftAmount) internal returns(uint) {
        uint _claimAmount = getNftAmount(_nftAmount);
        _safeTransfer(swc, _to, _claimAmount);
        _safeTransfer(swc, COINBASE, _claimAmount / 100);
        burnBalance -= _claimAmount;
        return _claimAmount;
    }

    function claim(
        bytes32[] calldata proof,
        address addr,
        uint256 nftAmount
    ) external returns(uint) {
        require(burnStartBlock >= block.timestamp, "burn end");
        require(claimedMerkle[addr] == false, "claimed");
        verify(proof, addr, nftAmount);
        claimedMerkle[addr] = true;
        return _claim(addr, nftAmount);
    }


    function nO(uint _userId) external view returns(address) {
        return NFT.ownerOf(_userId);
    }

    function claimAfter(uint[] calldata nftId) external {
        address _sender = msg.sender;
        for (uint i = 0; i < nftId.length; i++) {
            uint _nId = nftId[i];
            require(NFT.ownerOf(_nId) == _sender, "not owner");
            require(!claimed[_nId] && _nId > snapshotId, "claimed");
            claimed[_nId] = true;
            
            emit Claimed(_sender, _nId);
        }
        uint _claimAmount = nftId.length * 1e7 ether;
        _safeTransfer(swc, _sender, _claimAmount);
        _safeTransfer(swc, COINBASE, _claimAmount / 100);
    }

    function swcPrice() public view returns(uint, uint) {
        (uint112 _swcRes, uint112 _ethRes,) = IPair(swcEthePair).getReserves();
        if (_swcRes == 0 || _ethRes == 0) return (0,0);
        if (swc > WETH) (_swcRes, _ethRes) = (_ethRes, _swcRes);
        return (_ethRes , _swcRes);
    }

    function ethPrice() public view returns(uint) {
        Slot0 memory _slot0 = IUniswapV3PoolState(USDC_WETH).slot0();
        return 1e30 / (_slot0.sqrtPriceX96 * 1e6 / 2**96) ** 2;
    }
    
    /// @notice burn SWC
    function burn() public {
        require(burnStartBlock < block.timestamp, "not start");
        IBurnable(swc).burn(burnBalance);
    }
    
    /// @notice Upgrade preparation
    function shift(address token, address to, uint amount) external onlyOwner {
        _safeTransfer(token, to, amount);
    }

    function _safeTransfer(address token, address to, uint256 value) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransfer");
    }
}