Blockchain / Global / Address

Contract

0x3791e3ED0F311462912eA3d31A104dFd1d08C4De

Balance
0 SOMI
Transactions sent
3
Type
Contract · 21,134 bytes of code

CurveTokenFactory Verified

Compiler
solc 0.8.20+commit.a1b79de6
Language
Solidity
Optimization
No
EVM version
paris
License
—
Verified
2026-10-12

contracts/CurveTokenFactory.sol

and 21 more files

Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";

import "./CurveToken.sol";
import "./CurveLaunch.sol";

contract CurveTokenFactory is ReentrancyGuard {
    using Clones for address;

    /// @notice Factory owner
    address public immutable owner;

    /// @notice Launch implementation to be cloned
    address public immutable launchImplementation;

    /// @notice Developer fee recipient
    address public devWallet;

    /// @notice DEX router address
    address public dexRouter;

    /// @notice Wrapped BNB token address
    address public wbnb;

    /// @notice Chainlink BNB/USD price feed
    AggregatorV3Interface public priceFeed;

    /// @notice Decimals of the Chainlink feed (max 18)
    uint8 public immutable feedDecimals;

    /// @notice Maximum BNB-cap per launch (converted from USD)
    uint256 public curveCap;

    /// @notice Fee percentages for buys and sells
    uint256 public buyFeePercent;
    uint256 public sellFeePercent;

    /// @notice Maximum BNB one wallet can spend
    uint256 public maxBuy;

    /// @notice Percentages for migration fee & creator reward
    uint256 public migrationFeePercent;
    uint256 public creatorRewardPercent;

    struct LaunchInfo {
        address token;
        address launchContract;
        string  name;
        string  symbol;
        string  imageURI;
        uint256 createdAt;
    }

    mapping(uint256 => LaunchInfo) public launches;
    uint256 public launchesCount;
    mapping(address => address) public tokenToLaunch;

    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner");
        _;
    }

    /// @notice Deploys factory with fee, cap, router, and price‐feed settings
    constructor(
        address _launchImpl,
        address _priceFeed,
        address _dexRouter,
        address _wbnb,
        uint256 _curveCapUsd,
        uint256 _maxBuy,
        uint256 _buyFeePercent,
        uint256 _sellFeePercent,
        uint256 _migrationFeePercent,
        uint256 _creatorRewardPercent
    ) {
        require(_launchImpl != address(0), "Bad launch impl");
        launchImplementation = _launchImpl;

        require(_priceFeed      != address(0), "Invalid feed");
        require(_dexRouter      != address(0), "Invalid router");
        require(_wbnb           != address(0), "Invalid wbnb");
        require(_migrationFeePercent <= 100,   "Migration fee too high");
        require(_creatorRewardPercent <= 100,  "Creator reward too high");

        owner     = msg.sender;
        devWallet = msg.sender;

        dexRouter = _dexRouter;
        wbnb      = _wbnb;

        priceFeed    = AggregatorV3Interface(_priceFeed);
        feedDecimals = priceFeed.decimals();
        require(feedDecimals <= 18, "Unsupported decimals");

        curveCap = getBNBForUSD(_curveCapUsd);

        require(_maxBuy         > 0,   "Invalid maxBuy");
        require(_buyFeePercent  <= 100, "Buy fee too high");
        require(_sellFeePercent <= 100, "Sell fee too high");

        maxBuy               = _maxBuy;
        buyFeePercent        = _buyFeePercent;
        sellFeePercent       = _sellFeePercent;
        migrationFeePercent  = _migrationFeePercent;
        creatorRewardPercent = _creatorRewardPercent;
    }
    
    /// @notice Deploys a new token and launch clone with specified parameters
    function createLaunch(
        string memory name,
        string memory symbol,
        string memory imageURI,
        bool isRefundable,
        bool claimLP,
        bool creatorPreBuys,
        uint256 _durationSeconds
    ) external payable nonReentrant returns (address token, address payable launchProxy) {
        require(_durationSeconds > 0,          "Duration must be > 0");
        require(_durationSeconds <= 7 days,    "Duration must be <= 7 days");
        require(bytes(name).length > 0 && bytes(symbol).length > 0, "Invalid input");

        uint256 creatorBuyAmount = creatorPreBuys ? msg.value : 0;
        require(msg.value == creatorBuyAmount, "msg.value mismatch");

        CurveToken curveToken = new CurveToken(name, symbol, address(this));
        token = address(curveToken);

        CurveLaunch.LaunchParams memory params = CurveLaunch.LaunchParams({
            creator:              msg.sender,
            token:                token,
            isRefundable:         isRefundable,
            claimLP:              claimLP,
            creatorBuys:          creatorPreBuys,
            creatorBuyAmount:     creatorPreBuys ? msg.value : 0,
            devWallet:            devWallet,
            priceFeed:            address(priceFeed),
            curveCap:             curveCap,
            buyFeePercent:        buyFeePercent,
            sellFeePercent:       sellFeePercent,
            maxBuy:               maxBuy,
            finalizeTime:         _durationSeconds,
            migrationFeePercent:  migrationFeePercent,
            creatorRewardPercent: creatorRewardPercent,
            dexRouter:            dexRouter,
            wbnb:                 wbnb,
            imageURI:             imageURI
        });

        launchProxy = payable(launchImplementation.clone());
        CurveLaunch(launchProxy).initialize{ value: msg.value }(params);

        curveToken.transfer(launchProxy, curveToken.totalSupply());

        launches[launchesCount] = LaunchInfo({
            token:          token,
            launchContract: launchProxy,
            name:           name,
            symbol:         symbol,
            imageURI:       imageURI,
            createdAt:      block.timestamp
        });

        launchesCount++;
        tokenToLaunch[token] = launchProxy;
    }

    /// @notice Update migration and creator reward fee percentages (each ≤100)
    function setLpFeePercents(uint256 _migrationFee, uint256 _creatorReward)
        external
        onlyOwner
    {
        require(_migrationFee   <= 100, "Migration fee too high");
        require(_creatorReward  <= 100, "Creator reward too high");
        migrationFeePercent  = _migrationFee;
        creatorRewardPercent = _creatorReward;
    }

    /// @notice Update developer wallet address
    function setDevWallet(address newWallet) external onlyOwner {
        require(newWallet != address(0), "Bad address");
        devWallet = newWallet;
    }

    /// @notice Update curve cap (USD→BNB)
    function setCurveCap(uint256 usdAmount) external onlyOwner {
        require(usdAmount > 0, "Invalid cap");
        curveCap = getBNBForUSD(usdAmount);
    }

    /// @notice Update buy and sell fee percentages (each ≤100)
    function setTradeFeePercents(uint256 _buyFeePercent, uint256 _sellFeePercent)
        external
        onlyOwner
    {
        require(_buyFeePercent  <= 100, "Buy fee too high");
        require(_sellFeePercent <= 100, "Sell fee too high");
        buyFeePercent  = _buyFeePercent;
        sellFeePercent = _sellFeePercent;
    }

    /// @notice Update maximum BNB per wallet
    function setMaxBuy(uint256 etherAmount) external onlyOwner {
        require(etherAmount > 0, "Invalid max");
        maxBuy = etherAmount;
    }

    /// @notice Update Chainlink price feed address
    function setPriceFeed(address _newFeed) external onlyOwner {
        require(_newFeed != address(0), "Bad feed");
        priceFeed = AggregatorV3Interface(_newFeed);
    }

    /// @notice Update DEX router address
    function setDexRouter(address _router) external onlyOwner {
        require(_router != address(0), "Invalid router");
        dexRouter = _router;
    }

    /// @notice Update wrapped BNB token address
    function setWbnb(address _wbnb) external onlyOwner {
        require(_wbnb != address(0), "Invalid wbnb");
        wbnb = _wbnb;
    }

    /// @notice Convert USD amount to BNB using Chainlink
    function getBNBForUSD(uint256 usdAmount) public view returns (uint256) {
        (uint80 roundId, int price, , uint256 updatedAt, uint80 answeredInRound) =
            priceFeed.latestRoundData();
        require(price > 0,                    "Invalid price");
        require(answeredInRound >= roundId,   "Stale price");

        return (usdAmount * 10**(18 + feedDecimals)) / uint256(price);
    }
}

