TokenFactory
0xeac47e690235fe008d94c1d1244df53d9f023248
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
5,725 bytes
ABI entries
23
17 read · 1 write
License
none
Contract information
- Address
- 0xeac47e690235fe008d94c1d1244df53d9f023248
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0xD61cd94965…80Bdb88E18
- Creation tx
- 0xcb507aa8cf…d2f88e5385
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (17)
CURVE_BUCKETS() → uint8
END_TICK() → int24
GRAD_TICK() → int24
MAX_VESTING_BPS() → uint16
START_TICK() → int24
TAIL_BUCKETS() → uint8
TICK_SPACING() → int24
feeRouter() → address
hook() → address
launchCount() → uint256
locker() → address
poolManager() → address
predictToken(address, string, uint256) → address
registry() → address
tokenImplementation() → address
vestingVault() → address
weth() → address
Events (1)
TokenCreated
ABI
[
{
"inputs": [
{
"internalType": "contract IPoolManager",
"name": "poolManager_",
"type": "address"
},
{
"internalType": "contract LaunchHook",
"name": "hook_",
"type": "address"
},
{
"internalType": "contract PermanentLocker",
"name": "locker_",
"type": "address"
},
{
"internalType": "contract FeeRouter",
"name": "feeRouter_",
"type": "address"
},
{
"internalType": "contract Registry",
"name": "registry_",
"type": "address"
},
{
"internalType": "address",
"name": "weth_",
"type": "address"
},
{
"internalType": "address",
"name": "tokenImplementation_",
"type": "address"
},
{
"internalType": "address",
"name": "vestingVault_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "FailedDeployment",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "needed",
"type": "uint256"
}
],
"name": "InsufficientBalance",
"type": "error"
},
{
"inputs": [],
"name": "SymbolRequired",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "socialClaimId",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "uint256",
"name": "launchBlock",
"type": "uint256"
},
{
"indexed": false,
"internalType": "string",
"name": "metadataURI",
"type": "string"
}
],
"name": "TokenCreated",
"type": "event"
},
{
"inputs": [],
"name": "CURVE_BUCKETS",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "END_TICK",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "GRAD_TICK",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_VESTING_BPS",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "START_TICK",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TAIL_BUCKETS",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TICK_SPACING",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "string",
"name": "metadataURI",
"type": "string"
},
{
"internalType": "bytes32",
"name": "socialClaimId",
"type": "bytes32"
},
{
"internalType": "uint16",
"name": "creatorFeeBps",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "claimerShareBps",
"type": "uint16"
},
{
"internalType": "address",
"name": "referrer",
"type": "address"
},
{
"internalType": "bool",
"name": "vestingEnabled",
"type": "bool"
}
],
"internalType": "struct TokenFactory.LaunchParams",
"name": "p",
"type": "tuple"
}
],
"name": "createToken",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "feeRouter",
"outputs": [
{
"internalType": "contract FeeRouter",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "hook",
"outputs": [
{
"internalType": "contract LaunchHook",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "launchCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "locker",
"outputs": [
{
"internalType": "contract PermanentLocker",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "poolManager",
"outputs": [
{
"internalType": "contract IPoolManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
}
],
"name": "predictToken",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "registry",
"outputs": [
{
"internalType": "contract Registry",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tokenImplementation",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "vestingVault",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "weth",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol";
import {IHooks} from "v4-core/interfaces/IHooks.sol";
import {PoolKey} from "v4-core/types/PoolKey.sol";
import {PoolId} from "v4-core/types/PoolId.sol";
import {Currency} from "v4-core/types/Currency.sol";
import {TickMath} from "v4-core/libraries/TickMath.sol";
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {LaunchToken} from "./LaunchToken.sol";
import {LaunchHook} from "./LaunchHook.sol";
import {PermanentLocker} from "./PermanentLocker.sol";
import {FeeRouter} from "./FeeRouter.sol";
import {Registry} from "./Registry.sol";
interface IWETH9 {
function deposit() external payable;
function transfer(address to, uint256 amount) external returns (bool);
}
/// @title TokenFactory
/// @notice Single entrypoint: one transaction deploys the token, initializes the v4 pool
/// behind the LaunchHook, seeds the curve-shaped liquidity ladder into the
/// PermanentLocker, optionally executes the creator's atomic dev-buy, and registers
/// everything. Standardization is the product: fixed 1B supply, fixed curve shape;
/// creators choose name/symbol/metadata and fee routing only.
///
/// The factory is upgrade-free. New versions are new factories registered into the
/// same Registry.
contract TokenFactory {
uint16 public constant MAX_VESTING_BPS = 200; // <=2% creator slice
// Canonical ladder (token-as-currency0 ticks; mirrored by the locker when token sorts
// as currency1). Start FDV ~$4-5K in WETH terms; graduation ~22x above start.
int24 public constant TICK_SPACING = 100;
int24 public constant START_TICK = -204_800;
int24 public constant GRAD_TICK = -173_800;
int24 public constant END_TICK = -143_800;
uint8 public constant CURVE_BUCKETS = 10;
uint8 public constant TAIL_BUCKETS = 2;
struct LaunchParams {
string name;
string symbol;
string metadataURI; // IPFS/CDN pointer; immutable post-launch
bytes32 socialClaimId; // optional: keccak256("x.com/<handle>")
uint16 creatorFeeBps; // curve-phase creator share, 1000-5500
uint16 claimerShareBps; // portion of creator side routed to socialClaimId
address referrer; // launch referrer (interface/bot), earns on this pool forever
bool vestingEnabled; // 2% creator slice, 30d cliff + linear
// creator dev-buy: msg.value is wrapped to WETH and swapped atomically inside this
// transaction — the only guaranteed first fill under strict FCFS sequencing.
// No cap: a dev buy may take up to the entire bonding curve (UI caps input at graduationQuote).
}
IPoolManager public immutable poolManager;
LaunchHook public immutable hook;
PermanentLocker public immutable locker;
FeeRouter public immutable feeRouter;
Registry public immutable registry;
address public immutable weth;
address public immutable tokenImplementation;
address public immutable vestingVault;
uint256 public launchCount;
event TokenCreated(
address indexed token,
address indexed creator,
bytes32 indexed socialClaimId,
PoolId poolId,
uint256 launchBlock,
string metadataURI
);
error SymbolRequired();
constructor(
IPoolManager poolManager_,
LaunchHook hook_,
PermanentLocker locker_,
FeeRouter feeRouter_,
Registry registry_,
address weth_,
address tokenImplementation_,
address vestingVault_
) {
poolManager = poolManager_;
hook = hook_;
locker = locker_;
feeRouter = feeRouter_;
registry = registry_;
weth = weth_;
tokenImplementation = tokenImplementation_;
vestingVault = vestingVault_;
}
function createToken(LaunchParams calldata p) external payable returns (address token, PoolId poolId) {
if (bytes(p.symbol).length == 0) revert SymbolRequired();
address creator = msg.sender;
// deterministic, front-run-resistant token address
uint256 nonce = launchCount++;
bytes32 salt = keccak256(abi.encodePacked(creator, p.symbol, nonce));
token = Clones.cloneDeterministic(tokenImplementation, salt);
uint256 vestAmount =
p.vestingEnabled ? (LaunchToken(token).TOTAL_SUPPLY() * MAX_VESTING_BPS) / 10_000 : 0;
LaunchToken(token).initialize(p.name, p.symbol, address(locker), vestingVault, vestAmount);
if (vestAmount > 0) IVestingVault(vestingVault).register(token, creator, vestAmount);
bool tokenIs0 = token < weth;
PoolKey memory key = PoolKey({
currency0: Currency.wrap(tokenIs0 ? token : weth),
currency1: Currency.wrap(tokenIs0 ? weth : token),
fee: 0, // hook takes the fee; LP fee is zero
tickSpacing: TICK_SPACING,
hooks: IHooks(address(hook))
});
poolId = key.toId();
hook.register(key, token, tokenIs0);
poolManager.initialize(key, TickMath.getSqrtPriceAtTick(tokenIs0 ? START_TICK : -START_TICK));
feeRouter.setPoolConfig(poolId, creator, p.referrer, p.socialClaimId, p.creatorFeeBps, p.claimerShareBps);
// wrap the dev-buy ETH and hand it to the locker for the atomic first fill
if (msg.value > 0) {
IWETH9(weth).deposit{value: msg.value}();
IWETH9(weth).transfer(address(locker), msg.value);
}
locker.seed(
PermanentLocker.SeedParams({
key: key,
token: token,
tokenIs0: tokenIs0,
startTick: START_TICK,
gradTick: GRAD_TICK,
endTick: END_TICK,
curveBuckets: CURVE_BUCKETS,
tailBuckets: TAIL_BUCKETS,
supplyToSeed: LaunchToken(token).TOTAL_SUPPLY() - vestAmount,
initialBuyQuote: msg.value,
buyRecipient: creator
})
);
registry.register(token, creator, poolId, p.socialClaimId, p.metadataURI);
emit TokenCreated(token, creator, p.socialClaimId, poolId, block.number, p.metadataURI);
}
/// @notice Predict the token address for (creator, symbol, nonce) — CREATE2-deterministic,
/// so the address cannot be squatted between announce and deploy, and the SDK can
/// mine vanity addresses off-chain.
function predictToken(address creator, string calldata symbol, uint256 nonce) external view returns (address) {
bytes32 salt = keccak256(abi.encodePacked(creator, symbol, nonce));
return Clones.predictDeterministicAddress(tokenImplementation, salt, address(this));
}
}
interface IVestingVault {
function register(address token, address creator, uint256 amount) external;
}
Chain explorer1630msChain node92ms