// File: MOZVesting.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.5;

import "./Ownable.sol";
import "./ReentrancyGuard.sol";
import "./TransferHelper.sol";

interface Token {
    function decimals() external view returns (uint256);

    function balanceOf(address who) external view returns (uint256);

    function totalsupply() external view returns (uint256);

    function mint(address to, uint256 value) external returns (bool success);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);
}

contract MOZVesting is Ownable, ReentrancyGuard {
    address public teamAllocationAddress =
        0xAf806721C80fF71EEebA33e9Ceb81e1facD7c63b;
    address public mozTokenAddress;
    struct UserInfo {
        uint256 totalTokens;
        uint256 claimedAmount;
    }
    mapping(address => UserInfo) public userMapping;

    //teamAllocation Time
    uint64 vestingPoint1 = 1644451200; //10-02-2022
    uint64 vestingPoint2 = 1675987200; //10-02-2023
    uint64 vestingPoint3 = 1707523200; //10-02-2024
    uint64 vestingPoint4 = 1739145600; //10-02-2025

    constructor(address _mozTokenAddress) {
        mozTokenAddress = _mozTokenAddress;
        updateInfo(2_000_000_000, teamAllocationAddress);
    }

    function updateInfo(uint256 _totalTokens, address _userAddress) internal {
        UserInfo memory uInfo = UserInfo({
            totalTokens: _totalTokens * (10**Token(mozTokenAddress).decimals()),
            claimedAmount: 0
        });
        userMapping[_userAddress] = uInfo;
    }

    function claimTokens() external {
        require((msg.sender == teamAllocationAddress), "Invalid Address");
        UserInfo storage uInfo = userMapping[msg.sender];
        uint256 userTokens = calculateTokens();
        require(userTokens != 0, "Cannot Claim");
        TransferHelper.safeTransfer(mozTokenAddress, msg.sender, userTokens);
        uInfo.claimedAmount = uInfo.claimedAmount + userTokens;
    }

    function calculateTokens() public view returns (uint256) {
        UserInfo memory uInfo = userMapping[teamAllocationAddress];
        uint256 tokenAmount;
        if (uint64(block.timestamp) > vestingPoint4) {
            tokenAmount = uInfo.totalTokens - uInfo.claimedAmount;
        } else if (uint64(block.timestamp) > vestingPoint3) {
            tokenAmount =
                ((70 * uInfo.totalTokens) / 100) -
                uInfo.claimedAmount;
        } else if (uint64(block.timestamp) > vestingPoint2) {
            tokenAmount =
                ((40 * uInfo.totalTokens) / 100) -
                uInfo.claimedAmount;
        } else if (uint64(block.timestamp) > vestingPoint1) {
            tokenAmount =
                ((10 * uInfo.totalTokens) / 100) -
                uInfo.claimedAmount;
        }
        return (tokenAmount);
    }

    function updateMozAddress(address _mozTokenAddress) external onlyOwner {
        mozTokenAddress = _mozTokenAddress;
    }

    function updateTeamAllocationAddress(address _teamAllocationAddress)
        external
        onlyOwner
    {
        teamAllocationAddress = _teamAllocationAddress;
    }
}

// File: Ownable.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.5;

contract Ownable {
    address public owner;

    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );

    /**
     * @dev The Ownable constructor sets the original `owner` of the contract to the sender
     * account.
     */
    constructor() {
        _setOwner(msg.sender);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner Access");
        _;
    }

    /**
     * @dev Allows the current owner to transfer control of the contract to a newOwner.
     * @param newOwner The address to transfer ownership to.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0));
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }

    function _setOwner(address newOwner) internal {
        owner = newOwner;
    }
}


// File: ReentrancyGuard.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor () {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}



// File: TransferHelper.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.5;

// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
    function safeApprove(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            'TransferHelper::safeApprove: approve failed'
        );
    }

    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))),
            'TransferHelper::safeTransfer: transfer failed'
        );
    }

    function safeTransferFrom(
        address token,
        address from,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            'TransferHelper::transferFrom: transferFrom failed'
        );
    }

    function safeTransferETH(address to, uint256 value) internal {
        (bool success, ) = to.call{value: value}(new bytes(0));
        require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');
    }
}