Other files

  • @uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol
    pragma solidity >=0.5.0;
    
    interface IUniswapV2Factory {
        event PairCreated(address indexed token0, address indexed token1, address pair, uint);
    
        function feeTo() external view returns (address);
        function feeToSetter() external view returns (address);
    
        function getPair(address tokenA, address tokenB) external view returns (address pair);
        function allPairs(uint) external view returns (address pair);
        function allPairsLength() external view returns (uint);
    
        function createPair(address tokenA, address tokenB) external returns (address pair);
    
        function setFeeTo(address) external;
        function setFeeToSetter(address) external;
    }
    
  • @uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router01.sol
    pragma solidity >=0.6.2;
    
    interface IUniswapV2Router01 {
        function factory() external pure returns (address);
        function WETH() external pure returns (address);
    
        function addLiquidity(
            address tokenA,
            address tokenB,
            uint amountADesired,
            uint amountBDesired,
            uint amountAMin,
            uint amountBMin,
            address to,
            uint deadline
        ) external returns (uint amountA, uint amountB, uint liquidity);
        function addLiquidityETH(
            address token,
            uint amountTokenDesired,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline
        ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
        function removeLiquidity(
            address tokenA,
            address tokenB,
            uint liquidity,
            uint amountAMin,
            uint amountBMin,
            address to,
            uint deadline
        ) external returns (uint amountA, uint amountB);
        function removeLiquidityETH(
            address token,
            uint liquidity,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline
        ) external returns (uint amountToken, uint amountETH);
        function removeLiquidityWithPermit(
            address tokenA,
            address tokenB,
            uint liquidity,
            uint amountAMin,
            uint amountBMin,
            address to,
            uint deadline,
            bool approveMax, uint8 v, bytes32 r, bytes32 s
        ) external returns (uint amountA, uint amountB);
        function removeLiquidityETHWithPermit(
            address token,
            uint liquidity,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline,
            bool approveMax, uint8 v, bytes32 r, bytes32 s
        ) external returns (uint amountToken, uint amountETH);
        function swapExactTokensForTokens(
            uint amountIn,
            uint amountOutMin,
            address[] calldata path,
            address to,
            uint deadline
        ) external returns (uint[] memory amounts);
        function swapTokensForExactTokens(
            uint amountOut,
            uint amountInMax,
            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 swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
            external
            returns (uint[] memory amounts);
        function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
            external
            returns (uint[] memory amounts);
        function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
            external
            payable
            returns (uint[] memory amounts);
    
        function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
        function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
        function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
        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);
    }
    
  • @openzeppelin/contracts/token/ERC20/ERC20.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/ERC20.sol)
    
    pragma solidity ^0.8.20;
    
    import {IERC20} from "./IERC20.sol";
    import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
    import {Context} from "../../utils/Context.sol";
    import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
    
    /**
     * @dev Implementation of the {IERC20} interface.
     *
     * This implementation is agnostic to the way tokens are created. This means
     * that a supply mechanism has to be added in a derived contract using {_mint}.
     *
     * TIP: For a detailed writeup see our guide
     * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
     * to implement supply mechanisms].
     *
     * The default value of {decimals} is 18. To change this, you should override
     * this function so it returns a different value.
     *
     * We have followed general OpenZeppelin Contracts guidelines: functions revert
     * instead returning `false` on failure. This behavior is nonetheless
     * conventional and does not conflict with the expectations of ERC-20
     * applications.
     */
    abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
        mapping(address account => uint256) private _balances;
    
        mapping(address account => mapping(address spender => uint256)) private _allowances;
    
        uint256 private _totalSupply;
    
        string private _name;
        string private _symbol;
    
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * Both values are immutable: they can only be set once during construction.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
    
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual returns (string memory) {
            return _name;
        }
    
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual returns (string memory) {
            return _symbol;
        }
    
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5.05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the default value returned by this function, unless
         * it's overridden.
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view virtual returns (uint8) {
            return 18;
        }
    
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual returns (uint256) {
            return _totalSupply;
        }
    
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual returns (uint256) {
            return _balances[account];
        }
    
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - the caller must have a balance of at least `value`.
         */
        function transfer(address to, uint256 value) public virtual returns (bool) {
            address owner = _msgSender();
            _transfer(owner, to, value);
            return true;
        }
    
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(address owner, address spender) public view virtual returns (uint256) {
            return _allowances[owner][spender];
        }
    
        /**
         * @dev See {IERC20-approve}.
         *
         * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
         * `transferFrom`. This is semantically equivalent to an infinite approval.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function approve(address spender, uint256 value) public virtual returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, value);
            return true;
        }
    
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Skips emitting an {Approval} event indicating an allowance update. This is not
         * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
         *
         * NOTE: Does not update the allowance if the current allowance
         * is the maximum `uint256`.
         *
         * Requirements:
         *
         * - `from` and `to` cannot be the zero address.
         * - `from` must have a balance of at least `value`.
         * - the caller must have allowance for ``from``'s tokens of at least
         * `value`.
         */
        function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
            address spender = _msgSender();
            _spendAllowance(from, spender, value);
            _transfer(from, to, value);
            return true;
        }
    
        /**
         * @dev Moves a `value` amount of tokens from `from` to `to`.
         *
         * This internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * NOTE: This function is not virtual, {_update} should be overridden instead.
         */
        function _transfer(address from, address to, uint256 value) internal {
            if (from == address(0)) {
                revert ERC20InvalidSender(address(0));
            }
            if (to == address(0)) {
                revert ERC20InvalidReceiver(address(0));
            }
            _update(from, to, value);
        }
    
        /**
         * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
         * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
         * this function.
         *
         * Emits a {Transfer} event.
         */
        function _update(address from, address to, uint256 value) internal virtual {
            if (from == address(0)) {
                // Overflow check required: The rest of the code assumes that totalSupply never overflows
                _totalSupply += value;
            } else {
                uint256 fromBalance = _balances[from];
                if (fromBalance < value) {
                    revert ERC20InsufficientBalance(from, fromBalance, value);
                }
                unchecked {
                    // Overflow not possible: value <= fromBalance <= totalSupply.
                    _balances[from] = fromBalance - value;
                }
            }
    
            if (to == address(0)) {
                unchecked {
                    // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                    _totalSupply -= value;
                }
            } else {
                unchecked {
                    // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                    _balances[to] += value;
                }
            }
    
            emit Transfer(from, to, value);
        }
    
        /**
         * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
         * Relies on the `_update` mechanism
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * NOTE: This function is not virtual, {_update} should be overridden instead.
         */
        function _mint(address account, uint256 value) internal {
            if (account == address(0)) {
                revert ERC20InvalidReceiver(address(0));
            }
            _update(address(0), account, value);
        }
    
        /**
         * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
         * Relies on the `_update` mechanism.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * NOTE: This function is not virtual, {_update} should be overridden instead
         */
        function _burn(address account, uint256 value) internal {
            if (account == address(0)) {
                revert ERC20InvalidSender(address(0));
            }
            _update(account, address(0), value);
        }
    
        /**
         * @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         *
         * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
         */
        function _approve(address owner, address spender, uint256 value) internal {
            _approve(owner, spender, value, true);
        }
    
        /**
         * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
         *
         * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
         * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
         * `Approval` event during `transferFrom` operations.
         *
         * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
         * true using the following override:
         *
         * ```solidity
         * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
         *     super._approve(owner, spender, value, true);
         * }
         * ```
         *
         * Requirements are the same as {_approve}.
         */
        function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
            if (owner == address(0)) {
                revert ERC20InvalidApprover(address(0));
            }
            if (spender == address(0)) {
                revert ERC20InvalidSpender(address(0));
            }
            _allowances[owner][spender] = value;
            if (emitEvent) {
                emit Approval(owner, spender, value);
            }
        }
    
        /**
         * @dev Updates `owner`'s allowance for `spender` based on spent `value`.
         *
         * Does not update the allowance value in case of infinite allowance.
         * Revert if not enough allowance is available.
         *
         * Does not emit an {Approval} event.
         */
        function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
            uint256 currentAllowance = allowance(owner, spender);
            if (currentAllowance < type(uint256).max) {
                if (currentAllowance < value) {
                    revert ERC20InsufficientAllowance(spender, currentAllowance, value);
                }
                unchecked {
                    _approve(owner, spender, currentAllowance - value, false);
                }
            }
        }
    }
    
  • @chainlink/contracts/src/v0.8/shared/interfaces/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);
    }
    
  • @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
    
    pragma solidity ^0.8.2;
    
    import "../../utils/AddressUpgradeable.sol";
    
    /**
     * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
     * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
     * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
     * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
     *
     * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
     * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
     * case an upgrade adds a module that needs to be initialized.
     *
     * For example:
     *
     * [.hljs-theme-light.nopadding]
     * ```solidity
     * contract MyToken is ERC20Upgradeable {
     *     function initialize() initializer public {
     *         __ERC20_init("MyToken", "MTK");
     *     }
     * }
     *
     * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
     *     function initializeV2() reinitializer(2) public {
     *         __ERC20Permit_init("MyToken");
     *     }
     * }
     * ```
     *
     * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
     * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
     *
     * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
     * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
     *
     * [CAUTION]
     * ====
     * Avoid leaving a contract uninitialized.
     *
     * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
     * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
     * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
     *
     * [.hljs-theme-light.nopadding]
     * ```
     * /// @custom:oz-upgrades-unsafe-allow constructor
     * constructor() {
     *     _disableInitializers();
     * }
     * ```
     * ====
     */
    abstract contract Initializable {
        /**
         * @dev Indicates that the contract has been initialized.
         * @custom:oz-retyped-from bool
         */
        uint8 private _initialized;
    
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool private _initializing;
    
        /**
         * @dev Triggered when the contract has been initialized or reinitialized.
         */
        event Initialized(uint8 version);
    
        /**
         * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
         * `onlyInitializing` functions can be used to initialize parent contracts.
         *
         * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
         * constructor.
         *
         * Emits an {Initialized} event.
         */
        modifier initializer() {
            bool isTopLevelCall = !_initializing;
            require(
                (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                "Initializable: contract is already initialized"
            );
            _initialized = 1;
            if (isTopLevelCall) {
                _initializing = true;
            }
            _;
            if (isTopLevelCall) {
                _initializing = false;
                emit Initialized(1);
            }
        }
    
        /**
         * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
         * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
         * used to initialize parent contracts.
         *
         * A reinitializer may be used after the original initialization step. This is essential to configure modules that
         * are added through upgrades and that require initialization.
         *
         * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
         * cannot be nested. If one is invoked in the context of another, execution will revert.
         *
         * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
         * a contract, executing them in the right order is up to the developer or operator.
         *
         * WARNING: setting the version to 255 will prevent any future reinitialization.
         *
         * Emits an {Initialized} event.
         */
        modifier reinitializer(uint8 version) {
            require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
            _initialized = version;
            _initializing = true;
            _;
            _initializing = false;
            emit Initialized(version);
        }
    
        /**
         * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
         * {initializer} and {reinitializer} modifiers, directly or indirectly.
         */
        modifier onlyInitializing() {
            require(_initializing, "Initializable: contract is not initializing");
            _;
        }
    
        /**
         * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
         * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
         * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
         * through proxies.
         *
         * Emits an {Initialized} event the first time it is successfully executed.
         */
        function _disableInitializers() internal virtual {
            require(!_initializing, "Initializable: contract is initializing");
            if (_initialized != type(uint8).max) {
                _initialized = type(uint8).max;
                emit Initialized(type(uint8).max);
            }
        }
    
        /**
         * @dev Returns the highest version that has been initialized. See {reinitializer}.
         */
        function _getInitializedVersion() internal view returns (uint8) {
            return _initialized;
        }
    
        /**
         * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
         */
        function _isInitializing() internal view returns (bool) {
            return _initializing;
        }
    }
    
  • @openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
    
    pragma solidity ^0.8.0;
    import {Initializable} from "../proxy/utils/Initializable.sol";
    
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     */
    abstract contract ReentrancyGuardUpgradeable is Initializable {
        // 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;
    
        function __ReentrancyGuard_init() internal onlyInitializing {
            __ReentrancyGuard_init_unchained();
        }
    
        function __ReentrancyGuard_init_unchained() internal onlyInitializing {
            _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
            require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
    
            // 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;
        }
    
        /**
         * @dev This empty reserved space is put in place to allow future versions to add new
         * variables without shifting down storage in the inheritance chain.
         * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
         */
        uint256[49] private __gap;
    }
    
  • @openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
    
    pragma solidity ^0.8.1;
    
    /**
     * @dev Collection of functions related to the address type
     */
    library AddressUpgradeable {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         *
         * Furthermore, `isContract` will also return true if the target contract within
         * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
         * which only has an effect at the end of a transaction.
         * ====
         *
         * [IMPORTANT]
         * ====
         * You shouldn't rely on `isContract` to protect against flash loan attacks!
         *
         * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
         * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
         * constructor.
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize/address.code.length, which returns 0
            // for contracts in construction, since the code is only stored at the end
            // of the constructor execution.
    
            return account.code.length > 0;
        }
    
        /**
         * @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.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
    
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
    
        /**
         * @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, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * 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.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, "Address: low-level call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
    
        /**
         * @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`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
    
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
         * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
         *
         * _Available since v4.8._
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            if (success) {
                if (returndata.length == 0) {
                    // only check isContract if the call was successful and the return data is empty
                    // otherwise we already know that it was a contract
                    require(isContract(target), "Address: call to non-contract");
                }
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
    
        /**
         * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason or using the provided one.
         *
         * _Available since v4.3._
         */
        function verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
    
        function _revert(bytes memory returndata, string memory errorMessage) 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
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
    
  • @openzeppelin/contracts/interfaces/draft-IERC6093.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
    pragma solidity ^0.8.20;
    
    /**
     * @dev Standard ERC-20 Errors
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
     */
    interface IERC20Errors {
        /**
         * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         * @param balance Current balance for the interacting account.
         * @param needed Minimum amount required to perform a transfer.
         */
        error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
    
        /**
         * @dev Indicates a failure with the token `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         */
        error ERC20InvalidSender(address sender);
    
        /**
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
         * @param receiver Address to which tokens are being transferred.
         */
        error ERC20InvalidReceiver(address receiver);
    
        /**
         * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
         * @param spender Address that may be allowed to operate on tokens without being their owner.
         * @param allowance Amount of tokens a `spender` is allowed to operate with.
         * @param needed Minimum amount required to perform a transfer.
         */
        error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
    
        /**
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
         * @param approver Address initiating an approval operation.
         */
        error ERC20InvalidApprover(address approver);
    
        /**
         * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
         * @param spender Address that may be allowed to operate on tokens without being their owner.
         */
        error ERC20InvalidSpender(address spender);
    }
    
    /**
     * @dev Standard ERC-721 Errors
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
     */
    interface IERC721Errors {
        /**
         * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
         * Used in balance queries.
         * @param owner Address of the current owner of a token.
         */
        error ERC721InvalidOwner(address owner);
    
        /**
         * @dev Indicates a `tokenId` whose `owner` is the zero address.
         * @param tokenId Identifier number of a token.
         */
        error ERC721NonexistentToken(uint256 tokenId);
    
        /**
         * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         * @param tokenId Identifier number of a token.
         * @param owner Address of the current owner of a token.
         */
        error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
    
        /**
         * @dev Indicates a failure with the token `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         */
        error ERC721InvalidSender(address sender);
    
        /**
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
         * @param receiver Address to which tokens are being transferred.
         */
        error ERC721InvalidReceiver(address receiver);
    
        /**
         * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         * @param tokenId Identifier number of a token.
         */
        error ERC721InsufficientApproval(address operator, uint256 tokenId);
    
        /**
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
         * @param approver Address initiating an approval operation.
         */
        error ERC721InvalidApprover(address approver);
    
        /**
         * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         */
        error ERC721InvalidOperator(address operator);
    }
    
    /**
     * @dev Standard ERC-1155 Errors
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
     */
    interface IERC1155Errors {
        /**
         * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         * @param balance Current balance for the interacting account.
         * @param needed Minimum amount required to perform a transfer.
         * @param tokenId Identifier number of a token.
         */
        error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
    
        /**
         * @dev Indicates a failure with the token `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         */
        error ERC1155InvalidSender(address sender);
    
        /**
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
         * @param receiver Address to which tokens are being transferred.
         */
        error ERC1155InvalidReceiver(address receiver);
    
        /**
         * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         * @param owner Address of the current owner of a token.
         */
        error ERC1155MissingApprovalForAll(address operator, address owner);
    
        /**
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
         * @param approver Address initiating an approval operation.
         */
        error ERC1155InvalidApprover(address approver);
    
        /**
         * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         */
        error ERC1155InvalidOperator(address operator);
    
        /**
         * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
         * Used in batch transfers.
         * @param idsLength Length of the array of token identifiers
         * @param valuesLength Length of the array of token amounts
         */
        error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
    }
    
  • @openzeppelin/contracts/proxy/Clones.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.3.0) (proxy/Clones.sol)
    
    pragma solidity ^0.8.20;
    
    import {Create2} from "../utils/Create2.sol";
    import {Errors} from "../utils/Errors.sol";
    
    /**
     * @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for
     * deploying minimal proxy contracts, also known as "clones".
     *
     * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
     * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
     *
     * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
     * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
     * deterministic method.
     */
    library Clones {
        error CloneArgumentsTooLong();
    
        /**
         * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation`.
         *
         * This function uses the create opcode, which should never revert.
         */
        function clone(address implementation) internal returns (address instance) {
            return clone(implementation, 0);
        }
    
        /**
         * @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency
         * to the new contract.
         *
         * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
         * to always have enough balance for new deployments. Consider exposing this function under a payable method.
         */
        function clone(address implementation, uint256 value) internal returns (address instance) {
            if (address(this).balance < value) {
                revert Errors.InsufficientBalance(address(this).balance, value);
            }
            assembly ("memory-safe") {
                // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
                // of the `implementation` address with the bytecode before the address.
                mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
                // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
                mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
                instance := create(value, 0x09, 0x37)
            }
            if (instance == address(0)) {
                revert Errors.FailedDeployment();
            }
        }
    
        /**
         * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation`.
         *
         * This function uses the create2 opcode and a `salt` to deterministically deploy
         * the clone. Using the same `implementation` and `salt` multiple times will revert, since
         * the clones cannot be deployed twice at the same address.
         */
        function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
            return cloneDeterministic(implementation, salt, 0);
        }
    
        /**
         * @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with
         * a `value` parameter to send native currency to the new contract.
         *
         * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
         * to always have enough balance for new deployments. Consider exposing this function under a payable method.
         */
        function cloneDeterministic(
            address implementation,
            bytes32 salt,
            uint256 value
        ) internal returns (address instance) {
            if (address(this).balance < value) {
                revert Errors.InsufficientBalance(address(this).balance, value);
            }
            assembly ("memory-safe") {
                // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
                // of the `implementation` address with the bytecode before the address.
                mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
                // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
                mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
                instance := create2(value, 0x09, 0x37, salt)
            }
            if (instance == address(0)) {
                revert Errors.FailedDeployment();
            }
        }
    
        /**
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
         */
        function predictDeterministicAddress(
            address implementation,
            bytes32 salt,
            address deployer
        ) internal pure returns (address predicted) {
            assembly ("memory-safe") {
                let ptr := mload(0x40)
                mstore(add(ptr, 0x38), deployer)
                mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
                mstore(add(ptr, 0x14), implementation)
                mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
                mstore(add(ptr, 0x58), salt)
                mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
                predicted := and(keccak256(add(ptr, 0x43), 0x55), 0xffffffffffffffffffffffffffffffffffffffff)
            }
        }
    
        /**
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
         */
        function predictDeterministicAddress(
            address implementation,
            bytes32 salt
        ) internal view returns (address predicted) {
            return predictDeterministicAddress(implementation, salt, address(this));
        }
    
        /**
         * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom
         * immutable arguments. These are provided through `args` and cannot be changed after deployment. To
         * access the arguments within the implementation, use {fetchCloneArgs}.
         *
         * This function uses the create opcode, which should never revert.
         */
        function cloneWithImmutableArgs(address implementation, bytes memory args) internal returns (address instance) {
            return cloneWithImmutableArgs(implementation, args, 0);
        }
    
        /**
         * @dev Same as {xref-Clones-cloneWithImmutableArgs-address-bytes-}[cloneWithImmutableArgs], but with a `value`
         * parameter to send native currency to the new contract.
         *
         * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
         * to always have enough balance for new deployments. Consider exposing this function under a payable method.
         */
        function cloneWithImmutableArgs(
            address implementation,
            bytes memory args,
            uint256 value
        ) internal returns (address instance) {
            if (address(this).balance < value) {
                revert Errors.InsufficientBalance(address(this).balance, value);
            }
            bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
            assembly ("memory-safe") {
                instance := create(value, add(bytecode, 0x20), mload(bytecode))
            }
            if (instance == address(0)) {
                revert Errors.FailedDeployment();
            }
        }
    
        /**
         * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom
         * immutable arguments. These are provided through `args` and cannot be changed after deployment. To
         * access the arguments within the implementation, use {fetchCloneArgs}.
         *
         * This function uses the create2 opcode and a `salt` to deterministically deploy the clone. Using the same
         * `implementation`, `args` and `salt` multiple times will revert, since the clones cannot be deployed twice
         * at the same address.
         */
        function cloneDeterministicWithImmutableArgs(
            address implementation,
            bytes memory args,
            bytes32 salt
        ) internal returns (address instance) {
            return cloneDeterministicWithImmutableArgs(implementation, args, salt, 0);
        }
    
        /**
         * @dev Same as {xref-Clones-cloneDeterministicWithImmutableArgs-address-bytes-bytes32-}[cloneDeterministicWithImmutableArgs],
         * but with a `value` parameter to send native currency to the new contract.
         *
         * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
         * to always have enough balance for new deployments. Consider exposing this function under a payable method.
         */
        function cloneDeterministicWithImmutableArgs(
            address implementation,
            bytes memory args,
            bytes32 salt,
            uint256 value
        ) internal returns (address instance) {
            bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
            return Create2.deploy(value, salt, bytecode);
        }
    
        /**
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
         */
        function predictDeterministicAddressWithImmutableArgs(
            address implementation,
            bytes memory args,
            bytes32 salt,
            address deployer
        ) internal pure returns (address predicted) {
            bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
            return Create2.computeAddress(salt, keccak256(bytecode), deployer);
        }
    
        /**
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
         */
        function predictDeterministicAddressWithImmutableArgs(
            address implementation,
            bytes memory args,
            bytes32 salt
        ) internal view returns (address predicted) {
            return predictDeterministicAddressWithImmutableArgs(implementation, args, salt, address(this));
        }
    
        /**
         * @dev Get the immutable args attached to a clone.
         *
         * - If `instance` is a clone that was deployed using `clone` or `cloneDeterministic`, this
         *   function will return an empty array.
         * - If `instance` is a clone that was deployed using `cloneWithImmutableArgs` or
         *   `cloneDeterministicWithImmutableArgs`, this function will return the args array used at
         *   creation.
         * - If `instance` is NOT a clone deployed using this library, the behavior is undefined. This
         *   function should only be used to check addresses that are known to be clones.
         */
        function fetchCloneArgs(address instance) internal view returns (bytes memory) {
            bytes memory result = new bytes(instance.code.length - 45); // revert if length is too short
            assembly ("memory-safe") {
                extcodecopy(instance, add(result, 32), 45, mload(result))
            }
            return result;
        }
    
        /**
         * @dev Helper that prepares the initcode of the proxy with immutable args.
         *
         * An assembly variant of this function requires copying the `args` array, which can be efficiently done using
         * `mcopy`. Unfortunately, that opcode is not available before cancun. A pure solidity implementation using
         * abi.encodePacked is more expensive but also more portable and easier to review.
         *
         * NOTE: https://eips.ethereum.org/EIPS/eip-170[EIP-170] limits the length of the contract code to 24576 bytes.
         * With the proxy code taking 45 bytes, that limits the length of the immutable args to 24531 bytes.
         */
        function _cloneCodeWithImmutableArgs(
            address implementation,
            bytes memory args
        ) private pure returns (bytes memory) {
            if (args.length > 24531) revert CloneArgumentsTooLong();
            return
                abi.encodePacked(
                    hex"61",
                    uint16(args.length + 45),
                    hex"3d81600a3d39f3363d3d373d3d3d363d73",
                    implementation,
                    hex"5af43d82803e903d91602b57fd5bf3",
                    args
                );
        }
    }
    
  • @openzeppelin/contracts/token/ERC20/IERC20.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @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);
    }
    
  • @openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC20Burnable.sol)
    
    pragma solidity ^0.8.20;
    
    import {ERC20} from "../ERC20.sol";
    import {Context} from "../../../utils/Context.sol";
    
    /**
     * @dev Extension of {ERC20} that allows token holders to destroy both their own
     * tokens and those that they have an allowance for, in a way that can be
     * recognized off-chain (via event analysis).
     */
    abstract contract ERC20Burnable is Context, ERC20 {
        /**
         * @dev Destroys a `value` amount of tokens from the caller.
         *
         * See {ERC20-_burn}.
         */
        function burn(uint256 value) public virtual {
            _burn(_msgSender(), value);
        }
    
        /**
         * @dev Destroys a `value` amount of tokens from `account`, deducting from
         * the caller's allowance.
         *
         * See {ERC20-_burn} and {ERC20-allowance}.
         *
         * Requirements:
         *
         * - the caller must have allowance for ``accounts``'s tokens of at least
         * `value`.
         */
        function burnFrom(address account, uint256 value) public virtual {
            _spendAllowance(account, _msgSender(), value);
            _burn(account, value);
        }
    }
    
  • @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
    
    pragma solidity ^0.8.20;
    
    import {IERC20} from "../IERC20.sol";
    
    /**
     * @dev Interface for the optional metadata functions from the ERC-20 standard.
     */
    interface IERC20Metadata is IERC20 {
        /**
         * @dev Returns the name of the token.
         */
        function name() external view returns (string memory);
    
        /**
         * @dev Returns the symbol of the token.
         */
        function symbol() external view returns (string memory);
    
        /**
         * @dev Returns the decimals places of the token.
         */
        function decimals() external view returns (uint8);
    }
    
  • @openzeppelin/contracts/utils/Address.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.2.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, bytes memory returndata) = recipient.call{value: amount}("");
            if (!success) {
                _revert(returndata);
            }
        }
    
        /**
         * @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();
            }
        }
    }
    
  • @openzeppelin/contracts/utils/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;
        }
    }
    
  • @openzeppelin/contracts/utils/Create2.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/Create2.sol)
    
    pragma solidity ^0.8.20;
    
    import {Errors} from "./Errors.sol";
    
    /**
     * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
     * `CREATE2` can be used to compute in advance the address where a smart
     * contract will be deployed, which allows for interesting new mechanisms known
     * as 'counterfactual interactions'.
     *
     * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
     * information.
     */
    library Create2 {
        /**
         * @dev There's no code to deploy.
         */
        error Create2EmptyBytecode();
    
        /**
         * @dev Deploys a contract using `CREATE2`. The address where the contract
         * will be deployed can be known in advance via {computeAddress}.
         *
         * The bytecode for a contract can be obtained from Solidity with
         * `type(contractName).creationCode`.
         *
         * Requirements:
         *
         * - `bytecode` must not be empty.
         * - `salt` must have not been used for `bytecode` already.
         * - the factory must have a balance of at least `amount`.
         * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
         */
        function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
            if (address(this).balance < amount) {
                revert Errors.InsufficientBalance(address(this).balance, amount);
            }
            if (bytecode.length == 0) {
                revert Create2EmptyBytecode();
            }
            assembly ("memory-safe") {
                addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
                // if no address was created, and returndata is not empty, bubble revert
                if and(iszero(addr), not(iszero(returndatasize()))) {
                    let p := mload(0x40)
                    returndatacopy(p, 0, returndatasize())
                    revert(p, returndatasize())
                }
            }
            if (addr == address(0)) {
                revert Errors.FailedDeployment();
            }
        }
    
        /**
         * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
         * `bytecodeHash` or `salt` will result in a new destination address.
         */
        function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
            return computeAddress(salt, bytecodeHash, address(this));
        }
    
        /**
         * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
         * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
         */
        function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
            assembly ("memory-safe") {
                let ptr := mload(0x40) // Get free memory pointer
    
                // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |
                // |-------------------|---------------------------------------------------------------------------|
                // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |
                // | salt              |                                      BBBBBBBBBBBBB...BB                   |
                // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |
                // | 0xFF              |            FF                                                             |
                // |-------------------|---------------------------------------------------------------------------|
                // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
                // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |
    
                mstore(add(ptr, 0x40), bytecodeHash)
                mstore(add(ptr, 0x20), salt)
                mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
                let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
                mstore8(start, 0xff)
                addr := and(keccak256(start, 85), 0xffffffffffffffffffffffffffffffffffffffff)
            }
        }
    }
    
  • @openzeppelin/contracts/utils/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);
    }
    
  • @openzeppelin/contracts/utils/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;
        }
    }
    
  • @uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol
    pragma solidity >=0.6.2;
    
    import './IUniswapV2Router01.sol';
    
    interface IUniswapV2Router02 is IUniswapV2Router01 {
        function removeLiquidityETHSupportingFeeOnTransferTokens(
            address token,
            uint liquidity,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline
        ) external returns (uint amountETH);
        function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
            address token,
            uint liquidity,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline,
            bool approveMax, uint8 v, bytes32 r, bytes32 s
        ) external returns (uint amountETH);
    
        function swapExactTokensForTokensSupportingFeeOnTransferTokens(
            uint amountIn,
            uint amountOutMin,
            address[] calldata path,
            address to,
            uint deadline
        ) external;
        function swapExactETHForTokensSupportingFeeOnTransferTokens(
            uint amountOutMin,
            address[] calldata path,
            address to,
            uint deadline
        ) external payable;
        function swapExactTokensForETHSupportingFeeOnTransferTokens(
            uint amountIn,
            uint amountOutMin,
            address[] calldata path,
            address to,
            uint deadline
        ) external;
    }
    
  • contracts/BondingCurve.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    
    /// @notice Provides pricing math for a linear bonding curve: price(t) = basePrice + priceIncrement·t
    library BondingCurve {
        /// @notice Calculates ∫ₓ=sold→sold+count [basePrice + priceIncrement·(x/1e18)] dx
        /// @param sold      Number of whole tokens already sold
        /// @param count     Additional token-wei to purchase (in 1e18 units)
        /// @param basePrice Initial price per token (wei)
        /// @param priceIncrement Incremental price per additional whole token (wei)
        /// @return totalCost Total cost (wei) for `count` token-wei along the curve
        function areaUnderCurve(
            uint256 sold,
            uint256 count,
            uint256 basePrice,
            uint256 priceIncrement
        ) internal pure returns (uint256 totalCost) {
            // Linear term: basePrice * count / 1e18
            uint256 linearCost = basePrice * count / 1e18;
            // Quadratic term: priceIncrement * ( (sold+count)² - sold² ) / (2 * 1e36)
            uint256 s1         = sold + count;
            uint256 deltaSq    = s1 * s1 - sold * sold;
            uint256 quadCost   = priceIncrement * deltaSq / (2 * 1e36);
            return linearCost + quadCost;
        }
    
        /// @notice Returns current price per token (wei) on the curve
        /// @param netTokensSold Signed net tokens sold (positive means tokens in circulation)
        /// @param basePrice      Initial price per token (wei)
        /// @param priceIncrement Incremental price per whole token (wei)
        /// @return price Current price (wei) for one token-wei
        function currentPrice(
            int256 netTokensSold,
            uint256 basePrice,
            uint256 priceIncrement
        ) internal pure returns (uint256 price) {
            uint256 sold = netTokensSold > 0 ? uint256(netTokensSold) : 0;
            return basePrice + (priceIncrement * sold) / 1e18;
        }
    
        /// @notice Calculates how many token-wei you receive for a given spend (wei)
        /// @dev Solves k·q² + (2b + 2k·s − k)·q − 2·c = 0 for q, then scales by 1e18
        /// @param sold            Whole tokens already sold (not token-wei)
        /// @param spendWei        Wei you're spending
        /// @param basePrice       Initial price per token (wei)
        /// @param priceIncrement  Increment per whole token (wei)
        /// @return qWei           Token-wei you receive
        function tokensForSpend(
            uint256 sold,
            uint256 spendWei,
            uint256 basePrice,
            uint256 priceIncrement
        ) internal pure returns (uint256 qWei) {
            // Flat curve shortcut
            if (priceIncrement == 0) {
                return (spendWei * 1e18) / basePrice;
            }
    
            unchecked {
                // Quadratic coefficients
                // A = k
                // B = 2b + 2k·s − k
                // Discriminant D = B² + 8A·spendWei
                uint256 A = priceIncrement;
                uint256 B = 2 * basePrice + priceIncrement * (2 * sold - 1);
                uint256 D = B * B + 8 * A * spendWei;
    
                uint256 root = _sqrt(D);
                uint256 q    = (root - B) / (2 * A);
    
                qWei = q * 1e18;
            }
        }
    
        /// @dev Babylonian method for integer square root
        function _sqrt(uint256 y) private pure returns (uint256 z) {
            if (y == 0) return 0;
            uint256 x = y;
            z = (x + 1) >> 1;
            while (z < x) {
                x = z;
                z = (y / z + z) >> 1;
            }
        }
    }
  • contracts/CurveLaunch.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
    import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
    import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
    import "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
    import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
    import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
    import "@openzeppelin/contracts/utils/Address.sol";
    import "./BondingCurve.sol";
    
    using Address for address payable;
    
    contract CurveLaunch is Initializable, ReentrancyGuardUpgradeable {
        /// @notice URI of the token’s logo (ipfs://...)
        string public imageURI;
    
        /// @notice Emitted when tokens are sold back to the contract
        event TokensSold(
            address indexed seller,
            uint256 tokenAmount,
            uint256 grossReturn,
            uint256 devFee,
            uint256 userGets
        );
    
        event TokensBought(
            address indexed buyer,
            uint256 bnbSpent,
            uint256 tokenAmount,
            uint256 devFee,
            uint256 netSpent
        );
    
        /// @notice Emitted every time the live price or market-cap changes
        event PriceUpdate   (uint256 priceUsd,     uint256 timestamp);
        event MarketCapUpdate(uint256 marketCapUsd, uint256 timestamp);
    
        /// @notice Total token-wei available for sale
        uint256 public tokensForSale;
    
        /// @dev Initial price per token in wei (0.00000035 BNB)
        uint256 private constant BASE_PRICE_WEI = 350_000_000_000;
    
        /// @dev Multiplier for the final price (last = BASE_PRICE_WEI × FACTOR)
        uint256 private constant FACTOR = 5;
    
        /// @notice Initialization parameters for a new launch
        struct LaunchParams {
            address creator;
            address token;
            bool    isRefundable;
            bool    creatorBuys;
            uint256 creatorBuyAmount;
            address devWallet;
            address priceFeed;
            address dexRouter;
            address wbnb;
            uint256 curveCap;
            uint256 buyFeePercent;
            uint256 sellFeePercent;
            uint256 maxBuy;
            uint256 finalizeTime;
            uint256 migrationFeePercent;
            uint256 creatorRewardPercent;
            bool    claimLP;
            string  imageURI;
        }
    
        // State variables
        address public creator;
        address public token;
        address public devWallet;
        AggregatorV3Interface public priceFeed;
        IUniswapV2Router02 public dexRouter;
        address public wbnb;
    
        uint256 public createdAt;
        uint256 public duration;
        uint256 public curveCap;
        uint256 public buyFeePercent;
        uint256 public sellFeePercent;
        uint256 public maxBuy;
        uint256 public migrationFeePercent;
        uint256 public creatorRewardPercent;
    
        bool    public finalized;
        bool    public isRefundable;
        bool    public creatorBuys;
        bool    public claimLPMode;
        uint256 public creatorBuyAmount;
    
        uint256 public totalRaised;
        int256  public netTokensSold;
    
        uint256 private _totalTokensAllocated;
        uint256 private _truncatedTokensSold;
        uint256 private _remainderTokens;
        uint256 public totalNetSpend;
    
        uint256 public basePrice;
        uint256 public priceIncrement;
    
        IERC20 public lpToken;
        uint256 public soldSnapshot;
    
        uint256 public beLPSupply;
        uint256 public profitLPSupply;
        uint256 public totalNetSnapshot;
    
        /// @notice Buyer allocation tracking
        struct Buyer {
            uint256 bnbPaid;
            uint256 netSpend;
            uint256 tokensAllocated;
            bool    claimed;
        }
    
        /// @notice Per‐address purchase/accounting state
        mapping(address => Buyer) public buyers;
    
        /// @notice Only the developer wallet can call
        modifier onlyDevWallet() {
            require(msg.sender == devWallet);
            _;
        }
    
        /// @notice Syncs token tracking after state changes
        modifier syncTokenTracking() {
            _;
            uint256 totalWei = _totalTokensAllocated + _remainderTokens;
            _truncatedTokensSold = totalWei / 1e18;
            _remainderTokens     = totalWei % 1e18;
        }
    
        /// @notice Initialize a cloned launch with parameters
        function initialize(LaunchParams calldata p) external payable initializer {
            require(msg.value == p.creatorBuyAmount, "msg.value mismatch");
            __ReentrancyGuard_init();
            require(p.creator != address(0) && p.devWallet != address(0) && p.token != address(0), "Invalid params");
    
            uint256 avgPriceWei  = (BASE_PRICE_WEI * (FACTOR + 1)) / 2;
            uint256 wholeTokensN = p.curveCap / avgPriceWei;
            require(wholeTokensN >= 2, "Cap too small");
    
            basePrice      = BASE_PRICE_WEI;
            priceIncrement = (BASE_PRICE_WEI * (FACTOR - 1)) / (wholeTokensN - 1);
            tokensForSale  = wholeTokensN * 1e18;
    
            require(basePrice > 0 && priceIncrement > 0, "Curve mis-configured");
    
            creator             = p.creator;
            token               = p.token;
            isRefundable        = p.isRefundable;
            claimLPMode         = p.claimLP;
            creatorBuys         = p.creatorBuys;
            creatorBuyAmount    = p.creatorBuyAmount;
            devWallet           = p.devWallet;
            priceFeed           = AggregatorV3Interface(p.priceFeed);
            dexRouter           = IUniswapV2Router02(p.dexRouter);
            wbnb                = p.wbnb;
            imageURI            = p.imageURI;
            createdAt           = block.timestamp;
            curveCap            = p.curveCap;
            buyFeePercent       = p.buyFeePercent;
            sellFeePercent      = p.sellFeePercent;
            maxBuy              = p.maxBuy;
            duration            = p.finalizeTime;
            migrationFeePercent = p.migrationFeePercent;
            creatorRewardPercent= p.creatorRewardPercent;
    
            if (p.creatorBuys && p.creatorBuyAmount > 0) {
                require(msg.value <= maxBuy, "Amount is above maxBuy");
                _internalBuy(p.creator, p.creatorBuyAmount);
            }
        }
    
        /// @notice Public buy during launch
        function buy() external payable nonReentrant {
            if (isRefundable) {
                require(block.timestamp <= createdAt + duration, "Launch expired");
            }
            require(!finalized, "Already finalized");
            require(msg.value <= maxBuy, "Above maxBuy");
            require(!drainMode, "Launch failed, can't buy");
            require(msg.value > 0, "Invalid amount");
    
            uint256 spend = _clampGrossSpend();
            _internalBuy(msg.sender, spend);
    
            if (totalRaised >= curveCap) {
                finalized = true;
                _finalizeLaunch();
            }
        }
    
        /// @dev Core accounting for buys, syncs token tracking
        function _internalBuy(address buyer, uint256 amount) internal syncTokenTracking {
            Buyer storage b = buyers[buyer];
    
            uint256 room = maxBuy - b.bnbPaid;
            if (amount > room) {
                uint256 refund = amount - room;
                payable(buyer).sendValue(refund);
                amount = room;
            }
            require(amount > 0, "Exceeds max per wallet");
    
            uint256 devFee      = (amount * buyFeePercent) / 100;
            uint256 purchaseAmt = amount - devFee;
            require(
                BondingCurve.currentPrice(int256(_truncatedTokensSold), basePrice, priceIncrement) > 0,
                "Curve mis-configured"
            );
    
            uint256 tokens = BondingCurve.tokensForSpend(
                _truncatedTokensSold,
                purchaseAmt,
                basePrice,
                priceIncrement
            );
            require(tokens > 0, "Amount too small");
    
            require(_totalTokensAllocated + tokens <= tokensForSale, "Sold out");
    
            b.bnbPaid             += amount;
            b.netSpend            += purchaseAmt;
            totalNetSpend         += purchaseAmt;
            b.tokensAllocated     += tokens;
            totalRaised           += purchaseAmt;
            _totalTokensAllocated += tokens;
            netTokensSold         += int256(tokens);
    
            payable(devWallet).sendValue(devFee);
    
            emit TokensBought(
                buyer,
                amount,
                tokens,
                devFee,
                purchaseAmt
            );
    
            _emitMetrics();
        }
    
        /// @notice Finalize launch, add liquidity, and burn leftovers
        bool public lpFailed;
        bool public drainMode;
        function _finalizeLaunch() internal {
            require(
                IERC20(token).balanceOf(address(this)) >= _totalTokensAllocated,
                "Insufficient token balance for allocations"
            );
    
            uint256 finalPrice = BondingCurve.currentPrice(netTokensSold, basePrice, priceIncrement);
            require(finalPrice > 0, "Zero final price");
    
            uint256 cut    = (totalRaised * migrationFeePercent) / 100;
            uint256 reward = (totalRaised * creatorRewardPercent) / 100;
            uint256 liquidityBNB = totalRaised - cut - reward;
    
            finalized = true;
            bool added;
            uint256 tokensForLP = (liquidityBNB * 1e18) / finalPrice;
            uint256 available   = IERC20(token).balanceOf(address(this));
            if (tokensForLP > available) tokensForLP = available;
    
            if (!claimLPMode) {
                uint256 avail       = IERC20(token).balanceOf(address(this));
                if (tokensForLP > avail) tokensForLP = avail;
    
                if (address(dexRouter).code.length > 0 && tokensForLP > 0) {
                    IERC20(token).approve(address(dexRouter), tokensForLP);
                    try dexRouter.addLiquidityETH{ value: liquidityBNB }(
                        token,
                        tokensForLP,
                        (tokensForLP * 98) / 100,
                        (liquidityBNB * 98) / 100,
                        address(0xdead),
                        block.timestamp
                    ) {
                        added = true;
                    } catch {
                        lpFailed = true;
                    }
                } else {
                    lpFailed = true;
                }
            }
            else {
                uint256 beBNB    = liquidityBNB / 2;
                uint256 beTokens = (beBNB * 1e18) / finalPrice;
    
                uint256 profitBNB = liquidityBNB - beBNB;
                uint256 prTokens  = (profitBNB * 1e18) / finalPrice;
    
                if (beTokens + prTokens > available) {
                    uint256 scale = (available * 1e18) / (beTokens + prTokens);
                    beTokens  = (beTokens  * scale) / 1e18;
                    prTokens  = available - beTokens;
                    beBNB     = (beBNB     * scale) / 1e18;
                    profitBNB = (profitBNB * scale) / 1e18;
                }
    
                if (address(dexRouter).code.length > 0 && (beTokens > 0 || prTokens > 0)) {
                    IERC20(token).approve(address(dexRouter), beTokens);
                    (, , beLPSupply) = dexRouter.addLiquidityETH{ value: beBNB }(
                        token, beTokens,
                        (beTokens * 98) / 100,
                        (beBNB   * 98) / 100,
                        address(this),
                        block.timestamp
                    );
    
                    IERC20(token).approve(address(dexRouter), prTokens);
                    (, , profitLPSupply) = dexRouter.addLiquidityETH{ value: profitBNB }(
                        token, prTokens,
                        (prTokens  * 98) / 100,
                        (profitBNB * 98) / 100,
                        address(this),
                        block.timestamp
                    );
    
                    totalNetSnapshot = totalNetSpend == 0 ? 1 : totalNetSpend;
                    soldSnapshot     = uint256(netTokensSold) == 0 ? 1 : uint256(netTokensSold);
    
                    address pair = IUniswapV2Factory(dexRouter.factory()).getPair(token, dexRouter.WETH());
                    lpToken = IERC20(pair);
                    added   = true;
                } else {
                    lpFailed = true;
                }
            }
    
            if (added) {
                payable(devWallet).sendValue(cut);
                payable(creator  ).sendValue(reward);
            }
    
            uint256 remaining = IERC20(token).balanceOf(address(this));
            uint256 keep;
    
            if (claimLPMode && added && !lpFailed) {
                keep = 0;
                _totalTokensAllocated = 0;
                _truncatedTokensSold  = 0;
                _remainderTokens      = 0;
            } else {
                keep = _totalTokensAllocated + (added ? 0 : tokensForLP);
            }
    
            if (remaining > keep) {
                ERC20Burnable(token).burn(remaining - keep);
            }
    
            _emitMetrics();
        }
    
        /// @notice Claim purchased tokens or LP
        function claim() external nonReentrant {
            require(finalized && !lpFailed, "Cannot claim");
            Buyer storage b = buyers[msg.sender];
            require(!b.claimed && (b.netSpend > 0 || b.tokensAllocated > 0), "Nothing to claim");
            b.claimed = true;
    
            if (claimLPMode) {
                uint256 beShare = (beLPSupply     * b.netSpend)        / totalNetSnapshot;
                // profit share  (profitLPSupply is 0 when claimLP == false)
                uint256 prShare = soldSnapshot == 0
                    ? 0
                    : (profitLPSupply * b.tokensAllocated) / soldSnapshot;
                uint256 totalLP = beShare + prShare;
                require(totalLP > 0, "Nothing to claim");
                require(lpToken.transfer(msg.sender, totalLP), "LP transfer failed");
                // OPTIONAL: clean up their accounting to save gas on storage-refund
                b.tokensAllocated = 0;
                b.netSpend        = 0;
            } else {
                require(IERC20(token).transfer(msg.sender, b.tokensAllocated), "Transfer failed");
                b.tokensAllocated = 0;
            }
        }
    
        /// @notice Refund if LP addition failed
        function claimRefundIfLPFailed() external nonReentrant {
            require(finalized && lpFailed, "Not refundable");
    
            Buyer storage b = buyers[msg.sender];
            uint256 net = b.netSpend;
            require(net > 0, "Nothing to refund");
    
            b.bnbPaid  = 0;
            b.netSpend = 0;
    
            payable(msg.sender).sendValue(net);
        }
    
        /// @notice Refund if launch failed or expired
        function claimRefund() external nonReentrant {
            require(isRefundable, "No refunds");
            require(block.timestamp > createdAt + duration, "Still active");
            require(!finalized, "Already succeeded");
            Buyer storage b = buyers[msg.sender];
            uint256 feePerc = buyFeePercent;
            uint256 net = (b.bnbPaid * (100 - feePerc)) / 100;
            require(net > 0, "Nothing to refund");
            b.bnbPaid = 0;
            b.tokensAllocated = 0;
            payable(msg.sender).sendValue(net);
        }
    
        /// @notice Sell tokens back to contract after endtime
        function sellTokens(uint256 tokenAmt) external nonReentrant syncTokenTracking {
            Buyer storage b = buyers[msg.sender];
            require(!finalized, "Launch finalised");
            require(tokenAmt > 0 && tokenAmt <= b.tokensAllocated, "Invalid amount");
            require(!isRefundable, "Sell not allowed");
            require(block.timestamp > createdAt + duration, "Locked");
    
            if (drainMode) {
                _sellDrainMode(b, tokenAmt);
                return;
            }
    
            uint256 start       = uint256(netTokensSold) - tokenAmt;
            uint256 grossReturn = BondingCurve.areaUnderCurve(start, tokenAmt, basePrice, priceIncrement);
            uint256 devFee      = (grossReturn * sellFeePercent) / 100;
            uint256 userGets    = grossReturn - devFee;
            uint256 pool        = address(this).balance;
    
            if (userGets + devFee >= pool) {
                drainMode = true;
                _sellDrainMode(b, tokenAmt);
                return;
            }
    
            b.tokensAllocated -= tokenAmt;
    
            uint256 refund = userGets;
            if (refund > b.bnbPaid) b.bnbPaid = 0;
            else b.bnbPaid -= refund;
    
            if (refund > b.netSpend) { totalNetSpend -= b.netSpend; b.netSpend = 0; }
            else { b.netSpend -= refund; totalNetSpend -= refund; }
    
            if (b.tokensAllocated == 0 && b.netSpend > 0) {
                totalNetSpend -= b.netSpend;
                b.netSpend = 0;
            }
    
            netTokensSold     -= int256(tokenAmt);
            _totalTokensAllocated -= tokenAmt;
            totalRaised       -= (userGets + devFee);
    
            payable(msg.sender).sendValue(userGets);
            payable(devWallet).sendValue(devFee);
    
            emit TokensSold(msg.sender, tokenAmt, grossReturn, devFee, userGets);
    
            _emitMetrics();
        }
    
        /// @dev Emergency BNB withdraw for devWallet
        function emergencyWithdraw() external onlyDevWallet {
            uint256 bal = address(this).balance;
            require(bal > 0);
            payable(msg.sender).sendValue(bal);
        }
    
        /// @dev Clamp spend so totalRaised + net <= curveCap
        function _clampGrossSpend() internal returns (uint256 spend) {
            uint256 room = curveCap - totalRaised;
            require(room > 0, "Filled");
            uint256 grossNeeded = (room * 100) / (100 - buyFeePercent);
            if ((grossNeeded * (100 - buyFeePercent)) / 100 < room) grossNeeded++;
    
            spend = msg.value;
            if (spend > grossNeeded) {
                uint256 refund = spend - grossNeeded;
                spend = grossNeeded;
                payable(msg.sender).sendValue(refund);
            }
        }
    
        /// @notice Current price in USD (same decimals as Chainlink feed)
        function getCurrentPriceUsd() public view returns (uint256) {
            uint256 priceBnb = BondingCurve.currentPrice(netTokensSold, basePrice, priceIncrement);
            (, int256 p,,,) = priceFeed.latestRoundData();
            require(p > 0, "Invalid feed");
            return (priceBnb * uint256(p)) / 1e18;
        }
    
        /// @notice Live market-cap in USD (same decimals as Chainlink feed).
        function getLiveMarketCapUsd() public view returns (uint256) {
            uint256 priceWei = BondingCurve.currentPrice(
                netTokensSold,
                basePrice,
                priceIncrement
            );
            require(priceWei > 0, "Curve mis-configured");
    
            uint256 tokensSold = uint256(netTokensSold);
    
            uint256 cut    = (totalRaised * migrationFeePercent) / 100;
            uint256 reward = (totalRaised * creatorRewardPercent) / 100;
            uint256 liqBNB = totalRaised - cut - reward;
            uint256 projTokensLP = (liqBNB * 1e18) / priceWei;
    
            uint256 circulating = claimLPMode
                ? projTokensLP
                : (tokensSold + projTokensLP);
    
            (, int256 p,,,) = priceFeed.latestRoundData();
            require(p > 0, "Oracle error");
    
            return (circulating * priceWei * uint256(p)) / 1e18;
        }
    
        function getClaimView() external view returns (
            bool refundable,
            bool claimLP,
            uint256 endTime,
            uint256 raised,
            uint256 cap,
            bool lpFailed_
        )
        {
            refundable = isRefundable;
            claimLP    = claimLPMode;
            endTime    = createdAt + duration;
            raised     = totalRaised;
            cap        = curveCap;
            lpFailed_  = lpFailed;
        }
    
        /// @dev Internal sell in pro-rata (drain) mode
        function _sellDrainMode(Buyer storage b, uint256 tokenAmt) internal {
            require(netTokensSold > 0, "Nothing to redeem");
            uint256 pool   = address(this).balance;
            uint256 payout = (pool * tokenAmt) / uint256(netTokensSold);
            require(payout > 0, "Pool empty");
    
            b.tokensAllocated -= tokenAmt;
            if (payout > b.bnbPaid) b.bnbPaid = 0; else b.bnbPaid -= payout;
            if (payout > b.netSpend) {
                totalNetSpend -= b.netSpend;
                b.netSpend     = 0;
            } else {
                b.netSpend     -= payout;
                totalNetSpend -= payout;
            }
            if (b.tokensAllocated == 0 && b.netSpend > 0) {
                totalNetSpend -= b.netSpend;
                b.netSpend = 0;
            }
            netTokensSold -= int256(tokenAmt);
            _totalTokensAllocated -= tokenAmt;
            totalRaised = pool - payout;
    
            payable(msg.sender).sendValue(payout);
            emit TokensSold(msg.sender, tokenAmt, payout, 0, payout);
    
            _emitMetrics();
        }
    
        function _emitMetrics() private {
            uint256 p  = getCurrentPriceUsd();
            uint256 mc = getLiveMarketCapUsd();
            uint256 ts = block.timestamp;
    
            emit PriceUpdate(p, ts);
            emit MarketCapUpdate(mc, ts);
        }
    
        receive() external payable {}
    }
  • contracts/CurveToken.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    
    import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
    
    /// @notice ERC20 token minted to a launch contract for sale
    contract CurveToken is ERC20Burnable {
        /// @notice Deploys token and mints total supply to `initialHolder`
        /// @param tokenName     Name of the ERC20 token
        /// @param tokenSymbol   Symbol of the ERC20 token
        /// @param initialHolder Address receiving the initial supply
        constructor(
            string memory tokenName,
            string memory tokenSymbol,
            address initialHolder
        ) ERC20(tokenName, tokenSymbol) {
            uint256 initialSupply = 10_000_000_000 * 10 ** decimals();
            _mint(initialHolder, initialSupply);
        }
    }
