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"
}
]