EloXPresale

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/EloXPresale.sol": {
      "content": "// SPDX-License-Identifier: MIT

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

pragma solidity ^0.8.27;

// solhint-disable-next-line max-states-count
contract EloXPresale is Ownable, ReentrancyGuard {
    using Types for Types.Status;
    using Types for Types.PurchaseParams;
    using Errors for *;
    uint256 public tokensSold;
    uint256 public usdRaised;
    uint256 public serviceFeePerc = 3_000; // 3%
    uint256 public projectPerc = 9_000; // 9%
    // Referral reward percentages (in basis points, 1000 = 1%)
    uint256 public LEVEL_1_REWARD = 12000; // 12%
    uint256 public LEVEL_2_REWARD = 6000; // 6%
    uint256 public LEVEL_3_REWARD = 3000; // 3%
    uint256 public constant DENOMINATOR = 100_000; // 100%

    address public treasuryWallet;
    address public projectWallet;
    address public serviceFeeWallet;
    address public vestingContract;

    bool public referralStatus = true;

    struct PhaseDetails {
        uint256 price;
        uint256 tokensAllocated;
        uint256 tokensSold;
    }

    struct UserReferral {
        address referrer;
        uint256 totalEarned;
    }

    struct VestingDetails {
        uint256 cliffEnd;
        uint256 vestingEnd;
    }

    Types.Status public status;

    AggregatorV3Interface private dataFeed; //Chainlink AggregatorV3 for ETH/USD Price
    IERC20 public usdt;
    IERC20 public token;

    mapping(Types.Status => PhaseDetails) public phaseDetails;
    mapping(Types.Status => VestingDetails) public vestingDetails;
    mapping(address => uint256) public eloxInvestment;
    mapping(address => UserReferral) public usersReferral;
    mapping(address => bool) public blacklist;

    event UserBought(address indexed user, uint256 amount, Types.Status indexed status);
    event RewardDistributed(address indexed from, address indexed to, uint8 level, uint256 amount);

    constructor(
        address _token,
        address _usdt,
        address _treasuryWallet,
        address _projectWallet,
        address _serviceFeeWallet
    ) Ownable(msg.sender) {
        dataFeed = AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);
        usdt = IERC20(_usdt);
        token = IERC20(_token);
        treasuryWallet = _treasuryWallet;
        projectWallet = _projectWallet;
        serviceFeeWallet = _serviceFeeWallet;

        //Secret Sale Price
        phaseDetails[Types.Status.Secret] =
            PhaseDetails({price: 0.33 ether, tokensAllocated: 1_050_000 ether, tokensSold: 0});
        //Private Sale Price
        phaseDetails[Types.Status.Private] =
            PhaseDetails({price: 0.5 ether, tokensAllocated: 5_040_000 ether, tokensSold: 0});
        //Public Sale Price
        phaseDetails[Types.Status.Public] =
            PhaseDetails({price: 0.9 ether, tokensAllocated: 840_000 ether, tokensSold: 0});
        //Off Chain
        phaseDetails[Types.Status.OffChain] = PhaseDetails({price: 0 ether, tokensAllocated: 0 ether, tokensSold: 0});
    }

    /**
     * Buy EloX
     *
     *  @dev swaps ETH for EloX
     *
     */
    function buyEloX(address affiliate) external payable nonReentrant {
        if (status == Types.Status.Closed || status == Types.Status.OffChain) revert Errors.PresaleClosed();

        if (usersReferral[msg.sender].referrer == address(0) && affiliate != msg.sender) {
            usersReferral[msg.sender].referrer = affiliate;
        }

        if (blacklist[msg.sender]) revert Errors.UserBlacklisted();

        uint256 valueSent = msg.value;
        if (valueSent == 0) revert Errors.ZeroAmount();

        uint256 _amountToReceiveInUsd = getConversionRate(valueSent);
        uint256 _amountToSend = getEloXSaleAmountForEth(_amountToReceiveInUsd);

        if (phaseDetails[status].tokensSold + _amountToSend > phaseDetails[status].tokensAllocated) {
            revert Errors.AllocationExceeded();
        }
        if (token.balanceOf(address(this)) < _amountToSend) revert Errors.InsufficientBalance();

        tokensSold += _amountToSend;
        phaseDetails[status].tokensSold += _amountToSend;
        usdRaised += _amountToReceiveInUsd;
        eloxInvestment[msg.sender] += _amountToSend;

        uint256 serviceFeeAmount = (valueSent * serviceFeePerc) / DENOMINATOR;
        valueSent -= serviceFeeAmount;
        (bool successServiceFee,) = payable(serviceFeeWallet).call{value: serviceFeeAmount}("");
        if (!successServiceFee) revert Errors.TransferFailed();

        uint256 projectAmount = (valueSent * projectPerc) / DENOMINATOR;
        uint256 reward1 = (valueSent * LEVEL_1_REWARD) / DENOMINATOR;
        uint256 reward2 = (valueSent * LEVEL_2_REWARD) / DENOMINATOR;
        uint256 reward3 = (valueSent * LEVEL_3_REWARD) / DENOMINATOR;
        uint256 affiliateAmount = reward1 + reward2 + reward3;
        uint256 treasuryAmount = valueSent - projectAmount - affiliateAmount;
        uint256 affiliateLeftOver = affiliateAmount;
        (bool successProjectFee,) = payable(projectWallet).call{value: projectAmount}("");
        if (!successProjectFee) revert Errors.TransferFailed();

        if (referralStatus && affiliate != address(0)) {
            address level1 = usersReferral[msg.sender].referrer;
            address level2 = level1 != address(0) ? usersReferral[level1].referrer : address(0);
            address level3 = level2 != address(0) ? usersReferral[level2].referrer : address(0);

            if (level1 != address(0)) {
                affiliateLeftOver -= reward1;
                usersReferral[level1].totalEarned += reward1;
                (bool successAffiliateLevel1Amount,) = payable(level1).call{value: reward1}("");
                if (!successAffiliateLevel1Amount) revert Errors.TransferFailed();
                emit RewardDistributed(msg.sender, level1, 1, reward1);
            }

            if (level2 != address(0)) {
                affiliateLeftOver -= reward2;
                usersReferral[level2].totalEarned += reward2;
                (bool successAffiliateLevel2Amount,) = payable(level2).call{value: reward2}("");
                if (!successAffiliateLevel2Amount) revert Errors.TransferFailed();
                emit RewardDistributed(msg.sender, level2, 2, reward2);
            }

            if (level3 != address(0) && level3 != level1) {
                affiliateLeftOver -= reward3;
                usersReferral[level3].totalEarned += reward3;
                (bool successAffiliateLevel3Amount,) = payable(level3).call{value: reward3}("");
                if (!successAffiliateLevel3Amount) revert Errors.TransferFailed();
                emit RewardDistributed(msg.sender, level3, 3, reward3);
            }
        }

        (bool successTreasury,) = payable(treasuryWallet).call{value: treasuryAmount + affiliateLeftOver}("");
        if (!successTreasury) revert Errors.TransferFailed();

        bool successVestingTransfer = token.transfer(vestingContract, _amountToSend);
        if (!successVestingTransfer) revert Errors.TransferFailed();

        IEloXVesting(vestingContract).handlePurchase(msg.sender, _amountToSend, getVestingDetails(status));
        emit UserBought(msg.sender, _amountToSend, status);
    }

    /**
     * Buy EloXUsdt
     *  @param _amount the value of ETH or USD to be spent to buy EloX Tokens
     *
     *  @dev swaps USDT for EloX
     *
     */
    function buyEloXUsdt(uint256 _amount, address affiliate) external payable nonReentrant {
        //usdt is 6 decimal places
        if (status == Types.Status.Closed || status == Types.Status.OffChain) revert Errors.PresaleClosed();

        if (usersReferral[msg.sender].referrer == address(0) && affiliate != msg.sender) {
            usersReferral[msg.sender].referrer = affiliate;
        }

        if (blacklist[msg.sender]) revert Errors.UserBlacklisted();

        if (_amount == 0) revert Errors.ZeroAmount();

        uint256 _amountToReceive = _amount;

        if (usdt.balanceOf(msg.sender) < _amountToReceive) revert Errors.InsufficientBalance();

        usdRaised += _amountToReceive;
        uint256 _amountToSend = getEloXSaleAmountForUsd(_amount);

        if (phaseDetails[status].tokensSold + _amountToSend > phaseDetails[status].tokensAllocated) {
            revert Errors.AllocationExceeded();
        }
        if (token.balanceOf(address(this)) < _amountToSend) revert Errors.InsufficientBalance();

        tokensSold += _amountToSend;
        phaseDetails[status].tokensSold += _amountToSend;
        eloxInvestment[msg.sender] += _amountToSend;

        uint256 serviceFeeAmount = (_amountToReceive * serviceFeePerc) / DENOMINATOR;
        _amountToReceive -= serviceFeeAmount;
        bool serviceFeeTransfer = usdt.transferFrom(msg.sender, serviceFeeWallet, serviceFeeAmount);
        if (!serviceFeeTransfer) revert Errors.TransferFailed();

        uint256 projectAmount = (_amountToReceive * projectPerc) / DENOMINATOR;
        uint256 reward1 = (_amountToReceive * LEVEL_1_REWARD) / DENOMINATOR;
        uint256 reward2 = (_amountToReceive * LEVEL_2_REWARD) / DENOMINATOR;
        uint256 reward3 = (_amountToReceive * LEVEL_3_REWARD) / DENOMINATOR;
        uint256 affiliateAmount = reward1 + reward2 + reward3;
        uint256 treasuryAmount = _amountToReceive - projectAmount - affiliateAmount;
        uint256 affiliateLeftOver = affiliateAmount;
        bool projectFeeTransfer = usdt.transferFrom(msg.sender, projectWallet, projectAmount);
        if (!projectFeeTransfer) revert Errors.TransferFailed();

        if (referralStatus && affiliate != address(0)) {
            address level1 = usersReferral[msg.sender].referrer;
            address level2 = level1 != address(0) ? usersReferral[level1].referrer : address(0);
            address level3 = level2 != address(0) ? usersReferral[level2].referrer : address(0);

            if (level1 != address(0)) {
                affiliateLeftOver -= reward1;
                usersReferral[level1].totalEarned += reward1;
                bool level1Transfer = usdt.transferFrom(msg.sender, level1, reward1);
                if (!level1Transfer) revert Errors.TransferFailed();
                emit RewardDistributed(msg.sender, level1, 1, reward1);
            }

            if (level2 != address(0)) {
                affiliateLeftOver -= reward2;
                usersReferral[level2].totalEarned += reward2;
                bool level2Transfer = usdt.transferFrom(msg.sender, level2, reward2);
                if (!level2Transfer) revert Errors.TransferFailed();
                emit RewardDistributed(msg.sender, level2, 2, reward2);
            }

            if (level3 != address(0) && level3 != level1) {
                affiliateLeftOver -= reward3;
                usersReferral[level3].totalEarned += reward3;
                bool level3Transfer = usdt.transferFrom(msg.sender, level3, reward3);
                if (!level3Transfer) revert Errors.TransferFailed();
                emit RewardDistributed(msg.sender, level3, 3, reward3);
            }
        }

        bool treasuryTransfer = usdt.transferFrom(msg.sender, treasuryWallet, treasuryAmount + affiliateLeftOver);
        if (!treasuryTransfer) revert Errors.TransferFailed();

        bool vestingTransfer = token.transfer(vestingContract, _amountToSend);
        if (!vestingTransfer) revert Errors.TransferFailed();

        IEloXVesting(vestingContract).handlePurchase(msg.sender, _amountToSend, getVestingDetails(status));
        emit UserBought(msg.sender, _amountToSend, status);
    }

    function getEloXSaleAmountForEth(uint256 baseAmount) public view returns (uint256) {
        return (baseAmount * 1e18) / phaseDetails[status].price;
    }

    function getEloXSaleAmountForUsd(uint256 baseAmount) public view returns (uint256) {
        return (baseAmount * 1e30) / phaseDetails[status].price;
    }

    function getPrice() internal view returns (uint256) {
        (, int256 answer,,,) = dataFeed.latestRoundData();
        // ETH/USD rate in 18 digit
        // forge-lint: disable-next-line(unsafe-typecast)
        return uint256(answer * 1e10);
    }

    function getConversionRate(uint256 ethAmount) public view returns (uint256) {
        uint256 ethPrice = getPrice();
        uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1e18;
        // the actual ETH/USD conversation rate, after adjusting the extra 0s.
        return ethAmountInUsd;
    }

    function getStatus() public view returns (Types.Status) {
        return status;
    }

    /// @notice View the user's full referral structure
    function getReferrers(address user) external view returns (address, address, address) {
        address level1 = usersReferral[user].referrer;
        address level2 = level1 != address(0) ? usersReferral[level1].referrer : address(0);
        address level3 = level2 != address(0)
            ? usersReferral[level2].referrer == level1 ? address(0) : usersReferral[level2].referrer
            : address(0);

        return (level1, level2, level3);
    }

    function getVestingDetails(Types.Status phase) public view returns (Types.PurchaseParams memory) {
        return Types.PurchaseParams({
            status: phase, cliffEnd: vestingDetails[phase].cliffEnd, vestingEnd: vestingDetails[phase].vestingEnd
        });
    }

    // Update status by passing uint into input
    function setStatus(Types.Status _status) public onlyOwner {
        if (_status != Types.Status.Closed) {
            require(
                vestingDetails[_status].cliffEnd != 0 && vestingDetails[_status].vestingEnd != 0,
                "Vesting details not set."
            );
        }

        status = _status;
    }

    // Update status by passing uint into input
    function updateStatusInfo(Types.Status _status, uint256 _cliffEnd, uint256 _vestingEnd, uint256 _price)
        public
        onlyOwner
    {
        vestingDetails[_status].cliffEnd = _cliffEnd;
        vestingDetails[_status].vestingEnd = _vestingEnd;
        phaseDetails[_status].price = _price;
    }

    function setVestingContract(address _vesting) external onlyOwner {
        vestingContract = _vesting;
    }

    function withdrawPreSaleTokens(uint256 _amount) external onlyOwner {
        if (token.balanceOf(address(this)) < _amount) revert Errors.InsufficientBalance();
        bool success = token.transfer(msg.sender, _amount);
        if (!success) revert Errors.TransferFailed();
    }

    function changeTreasuryDetails(address _treasury) external onlyOwner {
        treasuryWallet = _treasury;
    }

    function changeProjectDetails(address _project, uint256 _projectPerc) external onlyOwner {
        projectWallet = _project;
        projectPerc = _projectPerc;
    }

    function changeAffiliatePerc(uint256 level1, uint256 level2, uint256 level3) external onlyOwner {
        LEVEL_1_REWARD = level1;
        LEVEL_2_REWARD = level2;
        LEVEL_3_REWARD = level3;
    }

    function updateVestingContract(address _vesting) external onlyOwner {
        vestingContract = _vesting;
    }

    function blacklistUsers(address[] memory user, bool[] memory bLStatus) external onlyOwner {
        for (uint256 i = 0; i < user.length; i++) {
            blacklist[user[i]] = bLStatus[i];
        }
    }

    function setReferralStatus(bool ref_status) external onlyOwner {
        referralStatus = ref_status;
    }

    function withdrawEther(uint256 _amount) external onlyOwner {
        (bool success,) = msg.sender.call{value: _amount}("");
        if (!success) revert Errors.TransferFailed();
    }

    function withdrawToken(address _tokenAddress, uint256 _amount) external onlyOwner {
        bool success = IERC20(_tokenAddress).transfer(msg.sender, _amount);
        if (!success) revert Errors.TransferFailed();
    }

    function creditEloXForOffChainBuyers(
        address[] memory _users,
        uint256[] memory _amounts,
        uint256 cliffEnd,
        uint256 vestingEnd
    ) external onlyOwner {
        uint256 totalAmount;
        Types.PurchaseParams memory params =
            Types.PurchaseParams({status: Types.Status.OffChain, cliffEnd: cliffEnd, vestingEnd: vestingEnd});
        phaseDetails[Types.Status.OffChain].tokensSold += totalAmount;
        for (uint256 i = 0; i < _users.length; i++) {
            totalAmount += _amounts[i];
            eloxInvestment[_users[i]] += _amounts[i];
            IEloXVesting(vestingContract).handlePurchase(_users[i], _amounts[i], params);
            emit UserBought(_users[i], _amounts[i], Types.Status.OffChain);
        }
        if (token.balanceOf(owner()) < totalAmount) revert Errors.InsufficientBalance();
        bool success = token.transferFrom(msg.sender, vestingContract, totalAmount);
        if (!success) revert Errors.TransferFailed();
    }
}
"
    },
    "node_modules/@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol": {
      "content": "// 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);
}
"
    },
    "node_modules/@openzeppelin/contracts/utils/ReentrancyGuard.sol": {
      "content": "// 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;
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)

