账户
0xda...bfc3
0xDA...BfC3

0xDA...BfC3

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.24+commit.e11b9ed9
语言
Solidity
合同源代码
文件 1 的 13:Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol)

pragma solidity ^0.8.20;

import {Errors} from "./Errors.sol";

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert Errors.InsufficientBalance(address(this).balance, amount);
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert Errors.FailedCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {Errors.FailedCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
     * of an unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {Errors.FailedCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            assembly ("memory-safe") {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert Errors.FailedCall();
        }
    }
}
合同源代码
文件 2 的 13:AggregatorV3Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// solhint-disable-next-line interface-starts-with-i
interface AggregatorV3Interface {
  function decimals() external view returns (uint8);

  function description() external view returns (string memory);

  function version() external view returns (uint256);

  function getRoundData(
    uint80 _roundId
  ) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);

  function latestRoundData()
    external
    view
    returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
合同源代码
文件 3 的 13:Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
合同源代码
文件 4 的 13:ElumphantPresale.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IElumphantStaking} from "./ElumphantStaking.sol";

interface IERC20WithDecimals is IERC20 {
    function decimals() external view returns (uint8);
}

/**
 * @title ElumphantPresale
 * @dev A presale contract for the Elumphant token, supporting purchases in ETH and USDT.
 * This contract facilitates the sale of Elumphant token at a dynamic price that increases
 * after a specified number of tokens are sold. Purchasers can claim their tokens after
 * the claim period begins. The contract uses Chainlink to fetch ETH/USD price data.
 */
