RootedLaunchFactory
0xb684a46c6e8258f281f740af1b92aa1925a47e66
Verification
Verified
v0.8.28+commit.7893614a
Type
Contract
15,905 bytes
ABI entries
116
35 read · 19 write
License
none
Contract information
- Address
- 0xb684a46c6e8258f281f740af1b92aa1925a47e66
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.28+commit.7893614a
- Optimization
- Enabled
- Creator
- 0x1a41354C85…Fb5bD5233F
- Creation tx
- 0xa288a81452…eebf8bc4ae
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (35)
MAX_METADATA_URI_LEN() → uint256
MAX_NAME_LEN() → uint256
MAX_ROOTED_BASKET() → uint256
MAX_SYMBOL_LEN() → uint256
ROOTED_CONTRIBUTION_BPS_MAX() → uint16
ROOTED_CONTRIBUTION_BPS_MIN() → uint16
STUCK_RESCUE_DELAY() → uint256
TOTAL_SUPPLY() → uint256
bondingPoolImplementation() → address
configEnabled(uint256) → bool
configs(uint256) → address, uint256, uint256, uint256, uint256, uint16, uint16, uint32, bool
deploymentFinalized() → bool
feeCollector() → address
getLaunchedToken(address) → tuple
isGraduated(address) → bool
lastLaunchAt(address) → uint256
launchCooldown() → uint32
launchPaused() → bool
launchedTokens(address) → address, address, address, address, address, string, uint256, uint256, uint256, bool, bool, address, uint8, uint8, uint16
marketModeOf(address) → uint8
nextConfigId() → uint256
nextLaunchAt(address) → uint256
nextTokenSalt() → uint256
owner() → address
pendingOwner() → address
protocolSink() → address
renounceOwnership()
rootedContributionBps(address) → uint16
rootedHook() → address
rootedLaunchesEnabled() → bool
rootedOrchestrator() → address
rootedPoolId(address) → bytes32
rootedTargetsOf(address) → tuple[]
stuckRescueEta(address) → uint256
tokenImplementation() → address
Events (18)
ConfigEnabledUpdatedDeploymentFinalizedLaunchConfigRegisteredLaunchCooldownUpdatedLaunchPausedUpdatedOwnershipTransferStartedOwnershipTransferredRescuedRootedHookUpdatedRootedLaunchCreatedRootedLaunchesEnabledUpdatedRootedOrchestratorUpdatedRootedTargetConfiguredStuckPoolRescueProposedStuckPoolRescuedTokenGraduatedTokenLaunchedTokenSaltBumped
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "tokenImplementation_",
"type": "address"
},
{
"internalType": "address",
"name": "bondingPoolImplementation_",
"type": "address"
},
{
"internalType": "address",
"name": "protocolSink_",
"type": "address"
},
{
"internalType": "address",
"name": "feeCollector_",
"type": "address"
},
{
"internalType": "address",
"name": "owner_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "AlreadyGraduated",
"type": "error"
},
{
"inputs": [],
"name": "BadMsgValue",
"type": "error"
},
{
"inputs": [],
"name": "ConfigDisabled",
"type": "error"
},
{
"inputs": [],
"name": "DeploymentAlreadyFinalized",
"type": "error"
},
{
"inputs": [],
"name": "DeploymentIncomplete",
"type": "error"
},
{
"inputs": [],
"name": "DeploymentNotFinalized",
"type": "error"
},
{
"inputs": [],
"name": "EmergencyGraduationDisabled",
"type": "error"
},
{
"inputs": [],
"name": "FailedDeployment",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "needed",
"type": "uint256"
}
],
"name": "InsufficientBalance",
"type": "error"
},
{
"inputs": [],
"name": "InvalidConfig",
"type": "error"
},
{
"inputs": [],
"name": "InvalidContributionBps",
"type": "error"
},
{
"inputs": [],
"name": "InvalidRootedBasket",
"type": "error"
},
{
"inputs": [],
"name": "InvalidRootedInfrastructure",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "nextLaunchAt",
"type": "uint256"
}
],
"name": "LaunchCooldownActive",
"type": "error"
},
{
"inputs": [],
"name": "LaunchPaused",
"type": "error"
},
{
"inputs": [],
"name": "MetadataTooLong",
"type": "error"
},
{
"inputs": [],
"name": "NameTooLong",
"type": "error"
},
{
"inputs": [],
"name": "NotBondingPool",
"type": "error"
},
{
"inputs": [],
"name": "NotPendingGraduation",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "OwnableInvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "OwnableUnauthorizedAccount",
"type": "error"
},
{
"inputs": [],
"name": "ReentrancyGuardReentrantCall",
"type": "error"
},
{
"inputs": [],
"name": "RenounceDisabled",
"type": "error"
},
{
"inputs": [],
"name": "RescueFailed",
"type": "error"
},
{
"inputs": [],
"name": "RootedAlreadyBound",
"type": "error"
},
{
"inputs": [],
"name": "RootedFactoryMismatch",
"type": "error"
},
{
"inputs": [],
"name": "RootedHookAlreadySet",
"type": "error"
},
{
"inputs": [],
"name": "RootedLaunchesDisabled",
"type": "error"
},
{
"inputs": [],
"name": "RootedNotConfigured",
"type": "error"
},
{
"inputs": [],
"name": "RootedOrchestratorAlreadySet",
"type": "error"
},
{
"inputs": [],
"name": "RootedOrchestratorNotSet",
"type": "error"
},
{
"inputs": [],
"name": "RootedOrchestratorPaused",
"type": "error"
},
{
"inputs": [],
"name": "RootedRegistrarMismatch",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "SafeERC20FailedOperation",
"type": "error"
},
{
"inputs": [],
"name": "StuckRescueAlreadyProposed",
"type": "error"
},
{
"inputs": [],
"name": "StuckRescueNotProposed",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "readyAt",
"type": "uint256"
}
],
"name": "StuckRescueNotReady",
"type": "error"
},
{
"inputs": [],
"name": "SymbolTooLong",
"type": "error"
},
{
"inputs": [],
"name": "UnknownConfig",
"type": "error"
},
{
"inputs": [],
"name": "UnknownToken",
"type": "error"
},
{
"inputs": [],
"name": "UtilityAllocationsNotSupported",
"type": "error"
},
{
"inputs": [],
"name": "ZeroAddress",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "configId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "ConfigEnabledUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [],
"name": "DeploymentFinalized",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "configId",
"type": "uint256"
}
],
"name": "LaunchConfigRegistered",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint32",
"name": "cooldownSecs",
"type": "uint32"
}
],
"name": "LaunchCooldownUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bool",
"name": "paused",
"type": "bool"
}
],
"name": "LaunchPausedUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferStarted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "asset",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "Rescued",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "hook",
"type": "address"
}
],
"name": "RootedHookUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "deployer",
"type": "address"
},
{
"indexed": false,
"internalType": "uint16",
"name": "contributionBps",
"type": "uint16"
},
{
"indexed": false,
"internalType": "uint256",
"name": "rootTargetCount",
"type": "uint256"
}
],
"name": "RootedLaunchCreated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "RootedLaunchesEnabledUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "orchestrator",
"type": "address"
}
],
"name": "RootedOrchestratorUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "asset",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "targetIndex",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint16",
"name": "targetWeightBps",
"type": "uint16"
},
{
"indexed": false,
"internalType": "bool",
"name": "priced",
"type": "bool"
}
],
"name": "RootedTargetConfigured",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "eta",
"type": "uint256"
}
],
"name": "StuckPoolRescueProposed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "recovery",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "ethAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "tokenAmount",
"type": "uint256"
}
],
"name": "StuckPoolRescued",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "uniswapPool",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "positionId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "ethMigrated",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "tokensMigrated",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "tokensBurned",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "ethRefunded",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint24",
"name": "feeUsed",
"type": "uint24"
}
],
"name": "TokenGraduated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "deployer",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "bondingPool",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "feeRecipient",
"type": "address"
},
{
"indexed": false,
"internalType": "string",
"name": "metadataURI",
"type": "string"
},
{
"indexed": false,
"internalType": "uint256",
"name": "configId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "graduationTarget",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "initialBuyAmount",
"type": "uint256"
}
],
"name": "TokenLaunched",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "previousSalt",
"type": "uint256"
},
{
"indexed": true,
"internalType": "uint256",
"name": "newSalt",
"type": "uint256"
}
],
"name": "TokenSaltBumped",
"type": "event"
},
{
"inputs": [],
"name": "MAX_METADATA_URI_LEN",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_NAME_LEN",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_ROOTED_BASKET",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_SYMBOL_LEN",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "ROOTED_CONTRIBUTION_BPS_MAX",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "ROOTED_CONTRIBUTION_BPS_MIN",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "STUCK_RESCUE_DELAY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TOTAL_SUPPLY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "acceptOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "bindRootedOrchestrator",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "bondingPoolImplementation",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "newSalt",
"type": "uint256"
}
],
"name": "bumpTokenSalt",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "completeEmergencyGraduation",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "ethAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tokenAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "virtualEth",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "virtualToken",
"type": "uint256"
}
],
"name": "completeGraduation",
"outputs": [
{
"internalType": "address",
"name": "liquidityVault",
"type": "address"
},
{
"internalType": "uint256",
"name": "positionId",
"type": "uint256"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "configEnabled",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "configs",
"outputs": [
{
"internalType": "address",
"name": "pairToken",
"type": "address"
},
{
"internalType": "uint256",
"name": "graduationTarget",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "virtualEth",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "virtualToken",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tokensForSale",
"type": "uint256"
},
{
"internalType": "uint16",
"name": "maxWalletBps",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "maxTxBps",
"type": "uint16"
},
{
"internalType": "uint32",
"name": "restrictionSecs",
"type": "uint32"
},
{
"internalType": "bool",
"name": "exists",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "deploymentFinalized",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address payable",
"name": "recovery",
"type": "address"
}
],
"name": "executeStuckPoolRescue",
"outputs": [
{
"internalType": "uint256",
"name": "ethAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tokenAmount",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "feeCollector",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "finalizeDeployment",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "getLaunchedToken",
"outputs": [
{
"components": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "deployer",
"type": "address"
},
{
"internalType": "address",
"name": "bondingPool",
"type": "address"
},
{
"internalType": "address",
"name": "uniswapPool",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipient",
"type": "address"
},
{
"internalType": "string",
"name": "metadataURI",
"type": "string"
},
{
"internalType": "uint256",
"name": "configId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "positionId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "graduationTarget",
"type": "uint256"
},
{
"internalType": "bool",
"name": "graduated",
"type": "bool"
},
{
"internalType": "bool",
"name": "exists",
"type": "bool"
},
{
"internalType": "address",
"name": "allocationVault",
"type": "address"
},
{
"internalType": "enum UtilityTypes.MarketMode",
"name": "marketMode",
"type": "uint8"
},
{
"internalType": "enum UtilityTypes.AllocationMode",
"name": "allocationMode",
"type": "uint8"
},
{
"internalType": "uint16",
"name": "utilityAllocationBps",
"type": "uint16"
}
],
"internalType": "struct RootedLaunchFactory.LaunchedToken",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "isGraduated",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "lastLaunchAt",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "launchCooldown",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "launchPaused",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"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": "address",
"name": "feeRecipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "configId",
"type": "uint256"
}
],
"internalType": "struct UtilityTypes.LaunchParams",
"name": "params",
"type": "tuple"
},
{
"components": [
{
"internalType": "address",
"name": "beneficiary",
"type": "address"
},
{
"internalType": "uint16",
"name": "allocationBps",
"type": "uint16"
},
{
"internalType": "enum UtilityTypes.AllocationRole",
"name": "role",
"type": "uint8"
}
],
"internalType": "struct UtilityTypes.AllocationInput[]",
"name": "allocations",
"type": "tuple[]"
},
{
"internalType": "uint16",
"name": "contributionBps",
"type": "uint16"
},
{
"components": [
{
"internalType": "address",
"name": "asset",
"type": "address"
},
{
"internalType": "uint16",
"name": "targetWeightBps",
"type": "uint16"
},
{
"internalType": "bool",
"name": "priced",
"type": "bool"
}
],
"internalType": "struct UtilityTypes.RootTargetInput[]",
"name": "rootTargets",
"type": "tuple[]"
}
],
"name": "launchRooted",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "bondingPool",
"type": "address"
},
{
"internalType": "address",
"name": "allocationVault",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "launchedTokens",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "deployer",
"type": "address"
},
{
"internalType": "address",
"name": "bondingPool",
"type": "address"
},
{
"internalType": "address",
"name": "uniswapPool",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipient",
"type": "address"
},
{
"internalType": "string",
"name": "metadataURI",
"type": "string"
},
{
"internalType": "uint256",
"name": "configId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "positionId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "graduationTarget",
"type": "uint256"
},
{
"internalType": "bool",
"name": "graduated",
"type": "bool"
},
{
"internalType": "bool",
"name": "exists",
"type": "bool"
},
{
"internalType": "address",
"name": "allocationVault",
"type": "address"
},
{
"internalType": "enum UtilityTypes.MarketMode",
"name": "marketMode",
"type": "uint8"
},
{
"internalType": "enum UtilityTypes.AllocationMode",
"name": "allocationMode",
"type": "uint8"
},
{
"internalType": "uint16",
"name": "utilityAllocationBps",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "marketModeOf",
"outputs": [
{
"internalType": "enum UtilityTypes.MarketMode",
"name": "",
"type": "uint8"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "nextConfigId",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "deployer",
"type": "address"
}
],
"name": "nextLaunchAt",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "nextTokenSalt",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingOwner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "proposeStuckPoolRescue",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "protocolSink",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "pairToken",
"type": "address"
},
{
"internalType": "uint256",
"name": "graduationTarget",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "virtualEth",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "virtualToken",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tokensForSale",
"type": "uint256"
},
{
"internalType": "uint16",
"name": "maxWalletBps",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "maxTxBps",
"type": "uint16"
},
{
"internalType": "uint32",
"name": "restrictionSecs",
"type": "uint32"
},
{
"internalType": "bool",
"name": "exists",
"type": "bool"
}
],
"internalType": "struct RootedLaunchFactory.LaunchConfig",
"name": "cfg",
"type": "tuple"
}
],
"name": "registerConfig",
"outputs": [
{
"internalType": "uint256",
"name": "configId",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "registerDefaultConfig",
"outputs": [
{
"internalType": "uint256",
"name": "configId",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "asset",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "rescue",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "rootedContributionBps",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "rootedHook",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "rootedLaunchesEnabled",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "rootedOrchestrator",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "rootedPoolId",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "rootedTargetsOf",
"outputs": [
{
"components": [
{
"internalType": "address",
"name": "asset",
"type": "address"
},
{
"internalType": "uint16",
"name": "targetWeightBps",
"type": "uint16"
},
{
"internalType": "bool",
"name": "priced",
"type": "bool"
}
],
"internalType": "struct UtilityTypes.RootTargetInput[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "configId",
"type": "uint256"
},
{
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "setConfigEnabled",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint32",
"name": "cooldownSecs",
"type": "uint32"
}
],
"name": "setLaunchCooldown",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "paused",
"type": "bool"
}
],
"name": "setLaunchPaused",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "hook_",
"type": "address"
}
],
"name": "setRootedHook",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "setRootedLaunchesEnabled",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "orchestrator_",
"type": "address"
}
],
"name": "setRootedOrchestrator",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "stuckRescueEta",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tokenImplementation",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {Config} from "./Config.sol";
import {ILaunchToken} from "./interfaces/ILaunchToken.sol";
import {IBondingPool} from "./interfaces/IBondingPool.sol";
import {ILaunchFactoryGraduation} from "./interfaces/ILaunchFactoryGraduation.sol";
import {BondingPool} from "./BondingPool.sol";
import {UtilityTypes} from "./utility/UtilityTypes.sol";
import {IWETH} from "./vendor/IWETH.sol";
/// @dev Minimal Rooted orchestrator surface (no direct rooted import — solc isolation).
interface IRootedGraduationOrchestratorLean {
struct LaunchGraduateParams {
address launchToken;
address quoteToken;
address hook;
address creator;
address feeCollector;
uint256 reserveQuote;
uint256 tokenInventory;
uint256 virtualEth;
uint256 virtualToken;
uint16 contributionBps;
}
function launchesPaused() external view returns (bool);
function launchFactory() external view returns (address);
function isLaunchFactory(address factory) external view returns (bool);
function poolManager() external view returns (address);
function weth() external view returns (address);
function graduateFromFactory(LaunchGraduateParams calldata p)
external
returns (bytes32 poolId, address capitalVault, address liquidityVault);
}
interface IRootedHookLean {
function registrar() external view returns (address);
function poolManager() external view returns (address);
}
/// @notice Lean Rooted-only factory: bonding curve → RootedGraduationOrchestrator.
/// @dev Separate from live Wild LaunchFactory (EIP-170). No Utility / Uniswap V3 graduation.
contract RootedLaunchFactory is Ownable2Step, ReentrancyGuard, ILaunchFactoryGraduation {
using Clones for address;
using SafeERC20 for IERC20;
error ZeroAddress();
error InvalidConfig();
error UnknownConfig();
error MetadataTooLong();
error LaunchCooldownActive(uint256 nextLaunchAt);
error UnknownToken();
error AlreadyGraduated();
error NotBondingPool();
error BadMsgValue();
error RescueFailed();
error DeploymentNotFinalized();
error DeploymentAlreadyFinalized();
error DeploymentIncomplete();
error NameTooLong();
error SymbolTooLong();
error RenounceDisabled();
error LaunchPaused();
error ConfigDisabled();
error RootedOrchestratorAlreadySet();
error RootedOrchestratorNotSet();
error RootedAlreadyBound();
error RootedLaunchesDisabled();
error RootedNotConfigured();
error RootedOrchestratorPaused();
error InvalidRootedBasket();
error InvalidContributionBps();
error RootedHookAlreadySet();
error InvalidRootedInfrastructure();
error RootedFactoryMismatch();
error RootedRegistrarMismatch();
error UtilityAllocationsNotSupported();
error EmergencyGraduationDisabled();
error StuckRescueNotProposed();
error StuckRescueNotReady(uint256 readyAt);
error StuckRescueAlreadyProposed();
error NotPendingGraduation();
event TokenLaunched(
address indexed token,
address indexed deployer,
address indexed bondingPool,
address feeRecipient,
string metadataURI,
uint256 configId,
uint256 graduationTarget,
uint256 initialBuyAmount
);
event TokenGraduated(
address indexed token,
address indexed uniswapPool,
uint256 positionId,
uint256 ethMigrated,
uint256 tokensMigrated,
uint256 tokensBurned,
uint256 ethRefunded,
uint24 feeUsed
);
event LaunchConfigRegistered(uint256 indexed configId);
event LaunchCooldownUpdated(uint32 cooldownSecs);
event DeploymentFinalized();
event Rescued(address indexed asset, address indexed to, uint256 amount);
event TokenSaltBumped(uint256 indexed previousSalt, uint256 indexed newSalt);
event LaunchPausedUpdated(bool paused);
event ConfigEnabledUpdated(uint256 indexed configId, bool enabled);
event RootedOrchestratorUpdated(address indexed orchestrator);
event RootedHookUpdated(address indexed hook);
event RootedLaunchesEnabledUpdated(bool enabled);
event RootedLaunchCreated(
address indexed token,
address indexed deployer,
uint16 contributionBps,
uint256 rootTargetCount
);
event RootedTargetConfigured(
address indexed token,
address indexed asset,
uint256 targetIndex,
uint16 targetWeightBps,
bool priced
);
event StuckPoolRescueProposed(address indexed token, uint256 eta);
event StuckPoolRescued(
address indexed token,
address indexed recovery,
uint256 ethAmount,
uint256 tokenAmount
);
uint256 public constant MAX_ROOTED_BASKET = 10;
/// @notice Minimum delay between propose and execute for stuck-graduation abort.
uint256 public constant STUCK_RESCUE_DELAY = 3 days;
uint16 public constant ROOTED_CONTRIBUTION_BPS_MIN = 100;
uint16 public constant ROOTED_CONTRIBUTION_BPS_MAX = 500;
uint256 public constant TOTAL_SUPPLY = Config.TOTAL_SUPPLY;
uint256 public constant MAX_METADATA_URI_LEN = 2048;
uint256 public constant MAX_NAME_LEN = 64;
uint256 public constant MAX_SYMBOL_LEN = 16;
struct LaunchConfig {
address pairToken;
uint256 graduationTarget;
uint256 virtualEth;
uint256 virtualToken;
uint256 tokensForSale;
uint16 maxWalletBps;
uint16 maxTxBps;
uint32 restrictionSecs;
bool exists;
}
/// @dev Field layout matches Wild LaunchFactory for shared FE ABI decoding.
struct LaunchedToken {
address token;
address deployer;
address bondingPool;
address uniswapPool;
address feeRecipient;
string metadataURI;
uint256 configId;
uint256 positionId;
uint256 graduationTarget;
bool graduated;
bool exists;
address allocationVault;
UtilityTypes.MarketMode marketMode;
UtilityTypes.AllocationMode allocationMode;
uint16 utilityAllocationBps;
}
address public immutable tokenImplementation;
address public immutable bondingPoolImplementation;
address public immutable protocolSink;
/// @notice Passed to Rooted liquidity vault as feeCollector (Safe or protocol sink).
address public immutable feeCollector;
address public rootedOrchestrator;
address public rootedHook;
bool public rootedLaunchesEnabled;
mapping(address => uint16) public rootedContributionBps;
mapping(address => UtilityTypes.RootTargetInput[]) private _rootedTargets;
mapping(address => bytes32) public rootedPoolId;
uint256 public nextConfigId;
mapping(uint256 => LaunchConfig) public configs;
mapping(address => LaunchedToken) public launchedTokens;
uint32 public launchCooldown = Config.DEFAULT_LAUNCH_COOLDOWN;
mapping(address => uint256) public lastLaunchAt;
bool public deploymentFinalized;
bool public launchPaused;
mapping(uint256 => bool) public configEnabled;
uint256 public nextTokenSalt;
/// @dev Earliest timestamp owner may execute stuck-pool rescue (0 = not proposed).
mapping(address => uint256) public stuckRescueEta;
constructor(
address tokenImplementation_,
address bondingPoolImplementation_,
address protocolSink_,
address feeCollector_,
address owner_
) Ownable(owner_) {
if (
tokenImplementation_ == address(0) || bondingPoolImplementation_ == address(0)
|| protocolSink_ == address(0) || feeCollector_ == address(0)
) {
revert ZeroAddress();
}
tokenImplementation = tokenImplementation_;
bondingPoolImplementation = bondingPoolImplementation_;
protocolSink = protocolSink_;
feeCollector = feeCollector_;
}
function renounceOwnership() public pure override {
revert RenounceDisabled();
}
function finalizeDeployment() external onlyOwner {
if (deploymentFinalized) revert DeploymentAlreadyFinalized();
if (rootedOrchestrator == address(0) || rootedHook == address(0) || nextConfigId == 0) {
revert DeploymentIncomplete();
}
_validateRootedInfrastructure();
for (uint256 i = 0; i < nextConfigId; i++) {
if (configs[i].exists) _validateLaunchConfig(configs[i]);
}
deploymentFinalized = true;
emit DeploymentFinalized();
}
function setLaunchPaused(bool paused) external onlyOwner {
launchPaused = paused;
emit LaunchPausedUpdated(paused);
}
function setConfigEnabled(uint256 configId, bool enabled) external onlyOwner {
if (!configs[configId].exists) revert UnknownConfig();
configEnabled[configId] = enabled;
emit ConfigEnabledUpdated(configId, enabled);
}
function setLaunchCooldown(uint32 cooldownSecs) external onlyOwner {
launchCooldown = cooldownSecs;
emit LaunchCooldownUpdated(cooldownSecs);
}
function bumpTokenSalt(uint256 newSalt) external onlyOwner {
uint256 previous = nextTokenSalt;
nextTokenSalt = newSalt;
emit TokenSaltBumped(previous, newSalt);
}
function setRootedOrchestrator(address orchestrator_) external onlyOwner {
if (orchestrator_ == address(0)) revert ZeroAddress();
if (rootedOrchestrator != address(0)) revert RootedOrchestratorAlreadySet();
if (orchestrator_.code.length == 0) revert InvalidRootedInfrastructure();
if (!IRootedGraduationOrchestratorLean(orchestrator_).isLaunchFactory(address(this))) {
revert RootedFactoryMismatch();
}
rootedOrchestrator = orchestrator_;
emit RootedOrchestratorUpdated(orchestrator_);
}
function setRootedHook(address hook_) external onlyOwner {
if (hook_ == address(0)) revert ZeroAddress();
if (rootedHook != address(0)) revert RootedHookAlreadySet();
if (hook_.code.length == 0) revert InvalidRootedInfrastructure();
address orch = rootedOrchestrator;
if (orch == address(0)) revert RootedNotConfigured();
if (IRootedHookLean(hook_).registrar() != orch) revert RootedRegistrarMismatch();
rootedHook = hook_;
emit RootedHookUpdated(hook_);
}
function setRootedLaunchesEnabled(bool enabled) external onlyOwner {
if (enabled) _validateRootedInfrastructure();
rootedLaunchesEnabled = enabled;
emit RootedLaunchesEnabledUpdated(enabled);
}
function bindRootedOrchestrator(address token) external onlyOwner {
if (rootedOrchestrator == address(0)) revert RootedOrchestratorNotSet();
LaunchedToken storage info = launchedTokens[token];
if (!info.exists) revert UnknownToken();
if (ILaunchToken(token).rootedOrchestrator() != address(0)) revert RootedAlreadyBound();
if (ILaunchToken(token).marketMode() != uint8(UtilityTypes.MarketMode.ROOTED)) {
ILaunchToken(token).setMarketMode(uint8(UtilityTypes.MarketMode.ROOTED));
}
ILaunchToken(token).setRootedOrchestrator(rootedOrchestrator);
}
function rootedTargetsOf(address token)
external
view
returns (UtilityTypes.RootTargetInput[] memory)
{
return _rootedTargets[token];
}
function marketModeOf(address) external pure returns (UtilityTypes.MarketMode) {
return UtilityTypes.MarketMode.ROOTED;
}
function isGraduated(address token) external view returns (bool) {
return launchedTokens[token].graduated;
}
function nextLaunchAt(address deployer) public view returns (uint256) {
uint256 last = lastLaunchAt[deployer];
if (last == 0 || launchCooldown == 0) return 0;
uint256 readyAt = last + launchCooldown;
return block.timestamp >= readyAt ? 0 : readyAt;
}
/// @notice Register the canonical mainnet launch config (pairToken = Config.WETH, 20 ETH target).
/// @dev Mainnet-only. On testnet, Safe must `registerConfig` with `pairToken = orchestrator.weth()`
/// and a cheap `graduationTarget` (≥ TESTNET_MIN_GRADUATION_TARGET) so LaunchToken.pairToken()
/// stays accurate for tooling/indexers.
function registerDefaultConfig() external onlyOwner returns (uint256 configId) {
if (block.chainid != Config.CHAIN_ID) revert InvalidConfig();
return registerConfig(
LaunchConfig({
pairToken: Config.WETH,
graduationTarget: Config.DEFAULT_GRADUATION_TARGET,
virtualEth: Config.DEFAULT_VIRTUAL_ETH,
virtualToken: Config.DEFAULT_VIRTUAL_TOKEN,
tokensForSale: Config.DEFAULT_TOKENS_FOR_SALE,
maxWalletBps: Config.DEFAULT_MAX_WALLET_BPS,
maxTxBps: Config.DEFAULT_MAX_TX_BPS,
restrictionSecs: Config.DEFAULT_RESTRICTION_SECS,
exists: true
})
);
}
function registerConfig(LaunchConfig memory cfg) public onlyOwner returns (uint256 configId) {
_validateLaunchConfig(cfg);
cfg.exists = true;
configId = nextConfigId++;
configs[configId] = cfg;
configEnabled[configId] = true;
emit LaunchConfigRegistered(configId);
}
function getLaunchedToken(address token) external view returns (LaunchedToken memory) {
return launchedTokens[token];
}
/// @notice Rooted create: bonding curve + contribution + basket intent (no Utility).
function launchRooted(
UtilityTypes.LaunchParams calldata params,
UtilityTypes.AllocationInput[] calldata allocations,
uint16 contributionBps,
UtilityTypes.RootTargetInput[] calldata rootTargets
) external payable nonReentrant returns (address token, address bondingPool, address allocationVault) {
if (!deploymentFinalized) revert DeploymentNotFinalized();
if (launchPaused) revert LaunchPaused();
if (!rootedLaunchesEnabled) revert RootedLaunchesDisabled();
_validateRootedInfrastructure();
if (IRootedGraduationOrchestratorLean(rootedOrchestrator).launchesPaused()) {
revert RootedOrchestratorPaused();
}
if (allocations.length != 0) revert UtilityAllocationsNotSupported();
_validateRootedIntent(contributionBps, rootTargets);
uint256 readyAt = nextLaunchAt(msg.sender);
if (readyAt != 0) revert LaunchCooldownActive(readyAt);
LaunchConfig memory cfg = configs[params.configId];
if (!cfg.exists) revert UnknownConfig();
if (!configEnabled[params.configId]) revert ConfigDisabled();
if (bytes(params.name).length == 0 || bytes(params.name).length > MAX_NAME_LEN) {
revert NameTooLong();
}
if (bytes(params.symbol).length == 0 || bytes(params.symbol).length > MAX_SYMBOL_LEN) {
revert SymbolTooLong();
}
if (bytes(params.metadataUri).length > MAX_METADATA_URI_LEN) revert MetadataTooLong();
address feeRecipient = params.feeRecipient == address(0) ? msg.sender : params.feeRecipient;
uint256 initialBuyAmount = msg.value;
uint256 saltEntropy = nextTokenSalt;
bytes32 salt = keccak256(abi.encode(msg.sender, saltEntropy, block.number));
nextTokenSalt = saltEntropy + 1;
token = tokenImplementation.cloneDeterministic(salt);
bondingPool = bondingPoolImplementation.clone();
allocationVault = address(0);
_initializeLaunchPair(
token, bondingPool, params.name, params.symbol, feeRecipient, cfg
);
launchedTokens[token] = LaunchedToken({
token: token,
deployer: msg.sender,
bondingPool: bondingPool,
uniswapPool: address(0),
feeRecipient: feeRecipient,
metadataURI: params.metadataUri,
configId: params.configId,
positionId: 0,
graduationTarget: cfg.graduationTarget,
graduated: false,
exists: true,
allocationVault: address(0),
marketMode: UtilityTypes.MarketMode.ROOTED,
allocationMode: UtilityTypes.AllocationMode.FAIR,
utilityAllocationBps: 0
});
rootedContributionBps[token] = contributionBps;
for (uint256 i; i < rootTargets.length; ++i) {
_rootedTargets[token].push(rootTargets[i]);
}
lastLaunchAt[msg.sender] = block.timestamp;
if (initialBuyAmount > 0) {
_executeInitialBuy(bondingPool, cfg.graduationTarget, initialBuyAmount);
}
emit TokenLaunched(
token,
msg.sender,
bondingPool,
feeRecipient,
params.metadataUri,
params.configId,
cfg.graduationTarget,
initialBuyAmount
);
emit RootedLaunchCreated(token, msg.sender, contributionBps, rootTargets.length);
for (uint256 i; i < rootTargets.length; ++i) {
UtilityTypes.RootTargetInput calldata target = rootTargets[i];
emit RootedTargetConfigured(
token, target.asset, i, target.targetWeightBps, target.priced
);
}
}
function completeGraduation(
address token,
uint256 ethAmount,
uint256 tokenAmount,
uint256 virtualEth,
uint256 virtualToken
) external payable override nonReentrant returns (address liquidityVault, uint256 positionId) {
return _completeRootedGraduation(token, ethAmount, tokenAmount, virtualEth, virtualToken);
}
function completeEmergencyGraduation(
address,
uint256,
uint256,
uint256,
uint256
) external payable override returns (address, uint256) {
// V3 emergency adapter path is intentionally disabled for Rooted.
// Use proposeStuckPoolRescue / executeStuckPoolRescue instead.
revert EmergencyGraduationDisabled();
}
/// @notice Start timelock to abort a Rooted pool stuck in `graduationPending`.
function proposeStuckPoolRescue(address token) external onlyOwner {
LaunchedToken storage info = launchedTokens[token];
if (!info.exists) revert UnknownToken();
if (info.graduated) revert AlreadyGraduated();
BondingPool pool = BondingPool(payable(info.bondingPool));
if (!pool.graduationPending() || pool.graduated()) revert NotPendingGraduation();
if (stuckRescueEta[token] != 0) revert StuckRescueAlreadyProposed();
uint256 eta = block.timestamp + STUCK_RESCUE_DELAY;
stuckRescueEta[token] = eta;
emit StuckPoolRescueProposed(token, eta);
}
/// @notice After delay, pull stuck ETH + tokens to `recovery` for ops-managed refunds.
function executeStuckPoolRescue(address token, address payable recovery)
external
onlyOwner
returns (uint256 ethAmount, uint256 tokenAmount)
{
if (recovery == address(0)) revert ZeroAddress();
LaunchedToken storage info = launchedTokens[token];
if (!info.exists) revert UnknownToken();
if (info.graduated) revert AlreadyGraduated();
uint256 eta = stuckRescueEta[token];
if (eta == 0) revert StuckRescueNotProposed();
if (block.timestamp < eta) revert StuckRescueNotReady(eta);
BondingPool pool = BondingPool(payable(info.bondingPool));
if (!pool.graduationPending() || pool.graduated()) revert NotPendingGraduation();
(ethAmount, tokenAmount) = pool.factoryAbortPendingGraduation(recovery, recovery);
info.graduated = true;
info.uniswapPool = address(0);
delete stuckRescueEta[token];
emit StuckPoolRescued(token, recovery, ethAmount, tokenAmount);
}
function rescue(address asset, address to, uint256 amount) external onlyOwner {
if (to == address(0)) revert ZeroAddress();
if (asset == address(0)) {
(bool ok,) = to.call{value: amount}("");
if (!ok) revert RescueFailed();
} else {
IERC20(asset).safeTransfer(to, amount);
}
emit Rescued(asset, to, amount);
}
function _completeRootedGraduation(
address token,
uint256 ethAmount,
uint256 tokenAmount,
uint256 virtualEth,
uint256 virtualToken
) private returns (address liquidityVault, uint256 positionId) {
LaunchedToken storage info = launchedTokens[token];
if (!info.exists) revert UnknownToken();
if (info.graduated) revert AlreadyGraduated();
if (msg.sender != info.bondingPool) revert NotBondingPool();
if (msg.value != ethAmount) revert BadMsgValue();
address orch = rootedOrchestrator;
address hook = rootedHook;
if (orch == address(0) || hook == address(0)) revert RootedNotConfigured();
if (IRootedGraduationOrchestratorLean(orch).launchesPaused()) {
revert RootedOrchestratorPaused();
}
uint16 contributionBps = rootedContributionBps[token];
if (
contributionBps < ROOTED_CONTRIBUTION_BPS_MIN
|| contributionBps > ROOTED_CONTRIBUTION_BPS_MAX
) {
revert InvalidContributionBps();
}
address weth = IRootedGraduationOrchestratorLean(orch).weth();
IERC20(token).safeTransferFrom(msg.sender, address(this), tokenAmount);
IWETH(weth).deposit{value: ethAmount}();
IERC20(weth).forceApprove(orch, ethAmount);
IERC20(token).forceApprove(orch, tokenAmount);
bytes32 poolId;
(poolId,, liquidityVault) = IRootedGraduationOrchestratorLean(orch).graduateFromFactory(
IRootedGraduationOrchestratorLean.LaunchGraduateParams({
launchToken: token,
quoteToken: weth,
hook: hook,
creator: info.deployer,
feeCollector: feeCollector,
reserveQuote: ethAmount,
tokenInventory: tokenAmount,
virtualEth: virtualEth,
virtualToken: virtualToken,
contributionBps: contributionBps
})
);
IERC20(weth).forceApprove(orch, 0);
IERC20(token).forceApprove(orch, 0);
rootedPoolId[token] = poolId;
info.uniswapPool = liquidityVault;
info.positionId = 0;
info.graduated = true;
positionId = 0;
emit TokenGraduated(token, liquidityVault, 0, ethAmount, tokenAmount, 0, 0, 0);
}
function _validateRootedIntent(
uint16 contributionBps,
UtilityTypes.RootTargetInput[] calldata rootTargets
) private pure {
if (
contributionBps < ROOTED_CONTRIBUTION_BPS_MIN
|| contributionBps > ROOTED_CONTRIBUTION_BPS_MAX
) {
revert InvalidContributionBps();
}
uint256 n = rootTargets.length;
if (n == 0 || n > MAX_ROOTED_BASKET) revert InvalidRootedBasket();
uint256 sum;
for (uint256 i; i < n; ++i) {
address asset = rootTargets[i].asset;
if (asset == address(0)) revert InvalidRootedBasket();
if (rootTargets[i].targetWeightBps == 0) revert InvalidRootedBasket();
sum += rootTargets[i].targetWeightBps;
for (uint256 j; j < i; ++j) {
if (rootTargets[j].asset == asset) revert InvalidRootedBasket();
}
}
if (sum != 10_000) revert InvalidRootedBasket();
}
function _executeInitialBuy(
address bondingPool,
uint256 graduationTarget,
uint256 initialBuyAmount
) private {
uint256 balanceBefore = address(this).balance - initialBuyAmount;
uint256 ethIn = _netEthInForBuy(initialBuyAmount, graduationTarget);
uint256 expected = BondingPool(payable(bondingPool)).getTokensOut(ethIn);
uint256 forSale = BondingPool(payable(bondingPool)).tokensForSale();
if (expected > forSale) expected = forSale;
uint256 minOut = (expected * 9_900) / 10_000;
IBondingPool(bondingPool).buy{value: initialBuyAmount}(msg.sender, minOut);
uint256 refund = address(this).balance - balanceBefore;
if (refund > 0) {
(bool ok,) = msg.sender.call{value: refund}("");
if (!ok) revert RescueFailed();
}
}
function _validateRootedInfrastructure() private view {
address orch = rootedOrchestrator;
address hook = rootedHook;
if (orch == address(0) || hook == address(0)) revert RootedNotConfigured();
if (orch.code.length == 0 || hook.code.length == 0) {
revert InvalidRootedInfrastructure();
}
if (!IRootedGraduationOrchestratorLean(orch).isLaunchFactory(address(this))) {
revert RootedFactoryMismatch();
}
if (IRootedHookLean(hook).registrar() != orch) revert RootedRegistrarMismatch();
if (
IRootedHookLean(hook).poolManager()
!= IRootedGraduationOrchestratorLean(orch).poolManager()
) {
revert InvalidRootedInfrastructure();
}
}
function _initializeLaunchPair(
address token,
address bondingPool,
string memory name_,
string memory symbol_,
address feeRecipient,
LaunchConfig memory cfg
) private {
uint256 maxWallet = (TOTAL_SUPPLY * cfg.maxWalletBps) / 10_000;
uint256 maxTx = (TOTAL_SUPPLY * cfg.maxTxBps) / 10_000;
ILaunchToken(token).initialize(
name_,
symbol_,
address(this),
bondingPool,
cfg.pairToken,
Config.DEFAULT_POOL_FEE,
feeRecipient,
maxWallet,
maxTx,
0,
TOTAL_SUPPLY
);
ILaunchToken(token).setBondingPool(bondingPool);
IBondingPool(bondingPool).initialize(
token,
address(this),
msg.sender,
feeRecipient,
protocolSink,
cfg.graduationTarget,
cfg.virtualEth,
cfg.virtualToken,
cfg.tokensForSale
);
ILaunchToken(token).setMarketMode(uint8(UtilityTypes.MarketMode.ROOTED));
ILaunchToken(token).setRootedOrchestrator(rootedOrchestrator);
}
function _validateLaunchConfig(LaunchConfig memory cfg) private view {
if (cfg.pairToken == address(0)) revert ZeroAddress();
if (cfg.graduationTarget == 0) revert InvalidConfig();
if (cfg.virtualEth == 0 || cfg.virtualToken == 0 || cfg.tokensForSale == 0) {
revert InvalidConfig();
}
if (cfg.virtualEth < Config.MIN_VIRTUAL_ETH) revert InvalidConfig();
if (cfg.virtualToken < Config.MIN_VIRTUAL_TOKEN) revert InvalidConfig();
uint256 minGraduationTarget = block.chainid == Config.TESTNET_CHAIN_ID
? Config.TESTNET_MIN_GRADUATION_TARGET
: Config.MIN_GRADUATION_TARGET;
if (cfg.graduationTarget < minGraduationTarget) revert InvalidConfig();
if (cfg.tokensForSale >= TOTAL_SUPPLY) revert InvalidConfig();
if (cfg.tokensForSale >= cfg.virtualToken) revert InvalidConfig();
{
uint256 terminalVe = cfg.virtualEth + cfg.graduationTarget;
uint256 tokensSoldAtTarget =
Math.mulDiv(cfg.graduationTarget, cfg.virtualToken, terminalVe);
if (tokensSoldAtTarget > cfg.tokensForSale) revert InvalidConfig();
}
if (cfg.maxWalletBps == 0 || cfg.maxWalletBps > 10_000) revert InvalidConfig();
if (cfg.maxTxBps == 0 || cfg.maxTxBps > 10_000) revert InvalidConfig();
if (cfg.restrictionSecs == 0) revert InvalidConfig();
}
function _netEthInForBuy(uint256 msgValue, uint256 room) private pure returns (uint256 ethIn) {
uint256 feeEth = (msgValue * Config.PRE_GRAD_TRADE_FEE_BPS) / 10_000;
ethIn = msgValue - feeEth;
if (ethIn > room) {
ethIn = room;
feeEth = (ethIn * Config.PRE_GRAD_TRADE_FEE_BPS)
/ (10_000 - Config.PRE_GRAD_TRADE_FEE_BPS);
uint256 used = ethIn + feeEth;
if (used > msgValue) {
feeEth = (msgValue * Config.PRE_GRAD_TRADE_FEE_BPS) / 10_000;
ethIn = msgValue - feeEth;
if (ethIn > room) ethIn = room;
}
}
}
receive() external payable {}
}
Chain explorer7645msChain node101ms