pragma solidity >=0.4.16;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
    },
    "src/interfaces/IEloXVesting.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import {Types} from "../shared/Types.sol";

interface IEloXVesting {
    function handlePurchase(address user, uint256 amount, Types.PurchaseParams memory params) external;
}
"
    },
    "node_modules/@openzeppelin/contracts/access/Ownable.sol": {
      "content": "// 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);
    }
}
"
    },
    "src/shared/Types.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

library Types {
    enum Status {
        Closed,
        Secret,
        Private,
        Public,
        OffChain
    }

    struct PurchaseParams {
        Status status;
        uint256 cliffEnd;
        uint256 vestingEnd;
    }
}
"
    },
    "src/shared/Errors.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

library Errors {
    error PresaleClosed();
    error ZeroAmount();
    error AllocationExceeded();
    error InsufficientBalance();
    error TransferFailed();
    error InvalidCaller();
    error UserBlacklisted();
}
"
    },
    "node_modules/@openzeppelin/contracts/utils/Context.sol": {
      "content": "// 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;
    }
}
"
    }
  },
  "settings": {
    "remappings": [
      "@openzeppelin/=node_modules/@openzeppelin/",
      "@chainlink/=node_modules/@chainlink/",
      "forge-std/=lib/forge-std/src/"
    ],
    "optimizer": {
      "enabled": true,
      "runs": 200,
      "details": {}
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "ipfs",
      "appendCBOR": true
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "prague",
    "viaIR": true
  }
}}

Tags:
ERC20, Multisig, Multi-Signature, Factory, Oracle|addr:0x795c4bbfbcb33ce95949bc089f366f85fcaf67a9|verified:true|block:23675133|tx:0xd7e640ae2eed8afa151496672aa0ecdf2d9e9b3e1e8de1d3fb78a81da2da5c42|first_check:1761652656

Submitted on: 2025-10-28 12:57:38

Comments

Log in to comment.

No comments yet.