ABI · 30 entries
json
[
  {
    "type": "constructor",
    "inputs": [
      {
        "name": "_launchImpl",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "_priceFeed",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "_dexRouter",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "_wbnb",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "_curveCapUsd",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "_maxBuy",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "_buyFeePercent",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "_sellFeePercent",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "_migrationFeePercent",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "_creatorRewardPercent",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "FailedDeployment",
    "type": "error",
    "inputs": []
  },
  {
    "name": "InsufficientBalance",
    "type": "error",
    "inputs": [
      {
        "name": "balance",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "needed",
        "type": "uint256",
        "internalType": "uint256"
      }
    ]
  },
  {
    "name": "ReentrancyGuardReentrantCall",
    "type": "error",
    "inputs": []
  },
  {
    "name": "buyFeePercent",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "createLaunch",
    "type": "function",
    "inputs": [
      {
        "name": "name",
        "type": "string",
        "internalType": "string"
      },
      {
        "name": "symbol",
        "type": "string",
        "internalType": "string"
      },
      {
        "name": "imageURI",
        "type": "string",
        "internalType": "string"
      },
      {
        "name": "isRefundable",
        "type": "bool",
        "internalType": "bool"
      },
      {
        "name": "claimLP",
        "type": "bool",
        "internalType": "bool"
      },
      {
        "name": "creatorPreBuys",
        "type": "bool",
        "internalType": "bool"
      },
      {
        "name": "_durationSeconds",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "launchProxy",
        "type": "address",
        "internalType": "address payable"
      }
    ],
    "stateMutability": "payable"
  },
  {
    "name": "creatorRewardPercent",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "curveCap",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "devWallet",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "dexRouter",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "feedDecimals",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint8",
        "internalType": "uint8"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "getBNBForUSD",
    "type": "function",
    "inputs": [
      {
        "name": "usdAmount",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "launchImplementation",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "launches",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "launchContract",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "name",
        "type": "string",
        "internalType": "string"
      },
      {
        "name": "symbol",
        "type": "string",
        "internalType": "string"
      },
      {
        "name": "imageURI",
        "type": "string",
        "internalType": "string"
      },
      {
        "name": "createdAt",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "launchesCount",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "maxBuy",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "migrationFeePercent",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "owner",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "priceFeed",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "contract AggregatorV3Interface"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "sellFeePercent",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "setCurveCap",
    "type": "function",
    "inputs": [
      {
        "name": "usdAmount",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setDevWallet",
    "type": "function",
    "inputs": [
      {
        "name": "newWallet",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setDexRouter",
    "type": "function",
    "inputs": [
      {
        "name": "_router",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setLpFeePercents",
    "type": "function",
    "inputs": [
      {
        "name": "_migrationFee",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "_creatorReward",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setMaxBuy",
    "type": "function",
    "inputs": [
      {
        "name": "etherAmount",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setPriceFeed",
    "type": "function",
    "inputs": [
      {
        "name": "_newFeed",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setTradeFeePercents",
    "type": "function",
    "inputs": [
      {
        "name": "_buyFeePercent",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "_sellFeePercent",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setWbnb",
    "type": "function",
    "inputs": [
      {
        "name": "_wbnb",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "tokenToLaunch",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "wbnb",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  }
]

Send feedback

Agents: POST the same to /api/feedback.