账户
0x3f...fea6
0x3f...feA6

0x3f...feA6

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.28+commit.7893614a
语言
Solidity
合同源代码
文件 1 的 1:privateSaleETH.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.28;

/**
 * @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;
    }
}

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);
    }
}

interface IOpenNFT {
    struct UserNFTs {
        uint256 tokenId;
        uint256 strength;
    }

    function getUserNFTs(
        address _address
    ) external view returns (UserNFTs[] memory);
}

interface IERC20 {
    function transfer(
        address recipient,
        uint256 amount
    ) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
}

interface IUniswapV2Router02 {
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);

    function swapExactETHForTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable returns (uint[] memory amounts);

    function getAmountsOut(
        uint amountIn,
        address[] calldata path
    ) external view returns (uint[] memory amounts);

    function getAmountsIn(
        uint amountOut,
        address[] calldata path
    ) external view returns (uint[] memory amounts);
}

contract TakoSwapPrivateSaleETH is Ownable {
    uint256 public MAX_BUY = 100_000 ether;
    uint256 public MIN_BUY_PER_TX = 100 ether;
    uint256 public MAX_BUY_PER_ADDRESS = 5_000 ether;
    uint256 public totalContributed;
    uint256 public startTime;

    IOpenNFT public openNFT;
    IERC20 public OGPU;
    IUniswapV2Router02 public uniswapRouter;

    address public OGPUAddress;
    address public openNFTAddress;
    address public uniswapRouterAddress;
    address public WETH;

    bool public isSaleOpen = false;
    bool public reentrancyLock = false;

    mapping(address => uint256) public contributionAmount;
    address[] public contributors;

    enum NFTType {
        Diamond,
        Common,
        None
    }
    
    // Events
    event Contributed(address indexed buyer, uint256 ogpuAmount);
    event SaleStatusChanged(bool isOpen);
    event SalePublicStatusChanged(bool isPublic);
    event AddressesSet(address ogpu, address nft, address router, address weth);
    event FundsWithdrawn(address indexed to, uint256 amount);
    event TokensWithdrawn(address indexed token, address indexed to, uint256 amount);
    event LimitsUpdated(uint256 maxBuy, uint256 minBuyPerTx, uint256 maxBuyPerAddress);

    constructor(
        address initialOwner,
        address _ogpuAddress,
        address _openNFTAddress,
        address _uniswapRouterAddress,
        address _weth
    ) Ownable(initialOwner) {
        require(_ogpuAddress != address(0), "OGPU address cannot be zero");
        require(_openNFTAddress != address(0), "NFT address cannot be zero");
        require(_uniswapRouterAddress != address(0), "Router address cannot be zero");
        require(_weth != address(0), "WETH address cannot be zero");
        
        OGPUAddress = _ogpuAddress;
        openNFTAddress = _openNFTAddress;
        uniswapRouterAddress = _uniswapRouterAddress;
        WETH = _weth;
        
        OGPU = IERC20(_ogpuAddress);
        openNFT = IOpenNFT(_openNFTAddress);
        uniswapRouter = IUniswapV2Router02(_uniswapRouterAddress);
        
        emit AddressesSet(_ogpuAddress, _openNFTAddress, _uniswapRouterAddress, _weth);
    }

    receive() external payable {}

    modifier stopped() {
        require(isSaleOpen, "TakoSwapPrivateSale: sale is stopped");
        _;
    }

    modifier nonReentrant() {
        require(!reentrancyLock, "TakoSwapPrivateSale: reentrant call");
        reentrancyLock = true;
        _;
        reentrancyLock = false;
    }

    function getBestNFT(address _address) public view returns (NFTType) {
        if (_address == address(0)) return NFTType.None;
        
        IOpenNFT.UserNFTs[] memory userNFTs = openNFT.getUserNFTs(_address);
        if (userNFTs.length == 0) return NFTType.None;
        
        bool isCommon = false;

        for (uint256 i = 0; i < userNFTs.length; i++) {
            if (userNFTs[i].strength > 10) {
                return NFTType.Diamond; 
            } 
            else if (userNFTs[i].strength > 0) {
                isCommon = true;
            }
        }
        
        if (isCommon) return NFTType.Common;
        return NFTType.None;
    }

    function getContributors() external view returns (address[] memory) {
        return contributors;
    }

    function getContributorAmount(
        address _address
    ) external view returns (uint256) {
        return contributionAmount[_address];
    }

    function contributionChecks(
        address buyerAddress,
        uint256 amount
    ) internal view {
        require(buyerAddress != address(0), "Invalid buyer address");
        require(
            amount >= MIN_BUY_PER_TX,
            "TakoSwapPrivateSale: amount must be greater than minimum"
        );
        require(
            contributionAmount[buyerAddress] + amount <= MAX_BUY_PER_ADDRESS,
            "TakoSwapPrivateSale: max buy per address exceeded"
        );
        require(
            totalContributed + amount <= MAX_BUY,
            "TakoSwapPrivateSale: presale is filled"
        );
        require(
            startTime > 0,
            "TakoSwapPrivateSale: presale is not started yet"
        );

        NFTType nftType = getBestNFT(buyerAddress);

        if (nftType == NFTType.Diamond && block.timestamp < startTime + 5 minutes) {
            require(
                totalContributed + amount <= MAX_BUY / 2,
                "TakoSwapPrivateSale: presale is filled for diamond holders"
            );
        } else if (nftType == NFTType.Common) {
            // Common holders get access after 5 minutes
            require(
                block.timestamp > startTime + 5 minutes,
                "TakoSwapPrivateSale: presale is not started yet for common holders"
            );
        } else {
            require(
                block.timestamp > startTime + 35 minutes,
                "TakoSwapPrivateSale: Sale not yet public. Buy OpenNFT for Whitelist Slot"
            );
        }
    }

    function buyOGPU(
        uint256 amount,
        address _buyerAddress
    ) internal returns (uint256) {
        address[] memory path = new address[](2);
        path[0] = address(WETH);
        path[1] = address(OGPU);

        uint256 amountOutMin = uniswapRouter.getAmountsOut(
            amount,
            path
        )[1];

        // 7% slippage is acceptable
        uint256 slippage = (amountOutMin * 90) / 100;
        uint256 balanceBefore = OGPU.balanceOf(address(this));
        // swap
        require(
            uniswapRouter.swapExactETHForTokens{value: amount}(
                slippage,
                path,
                address(this),
                block.timestamp
            )[1] > 0,
            "TakoSwapPrivateSale: swap failed"
        );
        uint256 balanceAfter = OGPU.balanceOf(address(this));
        require(
            balanceAfter - balanceBefore > 0,
            "TakoSwapPrivateSale: swap failed"
        );

        if (balanceAfter - balanceBefore > MAX_BUY_PER_ADDRESS) {
            uint256 excess = balanceAfter - balanceBefore - (MAX_BUY_PER_ADDRESS - contributionAmount[_buyerAddress]);
            require(
                OGPU.transfer(_msgSender(), excess),
                "TakoSwapPrivateSale: transfer failed"
            );
            return MAX_BUY_PER_ADDRESS - contributionAmount[_buyerAddress];
        }
        
        return balanceAfter - balanceBefore;
    }

    function contribute(uint256 amount) external payable nonReentrant stopped {
        require(msg.value == amount, "TakoSwapPrivateSale: insufficient funds");
        require(amount > 0, "Amount must be greater than 0");
        address buyerAddress = _msgSender();
        uint256 ogpuAmount = buyOGPU(amount, buyerAddress);
        contributionChecks(buyerAddress, ogpuAmount);

        if (contributionAmount[buyerAddress] == 0) {
            contributors.push(buyerAddress);
        }

        contributionAmount[buyerAddress] += ogpuAmount;
        totalContributed += ogpuAmount;
        
        emit Contributed(buyerAddress, ogpuAmount);
    }

    function contributeOGPU(
        uint256 amount
    ) external nonReentrant stopped {
        require(amount > 0, "Amount must be greater than 0");
        address buyerAddress = _msgSender();
        contributionChecks(buyerAddress, amount);
        
        require(
            OGPU.transferFrom(buyerAddress, address(this), amount),
            "TakoSwapPrivateSale: transfer failed"
        );

        if (contributionAmount[buyerAddress] == 0) {
            contributors.push(buyerAddress);
        }

        contributionAmount[buyerAddress] += amount;
        totalContributed += amount;
        
        emit Contributed(buyerAddress, amount);
    }

    function stopSale() external onlyOwner {
        startTime = 0;
        isSaleOpen = false;
        emit SaleStatusChanged(false);
    }

    function startSale() external onlyOwner {
        startTime = block.timestamp;
        isSaleOpen = true;
        emit SaleStatusChanged(true);
    }
    
    function updateLimits(
        uint256 _maxBuy, 
        uint256 _minBuyPerTx, 
        uint256 _maxBuyPerAddress
    ) external onlyOwner {
        require(_minBuyPerTx <= _maxBuyPerAddress, "Min buy cannot exceed max buy per address");
        require(_maxBuyPerAddress <= _maxBuy, "Max buy per address cannot exceed max buy");
        
        MAX_BUY = _maxBuy;
        MIN_BUY_PER_TX = _minBuyPerTx;
        MAX_BUY_PER_ADDRESS = _maxBuyPerAddress;
        
        emit LimitsUpdated(_maxBuy, _minBuyPerTx, _maxBuyPerAddress);
    }

    function withdraw() external onlyOwner nonReentrant {
        uint256 balance = address(this).balance;
        require(balance > 0, "No ETH to withdraw");
        payable(owner()).transfer(balance);
        emit FundsWithdrawn(owner(), balance);
    }

    function withdrawOGPU() external onlyOwner nonReentrant {
        uint256 balance = OGPU.balanceOf(address(this));
        require(balance > 0, "No OGPU to withdraw");
        require(
            OGPU.transfer(owner(), balance),
            "TakoSwapPrivateSale: transfer failed"
        );
        emit TokensWithdrawn(address(OGPU), owner(), balance);
    }

    function withdrawTokens(
        address tokenAddress
    ) external onlyOwner nonReentrant {
        require(tokenAddress != address(0), "Invalid token address");
        IERC20 token = IERC20(tokenAddress);
        uint256 balance = token.balanceOf(address(this));
        require(balance > 0, "No tokens to withdraw");
        require(
            token.transfer(owner(), balance),
            "TakoSwapPrivateSale: transfer failed"
        );
        emit TokensWithdrawn(tokenAddress, owner(), balance);
    }
}
设置
{
  "compilationTarget": {
    "contracts/privateSaleETH.sol": "TakoSwapPrivateSaleETH"
  },
  "evmVersion": "paris",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"address","name":"initialOwner","type":"address"},{"internalType":"address","name":"_ogpuAddress","type":"address"},{"internalType":"address","name":"_openNFTAddress","type":"address"},{"internalType":"address","name":"_uniswapRouterAddress","type":"address"},{"internalType":"address","name":"_weth","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"ogpu","type":"address"},{"indexed":false,"internalType":"address","name":"nft","type":"address"},{"indexed":false,"internalType":"address","name":"router","type":"address"},{"indexed":false,"internalType":"address","name":"weth","type":"address"}],"name":"AddressesSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"ogpuAmount","type":"uint256"}],"name":"Contributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxBuy","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minBuyPerTx","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxBuyPerAddress","type":"uint256"}],"name":"LimitsUpdated","type":"event"},{"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":"bool","name":"isPublic","type":"bool"}],"name":"SalePublicStatusChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isOpen","type":"bool"}],"name":"SaleStatusChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensWithdrawn","type":"event"},{"inputs":[],"name":"MAX_BUY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BUY_PER_ADDRESS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_BUY_PER_TX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OGPU","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OGPUAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"contribute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"contributeOGPU","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"contributionAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"contributors","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"getBestNFT","outputs":[{"internalType":"enum TakoSwapPrivateSaleETH.NFTType","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"getContributorAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getContributors","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isSaleOpen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"openNFT","outputs":[{"internalType":"contract IOpenNFT","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"openNFTAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reentrancyLock","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stopSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalContributed","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":"uniswapRouter","outputs":[{"internalType":"contract IUniswapV2Router02","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"uniswapRouterAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxBuy","type":"uint256"},{"internalType":"uint256","name":"_minBuyPerTx","type":"uint256"},{"internalType":"uint256","name":"_maxBuyPerAddress","type":"uint256"}],"name":"updateLimits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawOGPU","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"withdrawTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]