Cuentas
0xb3...5cc0
0xb3...5CC0

0xb3...5CC0

$500
¡El código fuente de este contrato está verificado!
Metadatos del Contrato
Compilador
0.8.20+commit.a1b79de6
Idioma
Solidity
Código Fuente del Contrato
Archivo 1 de 1: TokenPresale.sol
// SPDX-License-Identifier: MIT

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

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

interface IToken {
    function totalSupply() external view returns (uint256);

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

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

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

    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    function decimals() external view returns (uint8);
}

interface AggregatorV3Interface {
    function latestRoundData()
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        );
}

contract TokenPresale is Ownable {
    IToken public token;
    IToken public USDT;
    AggregatorV3Interface public priceFeedeth;

    uint256 public tokenPrice;

    enum Rounds {
        Round1,
        Round2,
        Round3,
        Final
    }
    Rounds public currentRound;

    uint256 public totalTokensSold;
    uint256 public totalRaisedETH;
    uint256 public totalRaisedUSDT;

    uint256 public ETHRaised;
    uint256 public USDTRaised;

    uint256 public numberOfWalletsToDistribute;
    address[] public distributionWallets;

    uint256 public round1Bonus = 10; // 10% bonus
    uint256 public round2Bonus = 7; // 7% bonus
    uint256 public round3Bonus = 5; // 5% bonus
    uint256 public finalRoundBonus = 5; // Starting at 5%, decreases

    struct UserRoundData {
        uint256 tokensPurchased;
        uint256 bonusTokens;
        bool claimed;
    }

    mapping(address => mapping(Rounds => UserRoundData)) public userRoundData;
    mapping(Rounds => bool) public roundStatus;

    constructor(IToken _token) Ownable(msg.sender) {
        priceFeedeth = AggregatorV3Interface(
            0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419
        );
        USDT = IToken(0xdAC17F958D2ee523a2206206994597C13D831ec7);
        token = _token;
        tokenPrice = 35210000000000000000;
        currentRound = Rounds.Round1;
    }

    function getLatestPriceETH() public view returns (uint256) {
        (, int256 price, , , ) = priceFeedeth.latestRoundData();
        return uint256(price);
    }

    // Buy tokens with ETH
    function buyWithETH() external payable {
        require(msg.value > 0, "Invalid amount");
        uint256 ETHAmount = msg.value;
        uint256 numberOfTokens = ethToToken(ETHAmount);
        uint256 tokensWithBonus = numberOfTokens + calculateBonus(numberOfTokens);

        uint256 totalTokensForSale = tokenForSale();
        require( totalTokensSold + tokensWithBonus <= totalTokensForSale, "Insufficient tokens in presale" );

        userRoundData[msg.sender][currentRound].tokensPurchased += numberOfTokens;
        userRoundData[msg.sender][currentRound].bonusTokens += calculateBonus(numberOfTokens);
        bool transferSuccess = token.transferFrom(owner(), address(this), tokensWithBonus);
        require(transferSuccess, "Presale Token transferFrom failed");

        totalTokensSold += tokensWithBonus;
        totalRaisedETH += ETHAmount;
        ETHRaised += ETHAmount;

        if (ETHRaised >= 1 ether) {
            distributeETH();
        }

        if(USDTRaised >= 1000 ether){
            distributeUSDT();
        }

    }

    // Buy tokens with USDT
    function buyWithUSDT(uint256 usdtAmount) external {
        require(usdtAmount > 0, "Invalid amount");
        uint256 numberOfTokens = usdtToToken(usdtAmount);
        uint256 tokensWithBonus = numberOfTokens +calculateBonus(numberOfTokens);

        uint256 totalTokensForSale = tokenForSale();
        require(totalTokensSold + tokensWithBonus <= totalTokensForSale,"Insufficient tokens in presale");

        USDT.transferFrom(msg.sender, address(this), usdtAmount);

        userRoundData[msg.sender][currentRound] .tokensPurchased += numberOfTokens;
        userRoundData[msg.sender][currentRound].bonusTokens += calculateBonus(numberOfTokens);

        bool transferSuccess = token.transferFrom(owner(), address(this), tokensWithBonus);
        require(transferSuccess, "Presale Token transferFrom failed");

        totalTokensSold += tokensWithBonus;
        totalRaisedUSDT += usdtAmount;
        USDTRaised += usdtAmount;
        if (ETHRaised >= 1 ether) {
            distributeETH();
        }

        if(USDTRaised >= 1000 ether){
            distributeUSDT();
        }
    }

    function updateUserRoundData(
        address user,
        uint256 tokensPurchased,
        uint256 round
    ) external onlyOwner {
        require(user != address(0), "Invalid user address");
        require(tokensPurchased > 0, "Amount greater than zero");

        UserRoundData storage userData = userRoundData[user][currentRound];

        userData.tokensPurchased = tokensPurchased;
        uint256 bonusTokens = calculate(tokensPurchased, round);
        userData.bonusTokens = bonusTokens;
    }

    // Claim purchased tokens during the airdrop phase
    function claimTokens(Rounds _round) external {
        require(roundStatus[_round] == true, "Round not ended yet");

        UserRoundData storage userData = userRoundData[msg.sender][_round];
        require(!userData.claimed, "Already claimed for this round");
        require(
            userData.tokensPurchased > 0,
            "No tokens to claim for this round"
        );

        uint256 claimableTokens = userData.tokensPurchased +
            userData.bonusTokens;
        userData.claimed = true;

        require(
            token.transfer(msg.sender, claimableTokens),
            "Token transfer failed"
        );
    }

    // Calculate bonus based on the current round
    function calculateBonus(uint256 tokens) internal view returns (uint256) {
        if (currentRound == Rounds.Round1) {
            return (tokens * round1Bonus) / 100;
        } else if (currentRound == Rounds.Round2) {
            return (tokens * round2Bonus) / 100;
        } else if (currentRound == Rounds.Round3) {
            return (tokens * round3Bonus) / 100;
        } else if (currentRound == Rounds.Final) {
            uint256 dynamicBonus = (finalRoundBonus *
                (tokenForSale() - totalTokensSold)) / tokenForSale();
            return (tokens * dynamicBonus) / 100;
        } else {
            return 0;
        }
    }

    function calculate(uint256 tokens, uint256 round)
        internal
        view
        returns (uint256)
    {
        if (round == 1) {
            return (tokens * round1Bonus) / 100;
        } else if (round == 2) {
            return (tokens * round2Bonus) / 100;
        } else if (round == 3) {
            return (tokens * round3Bonus) / 100;
        } else if (round == 4) {
            uint256 dynamicBonus = (finalRoundBonus *
                (tokenForSale() - totalTokensSold)) / tokenForSale();
            return (tokens * dynamicBonus) / 100;
        } else {
            return 0;
        }
    }

    function usdtToToken(uint256 _amount) public view returns (uint256) {
         uint8 usdtDecimals = IToken(address(USDT)).decimals();
        uint256 usdtAmountInWei = _amount * (10**(18 - usdtDecimals));

        uint256 totalTokens = (usdtAmountInWei * tokenPrice) / (1 ether);
        uint256 tokens = (totalTokens * (10**token.decimals())) / (1 ether);

        return tokens;
    }

    function ethToToken(uint256 _amount) public view returns (uint256) {
        uint256 ethToUsd = (_amount * getLatestPriceETH()) / (1 ether);
        uint256 numberOfTokens = (ethToUsd * tokenPrice) / 1 ether;
        uint256 tokens = (numberOfTokens * (10**token.decimals())) / 1e8;
        return tokens;
    }

    function getTotalInUSDT() public view returns (uint256) {
        uint256 ethToUsdT = (totalRaisedETH * getLatestPriceETH()) / 1e8;

        uint256 totalRaisedInUSDT = ethToUsdT + totalRaisedUSDT;

        return totalRaisedInUSDT;
    }

    function distributeUSDT() internal {
        require(
            numberOfWalletsToDistribute > 0,
            "Wallets distribution not set"
        );

        uint256 amountPerWallet = USDTRaised / numberOfWalletsToDistribute;

        for (uint256 i = 0; i < numberOfWalletsToDistribute; i++) {
            USDT.transfer(distributionWallets[i], amountPerWallet);
        }

        USDTRaised = 0;
    }

    function distributeETH() internal {
        require(
            numberOfWalletsToDistribute > 0,
            "Wallets distribution not set"
        );

        uint256 amountPerWallet = ETHRaised / numberOfWalletsToDistribute;

        for (uint256 i = 0; i < numberOfWalletsToDistribute; i++) {
            payable(distributionWallets[i]).transfer(amountPerWallet);
        }

        ETHRaised = 0;
    }

    function tokenForSale() public view returns (uint256) {
        return token.allowance(owner(), address(this));
    }

    function setDistributionWallets(address[] memory wallets)
        external
        onlyOwner
    {
        require(wallets.length > 0, "Must provide at least one wallet");

        delete distributionWallets;

        for (uint256 i = 0; i < wallets.length; i++) {
            distributionWallets.push(wallets[i]);
        }

        numberOfWalletsToDistribute = wallets.length;
    }

    function setTokenPrice(uint256 _newPrice) external onlyOwner {
        tokenPrice = _newPrice;
    }

    // End a presale round
    function endRound(Rounds _round) external onlyOwner {
        require(roundStatus[_round] == false, "Round already ended");
        roundStatus[_round] = true;
    }

    // Start a presale round
    function startRound(Rounds _round) external onlyOwner {
        require(roundStatus[_round] == true, "Round is still active");
        roundStatus[_round] = false;
    }

    // Withdraw raised funds (ETH) by owner
    function withdrawETH() external onlyOwner {
        payable(owner()).transfer(address(this).balance);
    }

     // Withdraw  USDT by owner
    function withdrawUSDT() external onlyOwner {
        uint256 contractBalance = USDT.balanceOf(address(this));
        require(contractBalance > 0, "No USDT balance to withdraw");
        require(
            USDT.transfer(owner(), contractBalance),
            "USDT withdrawal failed"
        );
    }

    // Emergency token recovery function
    function withdrawTokens(address _tokenAddress, uint256 _amount)
        external
        onlyOwner
    {
        uint256 contractBalance = IToken(_tokenAddress).balanceOf(address(this));
        require(contractBalance >= _amount, "Insufficient token balance");
        require(
            IToken(_tokenAddress).transfer(owner(), _amount),
            "Token transfer failed"
        );
    }

      // Function to set the token address
    function setToken(address _token) external onlyOwner {
        require(_token != address(0), "Invalid address");
        token = IToken(_token);
    }

    // Function to set the USDT address
    function setUSDT(address _usdt) external onlyOwner {
        require(_usdt != address(0), "Invalid address");
        USDT = IToken(_usdt);
    }

    // Function to set the price feed address
    function setPriceFeed(address _priceFeed) external onlyOwner {
        require(_priceFeed != address(0), "Invalid address");
        priceFeedeth = AggregatorV3Interface(_priceFeed);
    }
}
Configuraciones
{
  "compilationTarget": {
    "TokenPresale.sol": "TokenPresale"
  },
  "evmVersion": "shanghai",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"contract IToken","name":"_token","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":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"ETHRaised","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDT","outputs":[{"internalType":"contract IToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDTRaised","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"buyWithETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"usdtAmount","type":"uint256"}],"name":"buyWithUSDT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum TokenPresale.Rounds","name":"_round","type":"uint8"}],"name":"claimTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentRound","outputs":[{"internalType":"enum TokenPresale.Rounds","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"distributionWallets","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum TokenPresale.Rounds","name":"_round","type":"uint8"}],"name":"endRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"ethToToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"finalRoundBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLatestPriceETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalInUSDT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfWalletsToDistribute","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":"priceFeedeth","outputs":[{"internalType":"contract AggregatorV3Interface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"round1Bonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"round2Bonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"round3Bonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum TokenPresale.Rounds","name":"","type":"uint8"}],"name":"roundStatus","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"wallets","type":"address[]"}],"name":"setDistributionWallets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_priceFeed","type":"address"}],"name":"setPriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"setToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newPrice","type":"uint256"}],"name":"setTokenPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_usdt","type":"address"}],"name":"setUSDT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum TokenPresale.Rounds","name":"_round","type":"uint8"}],"name":"startRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenForSale","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalRaisedETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalRaisedUSDT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalTokensSold","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":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"tokensPurchased","type":"uint256"},{"internalType":"uint256","name":"round","type":"uint256"}],"name":"updateUserRoundData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"usdtToToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"enum TokenPresale.Rounds","name":"","type":"uint8"}],"name":"userRoundData","outputs":[{"internalType":"uint256","name":"tokensPurchased","type":"uint256"},{"internalType":"uint256","name":"bonusTokens","type":"uint256"},{"internalType":"bool","name":"claimed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawUSDT","outputs":[],"stateMutability":"nonpayable","type":"function"}]