LaunchFactoryV4R2
0x22b5c08869a5a6d12b4e5891a7e78d04817b4e21
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
12,139 bytes
ABI entries
42
27 read · 4 write
License
none
Contract information
- Address
- 0x22b5c08869a5a6d12b4e5891a7e78d04817b4e21
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0xFc2007F0B9…2681EEdA5B
- Creation tx
- 0xe80cf56d89…37ce233fa0
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (27)
FACTORY_VERSION() → uint256
FULL_TICK_LOWER() → int24
FULL_TICK_UPPER() → int24
LP_FEE() → uint24
POST_GRAD_CREATOR_BPS() → uint16
TICK_LOWER() → int24
TICK_SPACING() → int24
TICK_UPPER() → int24
TOKEN_METADATA_ONCHAIN() → bool
TOTAL_SUPPLY() → uint256
computeTokenAddress(bytes32) → address
graduationEth() → uint256
hook() → address
isLaunchToken(address) → bool
isOgTicker(address) → bool
isPool(address) → bool
isSymbolTaken(string) → bool
launchFee() → uint256
launches(uint256) → address, address, bytes32, uint256
launchesCount() → uint256
locker() → address
minDevBuy() → uint256
poolKeyOf(address) → tuple
poolManager() → address
protocolTreasury() → address
symbolToToken(string) → address
tokenImpl() → address
Events (2)
GraduatedLaunched
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "poolManager_",
"type": "address"
},
{
"internalType": "address",
"name": "hook_",
"type": "address"
},
{
"internalType": "address",
"name": "locker_",
"type": "address"
},
{
"internalType": "address",
"name": "protocolTreasury_",
"type": "address"
},
{
"internalType": "uint256",
"name": "launchFee_",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "minDevBuy_",
"type": "uint256"
},
{
"internalType": "int24",
"name": "tickLower_",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper_",
"type": "int24"
},
{
"internalType": "uint256",
"name": "graduationEth_",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "BadSymbol",
"type": "error"
},
{
"inputs": [],
"name": "FailedDeployment",
"type": "error"
},
{
"inputs": [],
"name": "FeeTransferFailed",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "needed",
"type": "uint256"
}
],
"name": "InsufficientBalance",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientValue",
"type": "error"
},
{
"inputs": [],
"name": "NotGraduatable",
"type": "error"
},
{
"inputs": [],
"name": "NotPoolManager",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "SafeERC20FailedOperation",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "ethToLp",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "tokensToLp",
"type": "uint256"
}
],
"name": "Graduated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"indexed": true,
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "string",
"name": "metadataURI",
"type": "string"
},
{
"indexed": false,
"internalType": "uint256",
"name": "devBuyEth",
"type": "uint256"
}
],
"name": "Launched",
"type": "event"
},
{
"inputs": [],
"name": "FACTORY_VERSION",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "FULL_TICK_LOWER",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "FULL_TICK_UPPER",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "LP_FEE",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "POST_GRAD_CREATOR_BPS",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TICK_LOWER",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TICK_SPACING",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TICK_UPPER",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TOKEN_METADATA_ONCHAIN",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TOTAL_SUPPLY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"name": "computeTokenAddress",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "graduate",
"outputs": [
{
"internalType": "uint256",
"name": "ethToLp",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tokensToLp",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "graduationEth",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "hook",
"outputs": [
{
"internalType": "contract FoundryHookV4R2",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "isLaunchToken",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "isOgTicker",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "isPool",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "symbol",
"type": "string"
}
],
"name": "isSymbolTaken",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "string",
"name": "metadataURI",
"type": "string"
},
{
"components": [
{
"internalType": "string",
"name": "logo",
"type": "string"
},
{
"internalType": "string",
"name": "description",
"type": "string"
},
{
"internalType": "string",
"name": "website",
"type": "string"
},
{
"internalType": "string",
"name": "twitter",
"type": "string"
},
{
"internalType": "string",
"name": "telegram",
"type": "string"
}
],
"internalType": "struct LaunchpadTokenV4.TokenMeta",
"name": "meta",
"type": "tuple"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"name": "launch",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "launchFee",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "string",
"name": "metadataURI",
"type": "string"
},
{
"components": [
{
"internalType": "string",
"name": "logo",
"type": "string"
},
{
"internalType": "string",
"name": "description",
"type": "string"
},
{
"internalType": "string",
"name": "website",
"type": "string"
},
{
"internalType": "string",
"name": "twitter",
"type": "string"
},
{
"internalType": "string",
"name": "telegram",
"type": "string"
}
],
"internalType": "struct LaunchpadTokenV4.TokenMeta",
"name": "meta",
"type": "tuple"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "address[]",
"name": "payoutWallets",
"type": "address[]"
},
{
"internalType": "uint16[]",
"name": "payoutBps",
"type": "uint16[]"
}
],
"name": "launchWithPayout",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "launches",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "launchBlock",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "launchesCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "locker",
"outputs": [
{
"internalType": "contract FeeLockerV4R2",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "minDevBuy",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "poolKeyOf",
"outputs": [
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "poolManager",
"outputs": [
{
"internalType": "contract IPoolManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "protocolTreasury",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "symbol",
"type": "string"
}
],
"name": "symbolToToken",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tokenImpl",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "unlockCallback",
"outputs": [
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol";
import {IUnlockCallback} from "v4-core/src/interfaces/callback/IUnlockCallback.sol";
import {IHooks} from "v4-core/src/interfaces/IHooks.sol";
import {PoolKey} from "v4-core/src/types/PoolKey.sol";
import {PoolId, PoolIdLibrary} from "v4-core/src/types/PoolId.sol";
import {Currency, CurrencyLibrary} from "v4-core/src/types/Currency.sol";
import {SwapParams} from "v4-core/src/types/PoolOperation.sol";
import {BalanceDelta, BalanceDeltaLibrary} from "v4-core/src/types/BalanceDelta.sol";
import {TickMath} from "v4-core/src/libraries/TickMath.sol";
import {CurrencySettler} from "v4-core/test/utils/CurrencySettler.sol";
import {LiquidityAmounts} from "v4-core/test/utils/LiquidityAmounts.sol";
import {LaunchpadTokenV4} from "../v4/LaunchpadTokenV4.sol";
import {FoundryHookV4R2} from "./FoundryHookV4R2.sol";
import {FeeLockerV4R2} from "./FeeLockerV4R2.sol";
/// @title LaunchFactoryV4R2
/// @notice Revision 2 of the Uniswap-V4 launch factory. The launch flow (clone token → register hook →
/// initialize pool → lock full supply forever → route launch fee → mandatory dev-buy first swap) is
/// IDENTICAL to LaunchFactoryV4. What changed is where the fee-payout config lives: the optional
/// single-signature creator-fee split now goes to the LOCKER (`FeeLockerV4R2.setInitialPayout`) — the
/// contract where fee money actually moves — instead of the V4 hook's parallel (money-less) config.
/// The token implementation (`LaunchpadTokenV4`, verified) is reused unchanged: clones are born
/// verified on Blockscout, which is what keeps first-seconds scanner verdicts clean.
contract LaunchFactoryV4R2 is IUnlockCallback {
using SafeERC20 for IERC20;
using PoolIdLibrary for PoolKey;
using CurrencyLibrary for Currency;
using CurrencySettler for Currency;
using BalanceDeltaLibrary for BalanceDelta;
/// @dev Launchpad GENERATION counter (V1-V3 curve era, V4 = first Uniswap-V4 stack, 5 = this one).
/// The app resolves coins per-generation off this. The contract NAMES stay V4-based because the
/// underlying AMM is (and remains) Uniswap V4.
uint256 public constant FACTORY_VERSION = 5;
bool public constant TOKEN_METADATA_ONCHAIN = true;
uint256 public constant TOTAL_SUPPLY = 1_000_000_000 ether;
uint16 public constant POST_GRAD_CREATOR_BPS = 5000; // locker FALLBACK split (epoch split preferred)
uint24 public constant LP_FEE = 0x800000; // DYNAMIC_FEE_FLAG
int24 public constant TICK_SPACING = 60;
int24 public constant FULL_TICK_LOWER = -887220;
int24 public constant FULL_TICK_UPPER = 887220;
int24 public immutable TICK_LOWER;
int24 public immutable TICK_UPPER;
uint256 public immutable graduationEth;
IPoolManager public immutable poolManager;
FoundryHookV4R2 public immutable hook;
FeeLockerV4R2 public immutable locker;
address public immutable protocolTreasury;
address public immutable tokenImpl;
uint256 public immutable launchFee;
uint256 public immutable minDevBuy;
struct Launch {
address token;
address creator;
PoolId poolId;
uint256 launchBlock;
}
Launch[] public launches;
mapping(address => bool) public isPool;
mapping(address => bool) public isLaunchToken;
mapping(bytes32 => address) internal _ogTokenByKey;
event Launched(
address indexed token, address indexed creator, PoolId indexed poolId, string metadataURI, uint256 devBuyEth
);
event Graduated(PoolId indexed poolId, address indexed token, uint256 ethToLp, uint256 tokensToLp);
error InsufficientValue();
error FeeTransferFailed();
error BadSymbol();
error NotPoolManager();
error NotGraduatable();
/// @dev Locker<->factory circularity resolved by deterministic address prediction, exactly like
/// V4/V3: locker deployed one nonce before this factory with the PREDICTED factory address.
constructor(
address poolManager_,
address hook_,
address locker_,
address protocolTreasury_,
uint256 launchFee_,
uint256 minDevBuy_,
int24 tickLower_,
int24 tickUpper_,
uint256 graduationEth_
) {
require(tickLower_ < tickUpper_, "bad ticks");
require(tickLower_ % TICK_SPACING == 0 && tickUpper_ % TICK_SPACING == 0, "unaligned ticks");
require(graduationEth_ > 0, "bad graduationEth");
poolManager = IPoolManager(poolManager_);
hook = FoundryHookV4R2(hook_);
locker = FeeLockerV4R2(payable(locker_));
protocolTreasury = protocolTreasury_;
launchFee = launchFee_;
minDevBuy = minDevBuy_;
TICK_LOWER = tickLower_;
TICK_UPPER = tickUpper_;
graduationEth = graduationEth_;
tokenImpl = address(new LaunchpadTokenV4());
}
/// @notice Launch with the default payout (100% to the creator). Same selector shape as V4 so the
/// app's launch encoder ports with only an address change.
function launch(
string calldata name,
string calldata symbol,
string calldata metadataURI,
LaunchpadTokenV4.TokenMeta calldata meta,
bytes32 salt
) external payable returns (address token, PoolId poolId) {
return _launchCore(name, symbol, metadataURI, meta, salt, new address[](0), new uint16[](0));
}
/// @notice Single-signature launch WITH a creator-fee split applied in the SAME tx — now recorded in
/// the LOCKER (one-shot, factory-only, full validation + floor-ratchet seeding there).
function launchWithPayout(
string calldata name,
string calldata symbol,
string calldata metadataURI,
LaunchpadTokenV4.TokenMeta calldata meta,
bytes32 salt,
address[] calldata payoutWallets,
uint16[] calldata payoutBps
) external payable returns (address token, PoolId poolId) {
return _launchCore(name, symbol, metadataURI, meta, salt, payoutWallets, payoutBps);
}
function _launchCore(
string calldata name,
string calldata symbol,
string calldata metadataURI,
LaunchpadTokenV4.TokenMeta calldata meta,
bytes32 salt,
address[] memory payoutWallets,
uint16[] memory payoutBps
) internal returns (address token, PoolId poolId) {
if (msg.value < launchFee + minDevBuy) revert InsufficientValue();
bytes32 sk = _symbolKey(symbol);
token = _cloneAndInit(name, symbol, metadataURI, meta, salt);
PoolKey memory key = _poolKey(token);
poolId = key.toId();
uint128 liquidity = LiquidityAmounts.getLiquidityForAmount1(
TickMath.getSqrtPriceAtTick(TICK_LOWER), TickMath.getSqrtPriceAtTick(TICK_UPPER), TOTAL_SUPPLY
);
// Register geometry with the hook BEFORE initializing the pool (unregistered pools are inert).
hook.registerPool(key, msg.sender, TICK_LOWER, TICK_UPPER, liquidity, graduationEth);
poolManager.initialize(key, TickMath.getSqrtPriceAtTick(TICK_UPPER));
// Lock the full-supply single-sided position forever, then apply the OPTIONAL one-shot launch
// fee split IN THE LOCKER (the V4R2 single-source-of-truth change).
IERC20(token).safeTransfer(address(locker), TOTAL_SUPPLY);
locker.lockLiquidity(
key, TICK_LOWER, TICK_UPPER, liquidity, token, msg.sender, protocolTreasury, POST_GRAD_CREATOR_BPS
);
if (payoutWallets.length > 0) {
locker.setInitialPayout(token, payoutWallets, payoutBps);
}
isPool[token] = true;
isLaunchToken[token] = true;
if (_ogTokenByKey[sk] == address(0)) _ogTokenByKey[sk] = token;
launches.push(Launch({token: token, creator: msg.sender, poolId: poolId, launchBlock: block.number}));
if (launchFee > 0) {
(bool ok,) = protocolTreasury.call{value: launchFee}("");
if (!ok) revert FeeTransferFailed();
}
uint256 devBuyEth = msg.value - launchFee;
poolManager.unlock(abi.encode(key, msg.sender, devBuyEth));
emit Launched(token, msg.sender, poolId, metadataURI, devBuyEth);
}
/// @notice PoolManager unlock callback: executes the mandatory dev buy and forwards the bought
/// tokens to the creator.
function unlockCallback(bytes calldata data) external returns (bytes memory) {
if (msg.sender != address(poolManager)) revert NotPoolManager();
(PoolKey memory key, address creator, uint256 devBuyEth) = abi.decode(data, (PoolKey, address, uint256));
BalanceDelta delta = poolManager.swap(
key,
SwapParams({
zeroForOne: true,
amountSpecified: -int256(devBuyEth),
sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1
}),
""
);
if (delta.amount0() < 0) {
key.currency0.settle(poolManager, address(this), uint256(uint128(-delta.amount0())), false);
}
if (delta.amount1() > 0) {
poolManager.take(key.currency1, creator, uint256(uint128(delta.amount1())));
}
return "";
}
function _cloneAndInit(
string calldata name,
string calldata symbol,
string calldata metadataURI,
LaunchpadTokenV4.TokenMeta calldata meta,
bytes32 salt
) internal returns (address token) {
token = Clones.cloneDeterministic(tokenImpl, salt);
LaunchpadTokenV4(token).initialize(name, symbol, address(this), meta, metadataURI);
}
// ─────────────────────────── graduation ───────────────────────────
/// @notice Permissionless once the accumulated-ETH threshold is met. Identical to V4.
function graduate(address token) external returns (uint256 ethToLp, uint256 tokensToLp) {
if (!isLaunchToken[token]) revert NotGraduatable();
PoolKey memory key = _poolKey(token);
PoolId poolId = key.toId();
if (!hook.graduationReady(poolId)) revert NotGraduatable();
hook.markGraduated(poolId);
(ethToLp, tokensToLp) = locker.graduateRerange(token, FULL_TICK_LOWER, FULL_TICK_UPPER);
emit Graduated(poolId, token, ethToLp, tokensToLp);
}
// ─────────────────────────── views / helpers ───────────────────────────
function _poolKey(address token) internal view returns (PoolKey memory) {
return PoolKey({
currency0: CurrencyLibrary.ADDRESS_ZERO,
currency1: Currency.wrap(token),
fee: LP_FEE,
tickSpacing: TICK_SPACING,
hooks: IHooks(address(hook))
});
}
function poolKeyOf(address token) external view returns (PoolKey memory) {
return _poolKey(token);
}
function computeTokenAddress(bytes32 salt) public view returns (address) {
return Clones.predictDeterministicAddress(tokenImpl, salt, address(this));
}
function launchesCount() external view returns (uint256) {
return launches.length;
}
function symbolToToken(string calldata symbol) external view returns (address) {
return _ogTokenByKey[_symbolKey(symbol)];
}
function isOgTicker(address token) external view returns (bool) {
if (!isLaunchToken[token]) return false;
return _ogTokenByKey[_symbolKey(LaunchpadTokenV4(token).symbol())] == token;
}
function isSymbolTaken(string calldata symbol) external view returns (bool) {
return _ogTokenByKey[_symbolKey(symbol)] != address(0);
}
function _symbolKey(string memory symbol) internal pure returns (bytes32) {
bytes memory b = bytes(symbol);
uint256 len = b.length;
if (len == 0 || len > 32) revert BadSymbol();
for (uint256 i = 0; i < len; i++) {
if (b[i] >= 0x61 && b[i] <= 0x7a) b[i] = bytes1(uint8(b[i]) - 32);
}
return keccak256(b);
}
}
Chain explorer1800msChain node83ms