contract ElumphantPresale is Ownable, Pausable, ReentrancyGuard {
    /// @notice The Elumphant token being sold in the presale.
    IERC20WithDecimals public immutable token;

    /// @notice USDT token contract address for purchases in USDT.
    IERC20WithDecimals public immutable usdt;

    /// @notice The ElumphantStaking contract address.
    IElumphantStaking public stakingContract;

    /// @notice Chainlink price feed for the ETH/USD pair.
    AggregatorV3Interface public immutable ethPriceFeed;

    /// @notice Price of one token in USD.
    uint256 public tokenPriceInUSD;

    /// @notice Number of tokens sold in a price step before the price increases.
    uint256 public immutable tokensPerStep;

    /// @notice Increment in token price (in USD) after each step.
    uint256 public immutable priceIncrement;

    /// @notice Total tokens sold in the current price step.
    uint256 public tokensSoldInCurStep;

    /// @notice Total number of tokens purchased during the presale.
    uint256 public totalTokensPurchased;

    /// @notice Total number of tokens staked during the presale.
    uint256 public totalTokensStaked;

    /// @notice Minimum purchase amount in USD.
    uint256 public minPurchaseInUSD;

    /// @notice Start time of the presale (UNIX timestamp).
    uint256 public presaleStartTime;

    /// @notice End time of the presale (UNIX timestamp).
    uint256 public presaleEndTime;

    /// @notice Start time for token claiming (UNIX timestamp).
    uint256 public claimStartTime;

    /// @notice Maximum number of steps to externally pump the price.
    uint256 public constant MAX_PRICE_PUMP_STEPS = 100;

    /// @notice Tracks the number of tokens purchased but not yet claimed by each user.
    mapping(address => uint256) public purchasedTokens;

    /// @notice Tracks the number of tokens staked by each user.
    mapping(address => uint256) public stakedTokens;

    /// @notice Event emitted when tokens are purchased.
    /// @param buyer The address of the purchaser.
    /// @param amount The number of tokens purchased.
    /// @param cost The cost of the purchase in ETH or USDT.
    /// @param paymentMethod The method used for payment ("ETH" or "USDT").
    event TokensPurchased(
        address indexed buyer,
        uint256 amount,
        uint256 cost,
        string paymentMethod,
        uint256 costInUSD,
        uint256 totalPurchased
    );

    /// @notice Event emitted when tokens are successfully claimed.
    /// @param claimer The address of the user claiming tokens.
    /// @param amount The number of tokens claimed.
    event TokensClaimed(address indexed claimer, uint256 amount);

    /// @notice Event emitted when tokens are successfully claimed.
    /// @param claimer The address of the user claiming tokens.
    /// @param amount The number of tokens claimed.
    event TokensClaimedAndStaked(address indexed claimer, uint256 amount);

    /// @notice Event emitted when tokens are deposited into the contract.
    /// @param depositor The address of the user depositing tokens.
    /// @param amount The number of tokens deposited.
    event TokensDeposited(address indexed depositor, uint256 amount);

    /// @notice Event emitted when the contract owner withdraws funds.
    /// @param recipient The address receiving the withdrawn funds.
    /// @param amount The amount of funds withdrawn.
    /// @param asset The type of asset withdrawn ("ETH", "USDT", or "Tokens").
    event Withdrawn(address indexed recipient, uint256 amount, string asset);

    /// @notice Event emitted when the token price is updated.
    /// @param oldPrice Old token price.
    /// @param newPrice Updated token price.
    event TokenPriceUpdated(uint256 oldPrice, uint256 newPrice);

    /**
     * @notice Initializes the presale contract with the given parameters.
     * @param _initialOwner The address of the contract owner.
     * @param _token The address of the ElumphantToken contract.
     * @param _usdt The address of the USDT contract.
     * @param _ethPriceFeed The address of the Chainlink ETH/USD price feed.
     * @param _tokenPriceInUSD Initial price of one token in USD (scaled by 1e6).
     * @param _tokensPerStep Number of tokens sold in each step before price increases.
     * @param _priceIncrement The price increment in USD after each step (scaled by 1e6).
     * @param _minPurchaseInUSD The minimum purchase amount in USD.
     */
    constructor(
        address _initialOwner,
        address _token,
        address _usdt,
        address _ethPriceFeed,
        uint256 _tokenPriceInUSD,
        uint256 _tokensPerStep,
        uint256 _priceIncrement,
        uint256 _minPurchaseInUSD
    ) Ownable(_initialOwner) {
        token = IERC20WithDecimals(_token);
        usdt = IERC20WithDecimals(_usdt);
        ethPriceFeed = AggregatorV3Interface(_ethPriceFeed);

        tokenPriceInUSD = _tokenPriceInUSD;
        tokensPerStep = _tokensPerStep;
        priceIncrement = _priceIncrement;
        minPurchaseInUSD = _minPurchaseInUSD;
    }

    /**
     * @dev Ensures the function is called during the presale period.
     */
    modifier beforePresale() {
        require(
            presaleStartTime == 0 || block.timestamp < presaleStartTime,
            "Presale already started!"
        );
        _;
    }

    /**
     * @dev Ensures the function is called during the presale period.
     */
    modifier duringPresale() {
        require(
            block.timestamp >= presaleStartTime &&
                block.timestamp <= presaleEndTime,
            "Presale is not active"
        );
        _;
    }

    /**
     * @dev Ensures the function is called after the presale ends.
     */
    modifier afterPresale() {
        require(block.timestamp > presaleEndTime, "Presale has not ended yet");
        _;
    }

    /**
     * @notice Allows users to purchase tokens using ETH.
     */
    function buyWithETH()
        external
        payable
        whenNotPaused
        duringPresale
        nonReentrant
    {
        require(msg.value > 0, "Must send ETH to purchase tokens");

        uint256 ethPrice = getLatestETHPrice();
        uint256 costInUSD = (msg.value * ethPrice) /
            10 ** (18 + ethPriceFeed.decimals() - usdt.decimals());
        require(
            costInUSD >= minPurchaseInUSD,
            "Amount less than minimum limit"
        );

        uint256 tokensToBuy = (costInUSD * 10 ** token.decimals()) /
            tokenPriceInUSD;
        require(tokensToBuy > 0, "Insufficient ETH for any tokens");

        _updateTokenPrice(tokensToBuy);
        purchasedTokens[msg.sender] += tokensToBuy;
        totalTokensPurchased += tokensToBuy;

        emit TokensPurchased(
            msg.sender,
            tokensToBuy,
            msg.value,
            "ETH",
            costInUSD,
            totalTokensPurchased
        );
    }

    /**
     * @notice Allows users to purchase tokens using USDT.
     * @param _amount The amount of USDT sent for the purchase.
     */
    function buyWithUSDT(
        uint256 _amount
    ) external whenNotPaused duringPresale nonReentrant {
        require(_amount >= minPurchaseInUSD, "Amount less than minimum limit");

        uint256 tokensToBuy = (_amount * 10 ** token.decimals()) /
            tokenPriceInUSD;
        require(tokensToBuy > 0, "Insufficient USDT for any tokens");

        _updateTokenPrice(tokensToBuy);
        purchasedTokens[msg.sender] += tokensToBuy;
        totalTokensPurchased += tokensToBuy;

        (bool success, ) = address(usdt).call(
            abi.encodeWithSignature(
                "transferFrom(address,address,uint256)",
                msg.sender,
                address(this),
                _amount
            )
        );
        require(success, "Token transfer failed");

        emit TokensPurchased(
            msg.sender,
            tokensToBuy,
            _amount,
            "USDT",
            _amount,
            totalTokensPurchased
        );
    }

    /**
     * @notice Allows users to claim their purchased tokens after the claim start time.
     */
    function claim() external whenNotPaused nonReentrant {
        require(
            block.timestamp >= claimStartTime,
            "Claiming is not allowed yet"
        );

        uint256 tokensStaked = stakedTokens[msg.sender];
        uint256 tokensClaimable = purchasedTokens[msg.sender] - tokensStaked;
        require(tokensClaimable > 0, "No tokens to claim");

        purchasedTokens[msg.sender] = tokensStaked;
        bool success = token.transfer(msg.sender, tokensClaimable);
        require(success, "Token transfer failed");

        emit TokensClaimed(msg.sender, tokensClaimable);
    }

    function claimAndStake(uint256 amount) external whenNotPaused nonReentrant {
        require(
            address(stakingContract) != address(0),
            "Staking contract has not been set"
        );

        uint256 tokensClaimable = purchasedTokens[msg.sender] -
            stakedTokens[msg.sender];
        require(amount > 0 && amount <= tokensClaimable, "Invalid amount!");

        stakedTokens[msg.sender] += amount;
        totalTokensStaked += amount;

        token.approve(address(stakingContract), amount);
        stakingContract.depositByPresale(msg.sender, amount);
        emit TokensClaimedAndStaked(msg.sender, amount);
    }

    /**
     * @notice Allows to deposit Elumphant Tokens into the contract.
     * @param _amount The number of tokens to deposit.
     */
    function depositTokens(uint256 _amount) external {
        require(_amount > 0, "Deposit amount must be greater than zero");

        // Transfer tokens from the sender to the contract
        bool success = token.transferFrom(msg.sender, address(this), _amount);
        require(success, "Token transfer failed");

        emit TokensDeposited(msg.sender, _amount);
    }

    /**
     * @notice Sets the presale and claim start/end times.
     * @param _presaleStartTime The start time of the presale (UNIX timestamp).
     * @param _presaleEndTime The end time of the presale (UNIX timestamp).
     * @param _claimStartTime The start time for token claiming (UNIX timestamp).
     */
    function setTimes(
        uint256 _presaleStartTime,
        uint256 _presaleEndTime,
        uint256 _claimStartTime
    ) external onlyOwner beforePresale {
        require(
            block.timestamp <= _presaleEndTime &&
                _presaleStartTime < _presaleEndTime &&
                _presaleEndTime <= _claimStartTime,
            "Invalid time configuration"
        );

        presaleStartTime = _presaleStartTime;
        presaleEndTime = _presaleEndTime;
        claimStartTime = _claimStartTime;
    }

    /**
     * @notice Updates the presale end and claim start times.
     * @param _presaleEndTime The end time of the presale (UNIX timestamp).
     * @param _claimStartTime The start time for token claiming (UNIX timestamp).
     */
    function updatePresaleEndTime(
        uint256 _presaleEndTime,
        uint256 _claimStartTime
    ) external onlyOwner {
        require(
            presaleStartTime < _presaleEndTime &&
                block.timestamp <= _presaleEndTime &&
                _presaleEndTime <= _claimStartTime,
            "Invalid time configuration"
        );

        presaleEndTime = _presaleEndTime;
        claimStartTime = _claimStartTime;
    }

    /**
     * @notice Allows the owner to update the minimum purchase amount in USD.
     * @param _minPurchaseInUSD The new minimum purchase amount (scaled by 1e6).
     */
    function setMinPurchaseInUSD(uint256 _minPurchaseInUSD) external onlyOwner {
        require(
            _minPurchaseInUSD > 0,
            "Minimum purchase must be greater than zero"
        );
        minPurchaseInUSD = _minPurchaseInUSD;
    }

    /**
     * @notice Allows the owner to update the staking contract address.
     * @param _stakingContractAddress The new staking contract address.
     */
    function setStakingContractAddress(
        address _stakingContractAddress
    ) external onlyOwner {
        require(
            _stakingContractAddress != address(0),
            "Minimum purchase must be greater than zero"
        );
        stakingContract = IElumphantStaking(_stakingContractAddress);
    }

    /**
     * @notice Pauses the presale contract, preventing certain actions.
     * Only callable by the contract owner.
     */
    function pause() external onlyOwner {
        _pause();
    }

    /**
     * @notice Unpauses the presale contract, allowing actions to resume.
     * Only callable by the contract owner.
     */
    function unpause() external onlyOwner {
        _unpause();
    }

    /**
     * @notice Allows the contract owner to withdraw ETH.
     * @param _recipient The address to receive the withdrawn ETH.
     */
    function withdrawETH(address _recipient) external onlyOwner nonReentrant {
        uint256 balance = address(this).balance;
        require(balance > 0, "No ETH to withdraw");

        (bool success, ) = payable(_recipient).call{value: balance}("");
        require(success, "ETH transfer failed");

        emit Withdrawn(_recipient, balance, "ETH");
    }

    /**
     * @notice Allows the contract owner to withdraw USDT.
     * @param _recipient The address to receive the withdrawn USDT.
     */
    function withdrawUSDT(address _recipient) external onlyOwner nonReentrant {
        uint256 balance = usdt.balanceOf(address(this));
        require(balance > 0, "No USDT to withdraw");

        (bool success, ) = address(usdt).call(
            abi.encodeWithSignature(
                "transfer(address,uint256)",
                _recipient,
                balance
            )
        );
        require(success, "Token transfer failed");

        emit Withdrawn(_recipient, balance, "USDT");
    }

    /**
     * @notice Allows the contract owner to withdraw unsold tokens after the presale ends.
     * @param _recipient The address to receive the unsold tokens.
     */
    function withdrawUnsoldTokens(
        address _recipient
    ) external onlyOwner afterPresale nonReentrant {
        uint256 balance = token.balanceOf(address(this));
        uint256 soldTokens = totalTokensPurchased - totalTokensStaked;
        uint256 unsoldTokens = balance - soldTokens;
        require(unsoldTokens > 0, "No unsold tokens to withdraw");

        bool success = token.transfer(_recipient, unsoldTokens);
        require(success, "Token transfer failed");

        emit Withdrawn(_recipient, unsoldTokens, "Tokens");
    }

    /**
     * @notice Allows the owner to airdrop tokens to multiple recipients.
     * @param _recipients The array of recipient addresses.
     * @param _amount The number of tokens to airdrop to each recipient.
     */
    function airdrop(
        address[] calldata _recipients,
        uint256 _amount
    ) external onlyOwner {
        require(_recipients.length > 0, "Recipients array should not be empty");
        require(_amount > 0, "Airdrop amount must be greater than zero");

        _updateTokenPrice(_amount * _recipients.length);
        totalTokensPurchased += (_amount * _recipients.length);

        for (uint256 i = 0; i < _recipients.length; i++) {
            purchasedTokens[_recipients[i]] += _amount;
            emit TokensPurchased(
                _recipients[i],
                _amount,
                0,
                "Airdrop",
                _amount * tokenPriceInUSD,
                totalTokensPurchased
            );
        }
    }

    /**
     * @notice Allows the owner to update the token price.
     * @param _newPrice New token price in USD.
     */
    function updateTokenPrice(uint256 _newPrice) public onlyOwner {
        uint256 oldPrice = tokenPriceInUSD;
        tokenPriceInUSD = _newPrice;
        emit TokenPriceUpdated(oldPrice, _newPrice);
    }

    /**
     * @notice Updates the token price when the step threshold is crossed.
     * @param _tokensToBuy The number of tokens purchased in the transaction.
     */
    function _updateTokenPrice(uint256 _tokensToBuy) internal {
        tokensSoldInCurStep += _tokensToBuy;
        if (tokensSoldInCurStep >= tokensPerStep) {
            uint256 steps = tokensSoldInCurStep / tokensPerStep;
            tokensSoldInCurStep %= tokensPerStep; // Reset counter for the next step

            uint256 oldPrice = tokenPriceInUSD;
            uint256 newPrice = oldPrice + steps * priceIncrement;
            tokenPriceInUSD = newPrice;
            emit TokenPriceUpdated(oldPrice, newPrice);
        }
    }

    /**
     * @notice Fetches the current ETH/USD price from the Chainlink price feed.
     * @return The latest ETH price in USD.
     */
    function getLatestETHPrice() public view returns (uint256) {
        (, int256 price, , , ) = ethPriceFeed.latestRoundData();
        require(price > 0, "Invalid price feed data");
        return uint256(price);
    }
}
合同源代码
文件 5 的 13:ElumphantStaking.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

