Everything a developer needs to understand, integrate with, or extend the Core rail. The system is a two-chain pipeline: trustless escrow on HyperEVM (Solidity), token creation on HyperCore (off-chain signed actions), stitched together by a resumable Python relayer and verified back on-chain through read precompiles.
01System map
HYPEREVM (chain 999) OFF-CHAIN HYPERCORE (L1) ┌─────────────────────────┐ ┌───────────────────────────┐ ┌──────────────────────────────┐ │ LaunchVaultFactory.sol │ │ relayer/launch_relayer.py │ │ /exchange (EIP-712 signed) │ │ EIP-1167 clones, policy │ │ status · plan · │ │ spotDeploy actions │ │ │ creates │ │ execute · prove │ │ (registerToken2 … HIP-2) │ │ ▼ │ │ │ │ │ │ LaunchVault.sol ◄───────┼────┤ reads entitlements(), │ │ /info │ │ commit / refund │ │ writes submitDeployProof ├───►│ spotDeployState, spotMeta, │ │ points & entitlements │ │ │ │ spotClearinghouseState │ │ proof → challenge → │ │ key = OPERATOR │ │ │ │ reimburse XOR refund │ │ (= HIP-1 deployer of │ │ token identity = INDEX, │ │ ▲ │ │ record, permanent │ │ deploy slot = 31h Dutch │ │ │ staticcall │ │ fee-share recipient) │ │ auction in HYPE │ │ CoreReader.sol ──────────┼────┼───────────────────────────┼───►│ read precompiles 0x0800-0x0813│ │ raw-calldata reads │ └───────────────────────────┘ │ tokenInfo @ 0x…080C │ └─────────────────────────┘ │ spotBalance @ 0x…0801 │ frontend: hybrid.js + ethers v6 python: web3 + hl-sdk └──────────────────────────────┘
02The constraint that shapes everything
0x…3333, EVM→Core
write bridge) exposes action IDs 1–15: orders, transfers, staking, finalizeEvmContract — and no
spotDeploy. Genesis must be an off-chain EIP-712-signed L1 action POSTed to /exchange. Hence: an
off-chain relayer is mandatory, the trustless logic lives on HyperEVM, and the two are reconciled by EVM-side
reads of Core state (precompiles) — not by trust.Design consequence: the operator fronts the Core costs (auction HYPE + HIP-2 USDC) from its own balance sheet and
is reimbursed from escrow only after submitDeployProof passes the on-chain checks and a challenge window
elapses. The pool never crosses the bridge.
03Vault state machine
State is derived, never stored (no desync possible). Enum + transition guards:
| state (uint8) | derivation | exits via |
|---|---|---|
| 0 Pending | now < startsAt | time → Active |
| 1 Active | in window && raised < hardCap | commit()s; time/hardCap → 2 or 3 |
| 2 Failed | raise over && raised < softCap | refund() (terminal) |
| 3 AwaitingDeploy | raise over && softCap met | submitDeployProof() → 4 · markDefaulted() after endsAt+deployGrace → 5 · platform failDeploy() → 5 |
| 4 ChallengeWindow | proofAt != 0 | platform failDeploy() (≤ proofAt+window, slashes bond) → 5 · window elapsed → reimburse() → 6 |
| 5 Refunding | refundsEnabled flag | refund() per funder (terminal) |
| 6 Completed | completed flag | terminal — vault drains to exactly 0 |
reimburse() requires !refundsEnabled;
refund() requires state 2 or 5; state 2 can never reach a proof; state 5 permanently blocks reimburse.04LaunchVault API (vault/LaunchVault.sol)
// config struct (immutable per launch, set by factory.createLaunch) struct LaunchConfig { address quoteToken; // raise denomination (USDC) uint256 softCap; uint256 hardCap; // hardCap doubles as the bonus-decay denominator uint256 minCommit; uint256 maxPerWallet; // 0 = uncapped wallet uint64 startsAt; uint64 endsAt; uint64 bonusMaxWad; uint64 bonusMinWad; // 1e18-scaled: 1.25e18 → 1e18 default uint64 funderPoolWei; // HIP-1 tranche for funders, CORE wei (uint64 on HyperCore!) string ticker; // ≤6 chars; verified vs tokenInfo(name) at proof uint8 weiDecimals; // verified at proof uint64 maxFeeShareRaw; // ceiling vs tokenInfo.deployerTradingFeeShare (raw units) } // funder surface function commit(uint256 amount) external; // Active only; pulls quote; accrues points O(1) function refund() external; // Failed|Refunding; contribution + pro-rata bond share function pointsFor(uint256 amount, uint256 cumBefore) view returns (uint256); function entitlementOf(address) view returns (uint64); // CORE wei function entitlements(uint256 offset, uint256 limit) view returns (address[] memory, uint64[] memory); // relayer pagination // operator surface function submitDeployProof(uint32 coreToken, uint64 spotIndex) external; // requires: state==AwaitingDeploy, ≤ endsAt+deployGrace, msg.sender==operator // verifies via CoreReader.tokenInfo: deployer==operator, name==ticker, // weiDecimals match, feeShare ≤ maxFeeShareRaw, spotIndex ∈ spots[] // pulls bond = raised × bondBps/1e4; starts the challenge window // platform surface (factory.owner(), read LIVE — two-step transferable) function failDeploy(string reason) external; // AwaitingDeploy|ChallengeWindow; post-proof slashes bond // permissionless function markDefaulted() external; // no proof by endsAt+deployGrace → refunds function reimburse() external; // window clean → fee out, raise+bond → operator, drains to 0 // events: Committed(funder,amount,points,cumBefore) · Refunded · DeployProofSubmitted // DeployFailed(reason,slashedBond) · Defaulted · Reimbursed(operatorAmount,fee)
Factory (LaunchVaultFactory.sol): EIP-1167 clones, bounded policy (fee ≤10%, bond ≤100%, window
12h–14d, grace 1–30d, bonus cap ≤3×), approvedOperators registry, Ownable2Step.
Creation is onlyOwner in v1 — deploy slots are scarce, launches are curated by construction.
05Points math — closed-form, split-invariant
bonus(x) = BMAX − (BMAX − BMIN) · x / hardCap // continuous linear decay over fill points(amt @ cumBefore a) // integral over the commit's own span: = ∫ₐ^(a+amt) bonus(x) dx = amt · ( BMAX − (BMAX−BMIN) · (a + amt/2) / hardCap ) // O(1) at commit time, no freeze loop entitlement(f) = points[f] · funderPoolWei / totalPoints // fixed-pool normalization → genesis // checksum always reconciles; floor // dust sweeps to the HIP-2 tranche
Worked example (hardCap 50k, BMAX 1.25): commit 10k at fill 0 → rate 1.225 → 12,250 points. Same 10k at fill 10k → 1.175 → 11,750 points. Two 5k commits back-to-back == one 10k commit exactly (the integral is additive), so wallet-splitting is pointless. Fuzz-verified: every funder's points ∈ [1.00×, 1.25×] of their money.
06HyperCore read precompiles — the trust bridge
cast call addr 'tokenInfo(uint32)' 1 fails with PrecompileError; send the bare encoded args.// CoreReader.sol — verified live on mainnet address TOKEN_INFO = 0x000000000000000000000000000000000000080C; address SPOT_BALANCE = 0x0000000000000000000000000000000000000801; (bool ok, bytes memory res) = TOKEN_INFO.staticcall(abi.encode(uint32(tokenIndex))); TokenInfo memory t = abi.decode(res, (TokenInfo)); struct TokenInfo { string name; // ≤6 chars, NOT unique network-wide uint64[] spots; // spot pair indices containing this token uint64 deployerTradingFeeShare; // raw units — compare raw, don't interpret [verify] address deployer; // the registerToken2 signer = permanent fee recipient address evmContract; // linked ERC20 (0x0 if unlinked) uint8 szDecimals; uint8 weiDecimals; int8 evmExtraWeiDecimals; } // live sample, token 2 mainnet: // ("HFUN", [1], 0, 0xE2a0cC…8fF3, 0xa320…726c, 2, 8, 10) // shell equivalent: cast call 0x…080C $(printf '0x%064x' 2) --rpc-url https://rpc.hyperliquid.xyz/evm \ | cast abi-decode "f()((string,uint64[],uint64,address,address,uint8,uint8,int8))"
Identity model: names are not unique on HyperCore — the proof pins
deployer + name + weiDecimals + feeShare together, so a same-name impostor fails on the deployer field.
The relayer additionally scans spotMeta at plan time and warns on collisions before auction spend.
07Relayer pipeline (relayer/launch_relayer.py)
status vault summary (web3) + live deploy auction (info API) plan entitlements (paginated) → userGenesis batches + maxSupply reconciliation + ticker-collision scan → deploy-plan.json // REFUSES a non-balancing plan execute the 5-step spotDeploy as a persisted state machine // .relayer-state/<vault>.json await-auction → registerToken2 → userGenesis×N → genesis → registerSpot → registerHyperliquidity → setDeployerTradingFeeShare // dry-run default; --yes arms; batch cursor resumes a crashed run; // auction gas is only at risk before registerToken2 confirms prove EVM side: quote.approve(bond) + vault.submitDeployProof // staticCall simulated first
Reconciliation contract (the heart of plan):
userGenesisTotal = Σ entitlementOf(funder) // ≤ funderPoolWei (floor rounding) dust = funderPoolWei − userGenesisTotal // → swept into the HIP-2 ask tranche hip2AskWei = orderSz × (nOrders − nSeededLevels) × 10^weiDecimals + dust maxSupply = userGenesisTotal + hip2AskWei // MUST equal Σ of every genesis credit
08spotDeploy action shapes (wire format, SDK v0.24-verified)
{"type":"spotDeploy","registerToken2":{"spec":{"name":"TICK","szDecimals":2,"weiDecimals":8},
"maxGas":50000000000,"fullName":"…"}}
// maxGas is an INT in HYPE 8-dec wei (floor 500 HYPE = 5e10) — a string breaks the msgpack hash
{"type":"spotDeploy","userGenesis":{"token":N,"userAndWei":[["0xaddr_lowercase","weiStr"],…],
"existingTokenAndWei":[]}}
// omit blacklistUsers entirely — optional fields are never sent empty
{"type":"spotDeploy","genesis":{"token":N,"maxSupply":"weiStr"}} // noHyperliquidity only when true
{"type":"spotDeploy","registerSpot":{"tokens":[base,quote]}} // quote 0 = USDC
{"type":"spotDeploy","registerHyperliquidity":{"spot":S,"startPx":"0.002","orderSz":"11250",
"nOrders":100,"nSeededLevels":100}}
{"type":"spotDeploy","setDeployerTradingFeeShare":{"token":N,"share":"100%"}}
// {token, share} ONLY — no recipient field exists. Fees bind to the deployer address,
// permanently, ratchet-down only. Platform↔project splits happen EVM-side.
Signing: EIP-712 phantom-agent over keccak(msgpack(action) ‖ nonce ‖ …), domain
{name:"Exchange", chainId:1337}, source a/b for mainnet/testnet. Use the
official SDK (hyperliquid-python-sdk ≥ 0.24) — never hand-roll it.
09Auction economics, for engineers
| property | value |
|---|---|
| what it sells | the right to CREATE an asset on HyperCore — order book + main-UI listing + HIP-2 + fee stream. Not the name (not unique; identity = token index). Duplication costs the squatter their own slot → scarcity is the anti-spam. |
| cadence | one winner per 31h window, network-wide (~0.77 launches/day max) |
| price path | Dutch: starts at 2× last clear (500 if last window failed), linear decay to the 500 HYPE floor over 31h |
| read it live | POST /info {"type":"spotDeployState","user":"0x0"} → gasAuction.currentGas (null = window cleared; endGas = clearing price) |
| payment | HYPE (since 2025-05-22), charged at registerToken2, NON-REFUNDABLE if the deploy strands |
| strategy | relayer polls and bids when price ≤ budget; sniping the floor is cheap but raceable — a rival who outbids pushes YOUR next window to 2× their clear |
10Local dev environment (full pipeline minus Core writes)
# 1. chain + contracts + a funded raise (3 staggered commits) anvil --chain-id 31337 cd vault && forge script script/DevDeploy.s.sol --tc DevDeploy \ --rpc-url http://127.0.0.1:8545 --broadcast # prints factory/vault addresses # 2. frontend against it (no-store server; override persists in localStorage) python3 scripts/serve-frontend.py 5180 open "http://localhost:5180/?coreFactory=<FACTORY>&coreRpc=http://127.0.0.1:8545&devKey=<ANVIL_PK>" # devKey signs in-page — ONLY honored on localhost RPCs # 3. relayer against it cd relayer && .venv/bin/python launch_relayer.py status --vault <VAULT> --rpc http://127.0.0.1:8545 .venv/bin/python launch_relayer.py plan --vault <VAULT> --rpc http://127.0.0.1:8545 \ --max-gas-hype-wei 60000000000 --start-px 0.0001 --order-sz 1500000 --n-orders 200 --n-seeded 100 # tests: 23/23 (17 unit + 6 stateful invariants, 256 runs × depth 32) cd vault && forge test
Testnet rehearsal: relayer/preflight.py --testnet gates everything (RPC, precompile, balances,
big-blocks — the factory deploy exceeds the 2M small-block limit, toggle via --enable-big-blocks).
Full sequence in docs/TESTNET_RUNBOOK.md.
11Proven invariants (the fuzz suite's guarantees)
| invariant | statement |
|---|---|
| AccountingConsistent | totalRaised == Σ contributed; totalPoints == Σ points — always |
| Solvency | vault balance ≥ obligations in every state; Completed drains to exactly 0 |
| RefundXorReimburse | refunds paid ⟹ never reimbursed; reimbursed ⟹ no refunds, ever |
| PointsRateBounded | ∀ funder: contributed ≤ points ≤ contributed × BMAX |
| EntitlementsNeverExceedPool | Σ entitlements ≤ funderPoolWei (genesis checksum can always reconcile) |
| StateFlagsSane | no proof below softCap; no bond without proof; no partial slash |
12Known unknowns — [verify] on testnet before mainnet
| item | question | where it's handled |
|---|---|---|
| userGenesis scale | max entries/call; does genesis to fresh addresses trigger the 1-quote-token activation fee? | gate-2 experiment; relayer batch size configurable |
| HIP-2 seed recovery | is seeded USDC/token recoverable or permanently committed? | gate-3 experiment; affects operator capital model |
| hip2Mode | does genesis accept maxSupply > Σ credits (implicit hyperliquidity balance) or is a self-credit required? | plan --hip2-mode implicit|selfCredit |
| maxGas units | 8-dec HYPE wei is SDK convention; docs don't state units | confirm the debit on the testnet bid |
| feeShare raw units | tokenInfo.deployerTradingFeeShare encoding | rehearsal sets maxFeeShareRaw = uint64 max, tighten after |
HyperPad · Core rail dev reference · sources: vault/*.sol · relayer/*.py · docs/HYBRID_SPEC.md · concepts version