Core rail · developer reference

concepts version → app →

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.

01 system map02 the constraint that shapes everything 03 vault state machine04 LaunchVault API 05 points math06 HyperCore precompiles 07 relayer pipeline08 spotDeploy action shapes 09 auction economics10 local dev environment 11 proven invariants12 known unknowns

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

No Solidity contract can create a HIP-1 token. CoreWriter (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)derivationexits via
0 Pendingnow < startsAttime → Active
1 Activein window && raised < hardCapcommit()s; time/hardCap → 2 or 3
2 Failedraise over && raised < softCaprefund() (terminal)
3 AwaitingDeployraise over && softCap metsubmitDeployProof() → 4 · markDefaulted() after endsAt+deployGrace → 5 · platform failDeploy() → 5
4 ChallengeWindowproofAt != 0platform failDeploy() (≤ proofAt+window, slashes bond) → 5 · window elapsed → reimburse() → 6
5 RefundingrefundsEnabled flagrefund() per funder (terminal)
6 Completedcompleted flagterminal — vault drains to exactly 0
Refund-XOR-reimburse is structural: 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

Precompiles take RAW abi-encoded arguments — no 4-byte selector. A normal 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

propertyvalue
what it sellsthe 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.
cadenceone winner per 31h window, network-wide (~0.77 launches/day max)
price pathDutch: starts at 2× last clear (500 if last window failed), linear decay to the 500 HYPE floor over 31h
read it livePOST /info {"type":"spotDeployState","user":"0x0"}gasAuction.currentGas (null = window cleared; endGas = clearing price)
paymentHYPE (since 2025-05-22), charged at registerToken2, NON-REFUNDABLE if the deploy strands
strategyrelayer 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)

invariantstatement
AccountingConsistenttotalRaised == Σ contributed; totalPoints == Σ points — always
Solvencyvault balance ≥ obligations in every state; Completed drains to exactly 0
RefundXorReimburserefunds paid ⟹ never reimbursed; reimbursed ⟹ no refunds, ever
PointsRateBounded∀ funder: contributed ≤ points ≤ contributed × BMAX
EntitlementsNeverExceedPoolΣ entitlements ≤ funderPoolWei (genesis checksum can always reconcile)
StateFlagsSaneno proof below softCap; no bond without proof; no partial slash

12Known unknowns — [verify] on testnet before mainnet

itemquestionwhere it's handled
userGenesis scalemax entries/call; does genesis to fresh addresses trigger the 1-quote-token activation fee?gate-2 experiment; relayer batch size configurable
HIP-2 seed recoveryis seeded USDC/token recoverable or permanently committed?gate-3 experiment; affects operator capital model
hip2Modedoes genesis accept maxSupply > Σ credits (implicit hyperliquidity balance) or is a self-credit required?plan --hip2-mode implicit|selfCredit
maxGas units8-dec HYPE wei is SDK convention; docs don't state unitsconfirm the debit on the testnet bid
feeShare raw unitstokenInfo.deployerTradingFeeShare encodingrehearsal sets maxFeeShareRaw = uint64 max, tighten after
Plus the standing mainnet gates: external audit, factory owner behind a Safe + timelock, and the real USDC quote address. Testnet success does not waive them.

HyperPad · Core rail dev reference · sources: vault/*.sol · relayer/*.py · docs/HYBRID_SPEC.md · concepts version