interface IElumphantStaking {
    function depositByPresale(address _user, uint256 _amount) external;
}

contract ElumphantStaking is IElumphantStaking, Ownable {
    using SafeERC20 for IERC20; // Wrappers around ERC20 operations that throw on failure

    IERC20 public token; // Token to be staked and rewarded
    address public presaleContract; //presale contract address
    uint256 public tokensStakedByPresale; //total tokens staked by preSale
    uint256 public tokensStaked; // Total tokens staked

    uint256 private lastRewardedBlock; // Last block number the user had their rewards calculated
    uint256 private accumulatedRewardsPerShare; // Accumulated rewards per share times REWARDS_PRECISION
    uint256 public rewardTokensPerBlock; // Number of reward tokens minted per block
    uint256 private constant REWARDS_PRECISION = 1e12; // A big number to perform mul and div operations

    uint256 public lockedTime; //To lock the tokens in contract for definite time.
    bool public harvestLock = true; //To lock the harvest/claim.
    uint public endBlock; //At this block,the rewards generation will be stopped.
    uint256 public claimStart; //Users can claim after this time in epoch.

    // Staking user for a pool
    struct PoolStaker {
        uint256 amount; // The tokens quantity the user has staked.
        uint256 stakedTime; //the time at tokens staked
        uint256 Harvestedrewards; // The reward tokens quantity the user  harvested
        uint256 rewardDebt; // The amount relative to accumulatedRewardsPerShare the user can't get as reward
    }

    //  staker address => PoolStaker
    mapping(address => PoolStaker) public poolStakers;
    mapping(address => bool) public isBlacklisted;
    mapping(address => uint) public userLockedRewards;
    // Events
    event Deposit(address indexed user, uint256 amount);
    event Withdraw(address indexed user, uint256 amount);
    event HarvestRewards(address indexed user, uint256 amount);

    constructor(
        address _initialOwner,
        address _tokenAddress,
        address _presaleContract,
        uint256 _rewardTokensPerBlock,
        uint _lockTime,
        uint _endBlock
    ) Ownable(_initialOwner) {
        token = IERC20(_tokenAddress);
        presaleContract = _presaleContract;
        rewardTokensPerBlock = _rewardTokensPerBlock;
        lockedTime = _lockTime;
        endBlock = _endBlock;
        harvestLock = true;
    }

    modifier onlyPresale() {
        require(
            msg.sender == presaleContract,
            "This method can be called by only the Presale contract"
        );
        _;
    }

    /**
     * @dev Deposit tokens to the pool
     */
    function deposit(uint256 _amount) external {
        require(block.number < endBlock, "staking has been ended");
        require(_amount > 0, "Deposit amount can't be zero");

        PoolStaker storage staker = poolStakers[msg.sender];

        // Update pool stakers
        harvestRewards();

        // Update current staker
        staker.amount += _amount;
        staker.rewardDebt =
            (staker.amount * accumulatedRewardsPerShare) /
            REWARDS_PRECISION;
        staker.stakedTime = block.timestamp;

        // Update pool
        tokensStaked += _amount;

        // Deposit tokens
        emit Deposit(msg.sender, _amount);
        token.safeTransferFrom(msg.sender, address(this), _amount);
    }

    /**
     * @dev Deposit tokens to  pool by presale contract
     */
    function depositByPresale(
        address _user,
        uint256 _amount
    ) external override onlyPresale {
        require(block.number < endBlock, "staking has been ended");
        require(_amount > 0, "Deposit amount can't be zero");

        PoolStaker storage staker = poolStakers[_user];

        // Update pool stakers
        _harvestRewards(_user);

        // Update current staker
        staker.amount += _amount;
        staker.rewardDebt =
            (staker.amount * accumulatedRewardsPerShare) /
            REWARDS_PRECISION;
        staker.stakedTime = block.timestamp;

        // Update pool
        tokensStaked += _amount;
        tokensStakedByPresale += _amount;

        // Deposit tokens
        emit Deposit(_user, _amount);
        token.safeTransferFrom(presaleContract, address(this), _amount);
    }

    /**
     * @dev Withdraw all tokens from existing pool
     */
    function withdraw() external {
        PoolStaker memory staker = poolStakers[msg.sender];
        uint256 amount = staker.amount;
        require(
            staker.stakedTime + lockedTime <= block.timestamp &&
                claimStart + lockedTime <= block.timestamp,
            "you are not allowed to withdraw before locked Time"
        );
        require(amount > 0, "Withdraw amount can't be zero");

        // Pay rewards
        harvestRewards();

        //delete staker
        delete poolStakers[msg.sender];

        // Update pool
        tokensStaked -= amount;

        // Withdraw tokens
        emit Withdraw(msg.sender, amount);
        token.safeTransfer(msg.sender, amount);
    }

    /**
     * @dev Harvest user rewards
     */
    function harvestRewards() public {
        _harvestRewards(msg.sender);
    }

    /**
     * @dev Harvest user rewards
     */
    function _harvestRewards(address _user) private {
        require(!isBlacklisted[_user], "This Address is Blacklisted");

        updatePoolRewards();
        PoolStaker storage staker = poolStakers[_user];
        uint256 rewardsToHarvest = ((staker.amount *
            accumulatedRewardsPerShare) / REWARDS_PRECISION) -
            staker.rewardDebt;
        if (rewardsToHarvest == 0) {
            return;
        }

        staker.Harvestedrewards += rewardsToHarvest;
        staker.rewardDebt =
            (staker.amount * accumulatedRewardsPerShare) /
            REWARDS_PRECISION;
        if (!harvestLock) {
            if (userLockedRewards[_user] > 0) {
                rewardsToHarvest += userLockedRewards[_user];
                userLockedRewards[_user] = 0;
            }
            emit HarvestRewards(_user, rewardsToHarvest);
            token.safeTransfer(_user, rewardsToHarvest);
        } else {
            userLockedRewards[_user] += rewardsToHarvest;
        }
    }

    /**
     * @dev Update pool's accumulatedRewardsPerShare and lastRewardedBlock
     */
    function updatePoolRewards() private {
        if (tokensStaked == 0) {
            lastRewardedBlock = block.number;
            return;
        }
        uint256 blocksSinceLastReward = block.number > endBlock
            ? endBlock - lastRewardedBlock
            : block.number - lastRewardedBlock;
        uint256 rewards = blocksSinceLastReward * rewardTokensPerBlock;
        accumulatedRewardsPerShare =
            accumulatedRewardsPerShare +
            ((rewards * REWARDS_PRECISION) / tokensStaked);
        lastRewardedBlock = block.number > endBlock ? endBlock : block.number;
    }

    /**
     *@dev To get the number of rewards that user can get
     */
    function getRewards(address _user) public view returns (uint) {
        if (tokensStaked == 0) {
            return 0;
        }
        uint256 blocksSinceLastReward = block.number > endBlock
            ? endBlock - lastRewardedBlock
            : block.number - lastRewardedBlock;
        uint256 rewards = blocksSinceLastReward * rewardTokensPerBlock;
        uint256 accCalc = accumulatedRewardsPerShare +
            ((rewards * REWARDS_PRECISION) / tokensStaked);
        PoolStaker memory staker = poolStakers[_user];
        return
            ((staker.amount * accCalc) / REWARDS_PRECISION) -
            staker.rewardDebt +
            userLockedRewards[_user];
    }

    function setHarvestLock(bool _harvestlock) external onlyOwner {
        harvestLock = _harvestlock;
    }

    function setPresaleContract(address _presaleContract) external onlyOwner {
        presaleContract = _presaleContract;
    }

    function setLockedTime(uint _time) external onlyOwner {
        lockedTime = _time;
    }

    function setEndBlock(uint _endBlock) external onlyOwner {
        endBlock = _endBlock;
    }

    function setClaimStart(uint _claimStart) external onlyOwner {
        claimStart = _claimStart;
    }

    /**
     * @dev To add users to blacklist which restricts blacklisted users from claiming
     * @param _usersToBlacklist addresses of the users
     */
    function blacklistUsers(
        address[] calldata _usersToBlacklist
    ) external onlyOwner {
        for (uint256 i = 0; i < _usersToBlacklist.length; i++) {
            isBlacklisted[_usersToBlacklist[i]] = true;
        }
    }

    /**
     * @dev To remove users from blacklist which restricts blacklisted users from claiming
     * @param _userToRemoveFromBlacklist addresses of the users
     */
    function removeFromBlacklist(
        address[] calldata _userToRemoveFromBlacklist
    ) external onlyOwner {
        for (uint256 i = 0; i < _userToRemoveFromBlacklist.length; i++) {
            isBlacklisted[_userToRemoveFromBlacklist[i]] = false;
        }
    }
}
合同源代码
文件 6 的 13:Errors.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 *
 * _Available since v5.1._
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedCall();

    /**
     * @dev The deployment failed.
     */
    error FailedDeployment();

    /**
     * @dev A necessary precompile is missing.
     */
    error MissingPrecompile(address);
}
合同源代码
文件 7 的 13:IERC1363.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}
合同源代码
文件 8 的 13:IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";
合同源代码
文件 9 的 13:IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";
合同源代码
文件 10 的 13:Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
合同源代码
文件 11 的 13:Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    bool private _paused;

    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}
合同源代码
文件 12 的 13:ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @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 EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * 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;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    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 making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

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

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

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}
合同源代码
文件 13 的 13:SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}
设置
{
  "compilationTarget": {
    "contracts/ElumphantPresale.sol": "ElumphantPresale"
  },
  "evmVersion": "paris",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"address","name":"_initialOwner","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_usdt","type":"address"},{"internalType":"address","name":"_ethPriceFeed","type":"address"},{"internalType":"uint256","name":"_tokenPriceInUSD","type":"uint256"},{"internalType":"uint256","name":"_tokensPerStep","type":"uint256"},{"internalType":"uint256","name":"_priceIncrement","type":"uint256"},{"internalType":"uint256","name":"_minPurchaseInUSD","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newPrice","type":"uint256"}],"name":"TokenPriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"claimer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"claimer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensClaimedAndStaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"cost","type":"uint256"},{"indexed":false,"internalType":"string","name":"paymentMethod","type":"string"},{"indexed":false,"internalType":"uint256","name":"costInUSD","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalPurchased","type":"uint256"}],"name":"TokensPurchased","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"string","name":"asset","type":"string"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"MAX_PRICE_PUMP_STEPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_recipients","type":"address[]"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"airdrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"buyWithETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"buyWithUSDT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"claimAndStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"depositTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ethPriceFeed","outputs":[{"internalType":"contract AggregatorV3Interface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLatestETHPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minPurchaseInUSD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"presaleEndTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"presaleStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceIncrement","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"purchasedTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minPurchaseInUSD","type":"uint256"}],"name":"setMinPurchaseInUSD","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_stakingContractAddress","type":"address"}],"name":"setStakingContractAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_presaleStartTime","type":"uint256"},{"internalType":"uint256","name":"_presaleEndTime","type":"uint256"},{"internalType":"uint256","name":"_claimStartTime","type":"uint256"}],"name":"setTimes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakedTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingContract","outputs":[{"internalType":"contract IElumphantStaking","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20WithDecimals","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenPriceInUSD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokensPerStep","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokensSoldInCurStep","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalTokensPurchased","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalTokensStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_presaleEndTime","type":"uint256"},{"internalType":"uint256","name":"_claimStartTime","type":"uint256"}],"name":"updatePresaleEndTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newPrice","type":"uint256"}],"name":"updateTokenPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"usdt","outputs":[{"internalType":"contract IERC20WithDecimals","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"withdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"withdrawUSDT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"withdrawUnsoldTokens","outputs":[],"stateMutability":"nonpayable","type":"function"}]