01 Block Processing Pipeline
The confirmed block pipeline from binary RE (string at
0x539f8a):
RecoverUsers -> BeginBlock -> DeliverSignedActions -> Commit -> RespHash -> Check.
Implemented in Exchange::process_block() in hl-engine/src/exchange.rs.
CONFIRMED from binary RE
INFERRED
STUB / unimplemented
flowchart TD
subgraph InputLayer["Block Input (replica_cmds JSON)"]
BLK["Block JSON
round, time, hardfork,
signed_action_bundles"] end subgraph Pipeline["Exchange::process_block()"] direction TB PB["process_block(&block)"] RU["RecoverUsers
resolve_actor(sa, bundle)"] BB["BeginBlock
begin_block(round, time)"] subgraph BeginBlockEffects["begin_block: 9 named effects + supplementary maintenance"] direction TB E0["1. update_oracle()
pre-process oracle state"] E1["2. distribute_funding(dt)
settle cum_funding to positions"] E2["3. apply_bole_liquidations()
BOLE lending liquidation pass"] E3["4. update_funding_rates(dt)
sample premium, update EMA"] E4["5. refresh_hip3_stale_mark_pxs()
refresh stale HIP-3 prices"] E5["6. prune_book_empty_user_states()
clean up empty book entries"] E6["7. update_staking_rewards()
stage staking rewards"] E7["8. update_action_delayer()
drain matured CoreWriter actions"] E8["9. update_aligned_quote_token()
sample validator rate votes"] E9["supplementary maintenance
trigger orders, OI cap, stale guards"] E0 --> E1 --> E2 --> E3 --> E4 --> E5 --> E6 --> E7 --> E8 --> E9 end DSA["DeliverSignedActions
for each bundle: for each sa:"] TYPED["serde_json::from_value::<HlAction>()"] DT["dispatch_typed_action()"] DJ["dispatch_action_json()
fallback for unknown actions"] FILLS["Process Fills
compute_fee() + clearinghouse.process_fill()"] EB["end_block(round, time)
mark price from book mid"] PB --> RU --> BB BB --> BeginBlockEffects BeginBlockEffects --> DSA DSA --> TYPED TYPED -->|"Ok(typed)"| DT TYPED -->|"Err(_)"| DJ DT --> FILLS DJ --> FILLS FILLS --> EB end subgraph OutputLayer["Block Output"] BR["BlockResult
fills, actions_processed,
orders_placed, blocked_actions"] AH["compute_app_hash()
state_hasher.finalize() -> [u8; 32]"] end BLK --> PB EB --> BR EB --> AH style InputLayer fill:#1c2128,stroke:#30363d,color:#c9d1d9 style Pipeline fill:#0d1117,stroke:#7c3aed,color:#c9d1d9 style BeginBlockEffects fill:#161b22,stroke:#7c3aed,color:#c9d1d9 style OutputLayer fill:#1c2128,stroke:#30363d,color:#c9d1d9 style E0 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E1 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E2 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E3 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E4 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E5 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E6 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E7 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E8 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E9 fill:#1e1235,stroke:#7c3aed,color:#a78bfa
round, time, hardfork,
signed_action_bundles"] end subgraph Pipeline["Exchange::process_block()"] direction TB PB["process_block(&block)"] RU["RecoverUsers
resolve_actor(sa, bundle)"] BB["BeginBlock
begin_block(round, time)"] subgraph BeginBlockEffects["begin_block: 9 named effects + supplementary maintenance"] direction TB E0["1. update_oracle()
pre-process oracle state"] E1["2. distribute_funding(dt)
settle cum_funding to positions"] E2["3. apply_bole_liquidations()
BOLE lending liquidation pass"] E3["4. update_funding_rates(dt)
sample premium, update EMA"] E4["5. refresh_hip3_stale_mark_pxs()
refresh stale HIP-3 prices"] E5["6. prune_book_empty_user_states()
clean up empty book entries"] E6["7. update_staking_rewards()
stage staking rewards"] E7["8. update_action_delayer()
drain matured CoreWriter actions"] E8["9. update_aligned_quote_token()
sample validator rate votes"] E9["supplementary maintenance
trigger orders, OI cap, stale guards"] E0 --> E1 --> E2 --> E3 --> E4 --> E5 --> E6 --> E7 --> E8 --> E9 end DSA["DeliverSignedActions
for each bundle: for each sa:"] TYPED["serde_json::from_value::<HlAction>()"] DT["dispatch_typed_action()"] DJ["dispatch_action_json()
fallback for unknown actions"] FILLS["Process Fills
compute_fee() + clearinghouse.process_fill()"] EB["end_block(round, time)
mark price from book mid"] PB --> RU --> BB BB --> BeginBlockEffects BeginBlockEffects --> DSA DSA --> TYPED TYPED -->|"Ok(typed)"| DT TYPED -->|"Err(_)"| DJ DT --> FILLS DJ --> FILLS FILLS --> EB end subgraph OutputLayer["Block Output"] BR["BlockResult
fills, actions_processed,
orders_placed, blocked_actions"] AH["compute_app_hash()
state_hasher.finalize() -> [u8; 32]"] end BLK --> PB EB --> BR EB --> AH style InputLayer fill:#1c2128,stroke:#30363d,color:#c9d1d9 style Pipeline fill:#0d1117,stroke:#7c3aed,color:#c9d1d9 style BeginBlockEffects fill:#161b22,stroke:#7c3aed,color:#c9d1d9 style OutputLayer fill:#1c2128,stroke:#30363d,color:#c9d1d9 style E0 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E1 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E2 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E3 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E4 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E5 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E6 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E7 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E8 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style E9 fill:#1e1235,stroke:#7c3aed,color:#a78bfa
02 Action Dispatch Flow
Dispatch-shape reference for the action layer in
hl-engine/src/actions.rs.
Typed dispatch flows through dispatch_typed_action(), JSON fallback through
dispatch_action_json(). Use the Yellow Paper action inventory for the
authoritative current variant and sub-variant counts.
flowchart TD
subgraph Ingestion["Signed Action Bundle"]
SA["SignedAction
{action: JSON, nonce, signature}"] ACTOR["resolve_actor(sa, bundle)
recover user address"] end SA --> ACTOR subgraph Dispatch["Dual Dispatch"] DESER["serde_json::from_value::<HlAction>(action)"] TYPED_PATH["dispatch_typed_action()
pattern-match on 90 variants"] JSON_PATH["dispatch_action_json()
match action_type string"] DESER -->|"Ok(typed)"| TYPED_PATH DESER -->|"Err(_)"| JSON_PATH end ACTOR --> DESER subgraph Categories["11 Action Categories (90 total variants)"] direction TB C1["Cat 1: Trading (12)
order, cancel, cancelByCloid,
modify, batchModify, liquidate,
twapOrder, twapCancel, scheduleCancel,
updateIsolatedMargin,
topUpIsolatedOnlyMargin, updateLeverage"] C2["Cat 2: Transfers (6)
usdSend, spotSend, sendAsset,
subAccountTransfer,
subAccountSpotTransfer,
usdClassTransfer"] C3["Cat 3: Staking (5)
tokenDelegate, linkStakingUser,
claimRewards, registerValidator,
extendLongTermStaking"] C4["Cat 4: Vault (5)
createVault, vaultModify,
vaultTransfer, vaultDistribute,
netChildVaultPositions"] C5["Cat 5: Governance (3)
govPropose, govVote, voteGlobal"] C6["Cat 6: HIP-3/HIP-4 (4)
perpDeploy, spotDeploy,
userOutcome, SetGlobalAction"] C7["Cat 7: Validator (11)
CValidator, CSigner, validatorL1Vote,
validatorL1Stream, signValidatorSetUpdate,
ValidatorSignWithdrawal, VoteEthDeposit,
VoteEthFinalizedWithdrawal,
VoteEthFinalizedValidatorSetUpdate,
voteAppHash, forceIncreaseEpoch"] C8["Cat 8: Account (7)
approveAgent, setDisplayName,
setReferrer, convertToMultiSigUser,
approveBuilderFee, startFeeTrial,
createSubAccount"] C9["Cat 9: DeFi (7)
borrowLend, userDexAbstraction,
userSetAbstraction, agentSetAbstraction,
userPortfolioMargin, sendToEvmWithData,
agentEnableDexAbstraction"] C10["Cat 10: System (13)
SystemBole, SystemSpotSend,
SystemUsdClassTransfer, CWithdraw,
CUserModify, EvmUserModify,
ReassessFees, FinalizeEvmContract, ..."] C11["Cat 11: Special/EVM (16)
noop, evmRawTx, withdraw3,
multiSig, priorityBid,
settleFraction, registerSpot, ..."] end subgraph Results["ActionResult"] AR_OK["Ok"] AR_FILLS["Fills(Vec<Fill>)"] AR_CANCEL["Cancelled(count)"] AR_UNIMP["Unimplemented(reason)"] AR_REJ["Rejected{action, reason}"] end TYPED_PATH --> Categories JSON_PATH --> Categories Categories --> Results style Ingestion fill:#1c2128,stroke:#30363d,color:#c9d1d9 style Dispatch fill:#161b22,stroke:#7c3aed,color:#c9d1d9 style Categories fill:#0d1117,stroke:#30363d,color:#c9d1d9 style Results fill:#1c2128,stroke:#30363d,color:#c9d1d9 style C1 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style C6 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style C7 fill:#1e1235,stroke:#7c3aed,color:#a78bfa
{action: JSON, nonce, signature}"] ACTOR["resolve_actor(sa, bundle)
recover user address"] end SA --> ACTOR subgraph Dispatch["Dual Dispatch"] DESER["serde_json::from_value::<HlAction>(action)"] TYPED_PATH["dispatch_typed_action()
pattern-match on 90 variants"] JSON_PATH["dispatch_action_json()
match action_type string"] DESER -->|"Ok(typed)"| TYPED_PATH DESER -->|"Err(_)"| JSON_PATH end ACTOR --> DESER subgraph Categories["11 Action Categories (90 total variants)"] direction TB C1["Cat 1: Trading (12)
order, cancel, cancelByCloid,
modify, batchModify, liquidate,
twapOrder, twapCancel, scheduleCancel,
updateIsolatedMargin,
topUpIsolatedOnlyMargin, updateLeverage"] C2["Cat 2: Transfers (6)
usdSend, spotSend, sendAsset,
subAccountTransfer,
subAccountSpotTransfer,
usdClassTransfer"] C3["Cat 3: Staking (5)
tokenDelegate, linkStakingUser,
claimRewards, registerValidator,
extendLongTermStaking"] C4["Cat 4: Vault (5)
createVault, vaultModify,
vaultTransfer, vaultDistribute,
netChildVaultPositions"] C5["Cat 5: Governance (3)
govPropose, govVote, voteGlobal"] C6["Cat 6: HIP-3/HIP-4 (4)
perpDeploy, spotDeploy,
userOutcome, SetGlobalAction"] C7["Cat 7: Validator (11)
CValidator, CSigner, validatorL1Vote,
validatorL1Stream, signValidatorSetUpdate,
ValidatorSignWithdrawal, VoteEthDeposit,
VoteEthFinalizedWithdrawal,
VoteEthFinalizedValidatorSetUpdate,
voteAppHash, forceIncreaseEpoch"] C8["Cat 8: Account (7)
approveAgent, setDisplayName,
setReferrer, convertToMultiSigUser,
approveBuilderFee, startFeeTrial,
createSubAccount"] C9["Cat 9: DeFi (7)
borrowLend, userDexAbstraction,
userSetAbstraction, agentSetAbstraction,
userPortfolioMargin, sendToEvmWithData,
agentEnableDexAbstraction"] C10["Cat 10: System (13)
SystemBole, SystemSpotSend,
SystemUsdClassTransfer, CWithdraw,
CUserModify, EvmUserModify,
ReassessFees, FinalizeEvmContract, ..."] C11["Cat 11: Special/EVM (16)
noop, evmRawTx, withdraw3,
multiSig, priorityBid,
settleFraction, registerSpot, ..."] end subgraph Results["ActionResult"] AR_OK["Ok"] AR_FILLS["Fills(Vec<Fill>)"] AR_CANCEL["Cancelled(count)"] AR_UNIMP["Unimplemented(reason)"] AR_REJ["Rejected{action, reason}"] end TYPED_PATH --> Categories JSON_PATH --> Categories Categories --> Results style Ingestion fill:#1c2128,stroke:#30363d,color:#c9d1d9 style Dispatch fill:#161b22,stroke:#7c3aed,color:#c9d1d9 style Categories fill:#0d1117,stroke:#30363d,color:#c9d1d9 style Results fill:#1c2128,stroke:#30363d,color:#c9d1d9 style C1 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style C6 fill:#1e1235,stroke:#7c3aed,color:#a78bfa style C7 fill:#1e1235,stroke:#7c3aed,color:#a78bfa
03 Order Lifecycle
From user submission through
process_single_order() to matching, fill processing,
and state hash update. Covers place, cancel, modify, TWAP, trigger, and scheduled cancel.
Code in hl-engine/src/exchange.rs and hl-engine/src/matching.rs.
flowchart TD
subgraph UserAction["User Order Actions"]
PLACE["order
(1..N orders per action)"] CANCEL["cancel
(asset, oid pairs)"] CBYC["cancelByCloid
(asset, cloid)"] MODIFY["modify
(cancel old + place new)"] BMOD["batchModify
(N x modify)"] TWAP["twapOrder
(TWAP sliced execution)"] SCHED["scheduleCancel
(volume-gated, min 5s)"] end subgraph Validation["Pre-Match Validation"] direction TB PARSE["Parse order JSON
a=asset, b=is_buy, p=price,
s=size, r=reduce_only, t=tif, c=cloid"] PXSZ["Validate px > 0, sz > 0,
both finite"] MARGIN["margin::check_order_margin()
build_user_margin_state(user)
+ oracle prices + margin tiers"] end PLACE --> PARSE PARSE --> PXSZ PXSZ -->|"invalid"| REJECT["Return empty fills"] PXSZ -->|"valid"| MARGIN MARGIN -->|"Err"| REJECT_M["Reject: perpMarginRejected
record_order_action_response(error)"] subgraph Matching["MatchingEngine::process_order()"] direction TB ALLOC["allocate_oid()
next_oid++"] BUILD["Build Order struct
{oid, user, asset, side,
price, size, tif, reduce_only, cloid}"] BOOK["book.match_order(&order)"] PRICE_TIME["Price-Time Priority
walk opposite side levels"] GEN_FILL["Generate Fill
{asset, maker, taker,
price, size, side,
maker_oid, taker_oid}"] REST["Remainder rests on book
track_resting_order(oid, identity)"] IOC_CANCEL["IOC: cancel remainder
no rest on book"] end MARGIN -->|"Ok"| ALLOC ALLOC --> BUILD --> BOOK BOOK --> PRICE_TIME --> GEN_FILL BOOK -->|"no match + GTC/ALO"| REST BOOK -->|"no match + IOC"| IOC_CANCEL subgraph FillProcessing["Fill Processing (exchange.rs)"] direction TB FEE["compute_fee(maker/taker)
fee_tracker.get_user_state()
debit fees from balances"] CLEAR["clearinghouse.process_fill()
update positions, entry_px, szi"] EMIT["emit(ExchangeEvent::Fill)"] HIST["user_fills history
(circular buffer, max 1000)"] end GEN_FILL --> FEE --> CLEAR --> EMIT --> HIST subgraph HashPath["State Hash Update"] HASH_FILL["state_hasher.record_fill()
OrderFillHash{filled, totalSz, avgPx, oid}"] HASH_ORDER["state_hasher.record_order_action_response()
filled/resting/waitingForFill/error"] HASH_CANCEL["state_hasher.record_cancel_action_response()
Success or Error"] end FEE --> HASH_FILL REST --> HASH_ORDER IOC_CANCEL --> HASH_ORDER CANCEL --> HASH_CANCEL CBYC --> HASH_CANCEL MODIFY -->|"cancel_order_by_asset_oid"| CANCEL MODIFY -->|"process_single_order"| PARSE style UserAction fill:#1c2128,stroke:#30363d,color:#c9d1d9 style Validation fill:#161b22,stroke:#7c3aed,color:#c9d1d9 style Matching fill:#1e1235,stroke:#7c3aed,color:#a78bfa style FillProcessing fill:#161b22,stroke:#7c3aed,color:#c9d1d9 style HashPath fill:#1c2128,stroke:#7c3aed,color:#c9d1d9
(1..N orders per action)"] CANCEL["cancel
(asset, oid pairs)"] CBYC["cancelByCloid
(asset, cloid)"] MODIFY["modify
(cancel old + place new)"] BMOD["batchModify
(N x modify)"] TWAP["twapOrder
(TWAP sliced execution)"] SCHED["scheduleCancel
(volume-gated, min 5s)"] end subgraph Validation["Pre-Match Validation"] direction TB PARSE["Parse order JSON
a=asset, b=is_buy, p=price,
s=size, r=reduce_only, t=tif, c=cloid"] PXSZ["Validate px > 0, sz > 0,
both finite"] MARGIN["margin::check_order_margin()
build_user_margin_state(user)
+ oracle prices + margin tiers"] end PLACE --> PARSE PARSE --> PXSZ PXSZ -->|"invalid"| REJECT["Return empty fills"] PXSZ -->|"valid"| MARGIN MARGIN -->|"Err"| REJECT_M["Reject: perpMarginRejected
record_order_action_response(error)"] subgraph Matching["MatchingEngine::process_order()"] direction TB ALLOC["allocate_oid()
next_oid++"] BUILD["Build Order struct
{oid, user, asset, side,
price, size, tif, reduce_only, cloid}"] BOOK["book.match_order(&order)"] PRICE_TIME["Price-Time Priority
walk opposite side levels"] GEN_FILL["Generate Fill
{asset, maker, taker,
price, size, side,
maker_oid, taker_oid}"] REST["Remainder rests on book
track_resting_order(oid, identity)"] IOC_CANCEL["IOC: cancel remainder
no rest on book"] end MARGIN -->|"Ok"| ALLOC ALLOC --> BUILD --> BOOK BOOK --> PRICE_TIME --> GEN_FILL BOOK -->|"no match + GTC/ALO"| REST BOOK -->|"no match + IOC"| IOC_CANCEL subgraph FillProcessing["Fill Processing (exchange.rs)"] direction TB FEE["compute_fee(maker/taker)
fee_tracker.get_user_state()
debit fees from balances"] CLEAR["clearinghouse.process_fill()
update positions, entry_px, szi"] EMIT["emit(ExchangeEvent::Fill)"] HIST["user_fills history
(circular buffer, max 1000)"] end GEN_FILL --> FEE --> CLEAR --> EMIT --> HIST subgraph HashPath["State Hash Update"] HASH_FILL["state_hasher.record_fill()
OrderFillHash{filled, totalSz, avgPx, oid}"] HASH_ORDER["state_hasher.record_order_action_response()
filled/resting/waitingForFill/error"] HASH_CANCEL["state_hasher.record_cancel_action_response()
Success or Error"] end FEE --> HASH_FILL REST --> HASH_ORDER IOC_CANCEL --> HASH_ORDER CANCEL --> HASH_CANCEL CBYC --> HASH_CANCEL MODIFY -->|"cancel_order_by_asset_oid"| CANCEL MODIFY -->|"process_single_order"| PARSE style UserAction fill:#1c2128,stroke:#30363d,color:#c9d1d9 style Validation fill:#161b22,stroke:#7c3aed,color:#c9d1d9 style Matching fill:#1e1235,stroke:#7c3aed,color:#a78bfa style FillProcessing fill:#161b22,stroke:#7c3aed,color:#c9d1d9 style HashPath fill:#1c2128,stroke:#7c3aed,color:#c9d1d9
04 Funding Cycle
Oracle updates through
SetGlobalAction, mark price computation, funding rate EMA,
and settlement every 3600s. Source: hl-engine/src/funding.rs and
exchange.rs::begin_block(). Settlement formula:
funding_pnl = szi * oracle_px * (cum_funding_global - cum_funding_entry).
flowchart TD
subgraph OracleInput["Oracle Price Pipeline"]
direction TB
EXT["External Feeds
(Binance, etc.)"] VOTE["ValidatorL1VoteAction
validators vote on prices"] MEDIAN["VoteTracker
median of validator votes"] SGA["SetGlobalAction
{pxs, externalPerpPxs, usdtUsdcPx}"] ORACLE["Oracle.set_price(asset, px)
oracle.pxs HashMap"] CH_ORACLE["clearinghouse.set_oracle_price(asset, px)"] EXT --> VOTE --> MEDIAN --> SGA SGA --> ORACLE SGA --> CH_ORACLE end subgraph MarkPrice["Mark Price Computation"] direction TB BOOK_MID["OrderBook::mid_price()
(best_bid + best_ask) / 2"] MARK_STORE["clearinghouse.mark_prices"] END_BLOCK["end_block(): update mark
from book mid-prices"] BOOK_MID --> END_BLOCK --> MARK_STORE end subgraph FundingRate["Funding Rate (begin_block effect 3)"] direction TB PREMIUM["Premium = (mark_px - oracle_px) / oracle_px"] EMA_UPDATE["DeterministicEma::update(sample, dt)
decay = exp(-dt * ln2 / half_life)
num = num * decay + sample * (1 - decay)
denom = denom * decay + (1 - decay)"] RATE_CALC["rate = premium_ema + interest_rate
interest = 0.0000125/hr (0.01%/8h)"] RATE_STORE["clearinghouse.funding_rates[asset]"] PREMIUM --> EMA_UPDATE --> RATE_CALC --> RATE_STORE end subgraph Settlement["Funding Settlement (begin_block effect 1)"] direction TB DT["compute_dt(block_time)
~70ms per block"] FUND_PER["funding_per_unit =
rate * dt / 3600"] CUM_UPD["cum_funding_per_asset[asset] +=
funding_per_unit"] POS_WALK["For each user position:
delta = global_cum - pos.cum_funding_entry"] PAYMENT["payment = szi * oracle_px * delta
longs pay if rate > 0"] BAL_ADJ["balance -= payment
pos.cum_funding_entry = global_cum"] HASH_SETTLE["state_hasher.record_settlement()
HashCategory::Settlement"] DT --> FUND_PER --> CUM_UPD --> POS_WALK --> PAYMENT --> BAL_ADJ --> HASH_SETTLE end ORACLE --> PREMIUM MARK_STORE --> PREMIUM RATE_STORE --> FUND_PER ORACLE --> PAYMENT style OracleInput fill:#1c2128,stroke:#30363d,color:#c9d1d9 style MarkPrice fill:#161b22,stroke:#30363d,color:#c9d1d9 style FundingRate fill:#1e1235,stroke:#7c3aed,color:#a78bfa style Settlement fill:#161b22,stroke:#7c3aed,color:#c9d1d9
(Binance, etc.)"] VOTE["ValidatorL1VoteAction
validators vote on prices"] MEDIAN["VoteTracker
median of validator votes"] SGA["SetGlobalAction
{pxs, externalPerpPxs, usdtUsdcPx}"] ORACLE["Oracle.set_price(asset, px)
oracle.pxs HashMap"] CH_ORACLE["clearinghouse.set_oracle_price(asset, px)"] EXT --> VOTE --> MEDIAN --> SGA SGA --> ORACLE SGA --> CH_ORACLE end subgraph MarkPrice["Mark Price Computation"] direction TB BOOK_MID["OrderBook::mid_price()
(best_bid + best_ask) / 2"] MARK_STORE["clearinghouse.mark_prices"] END_BLOCK["end_block(): update mark
from book mid-prices"] BOOK_MID --> END_BLOCK --> MARK_STORE end subgraph FundingRate["Funding Rate (begin_block effect 3)"] direction TB PREMIUM["Premium = (mark_px - oracle_px) / oracle_px"] EMA_UPDATE["DeterministicEma::update(sample, dt)
decay = exp(-dt * ln2 / half_life)
num = num * decay + sample * (1 - decay)
denom = denom * decay + (1 - decay)"] RATE_CALC["rate = premium_ema + interest_rate
interest = 0.0000125/hr (0.01%/8h)"] RATE_STORE["clearinghouse.funding_rates[asset]"] PREMIUM --> EMA_UPDATE --> RATE_CALC --> RATE_STORE end subgraph Settlement["Funding Settlement (begin_block effect 1)"] direction TB DT["compute_dt(block_time)
~70ms per block"] FUND_PER["funding_per_unit =
rate * dt / 3600"] CUM_UPD["cum_funding_per_asset[asset] +=
funding_per_unit"] POS_WALK["For each user position:
delta = global_cum - pos.cum_funding_entry"] PAYMENT["payment = szi * oracle_px * delta
longs pay if rate > 0"] BAL_ADJ["balance -= payment
pos.cum_funding_entry = global_cum"] HASH_SETTLE["state_hasher.record_settlement()
HashCategory::Settlement"] DT --> FUND_PER --> CUM_UPD --> POS_WALK --> PAYMENT --> BAL_ADJ --> HASH_SETTLE end ORACLE --> PREMIUM MARK_STORE --> PREMIUM RATE_STORE --> FUND_PER ORACLE --> PAYMENT style OracleInput fill:#1c2128,stroke:#30363d,color:#c9d1d9 style MarkPrice fill:#161b22,stroke:#30363d,color:#c9d1d9 style FundingRate fill:#1e1235,stroke:#7c3aed,color:#a78bfa style Settlement fill:#161b22,stroke:#7c3aed,color:#c9d1d9
05 State Hash Computation
LtHash16 accumulator pipeline confirmed from binary RE (2026-04-01). Algorithm:
rmp_serde::to_vec_named(response) -> blake3 XOF (2048B) ->
interpret as 1024 u16 LE -> paddw into accumulator ->
SHA-256(combined 2048B) -> 32-byte app_hash.
All 11 L1 categories + 3 EVM categories. Code in hl-engine/src/state_hash.rs
and hl-engine/src/lthash.rs.
flowchart TD
subgraph Responses["Action Response Structs (6 Serializers from RE)"]
direction TB
SJ["SerializerJ (0x4280c90)
OrderFillHash{filled, error,
waitingForFill, waitingForTrigger,
resting, totalSz, avgPx, oid, cloid}"] SD["SerializerD (0x427eeb0)
SimpleResultResponse{success, error}"] SE["SerializerE (0x427f120)
StatusResultResponse{status}"] SB["SerializerB (0x427e860)
TwapSliceFillResponse{coin, user,
side, sz, executedSz, ...}"] SC["SerializerC (0x427ead0)
TwapStateResponse{running, twapId, error}"] SA["SerializerA (0x427dfb0)
SpotDust{coin, szi}"] end subgraph HashPipeline["Hash Pipeline (per response)"] direction TB RMP["rmp_serde::to_vec_named(response)
named msgpack, map mode, camelCase keys"] BLAKE["blake3::Hasher::update(&bytes)
XOF mode -> fill 2048 bytes"] U16["Interpret as 1024 u16 LE values"] PADDW["paddw (wrapping_add) into
category LtHash16 accumulator"] RMP --> BLAKE --> U16 --> PADDW end subgraph Categories["11 L1 Hash Categories (enum at 0x65fb61)"] direction TB CAT0["0: CValidator
validator set changes"] CAT1["1: CSigner
signer key changes"] CAT2["2: Na
general: trades, orders, balances"] CAT3["3: LiquidatedCross
cross-margin liquidations"] CAT4["4: LiquidatedIsolated
isolated-margin liquidations"] CAT5["5: Settlement
funding settlements"] CAT6["6: NetChildVaultPositions
child vault netting"] CAT7["7: SpotDustConversion
dust sweeps"] CAT8["8: BoleMarketLiquidation"] CAT9["9: BoleBackstopLiquidation"] CAT10["10: BolePartialLiquidation"] end subgraph EvmHash["3 EVM Categories (raw byte concat, NOT msgpack)"] direction TB EVM_A["accounts_hash
addr(20B) || balance(32B) || nonce(8B) || code_hash(32B) = 92B"] EVM_C["contracts_hash
addr(20B) || code(variable)"] EVM_S["storage_hash
addr(20B) || slot(32B) || value(32B) = 84B"] end subgraph Finalize["StateHasher::finalize()"] direction TB COMBINE["Combine all 14 LtHash16 accumulators
via paddw (wrapping add)"] SHA["SHA-256(combined 2048 bytes)"] APPHASH["32-byte app_hash
used in VoteAppHash consensus"] COMBINE --> SHA --> APPHASH end Responses --> RMP PADDW --> Categories PADDW --> EvmHash Categories --> COMBINE EvmHash --> COMBINE style Responses fill:#1c2128,stroke:#30363d,color:#c9d1d9 style HashPipeline fill:#1e1235,stroke:#7c3aed,color:#a78bfa style Categories fill:#161b22,stroke:#7c3aed,color:#c9d1d9 style EvmHash fill:#161b22,stroke:#30363d,color:#c9d1d9 style Finalize fill:#1e1235,stroke:#7c3aed,color:#a78bfa
OrderFillHash{filled, error,
waitingForFill, waitingForTrigger,
resting, totalSz, avgPx, oid, cloid}"] SD["SerializerD (0x427eeb0)
SimpleResultResponse{success, error}"] SE["SerializerE (0x427f120)
StatusResultResponse{status}"] SB["SerializerB (0x427e860)
TwapSliceFillResponse{coin, user,
side, sz, executedSz, ...}"] SC["SerializerC (0x427ead0)
TwapStateResponse{running, twapId, error}"] SA["SerializerA (0x427dfb0)
SpotDust{coin, szi}"] end subgraph HashPipeline["Hash Pipeline (per response)"] direction TB RMP["rmp_serde::to_vec_named(response)
named msgpack, map mode, camelCase keys"] BLAKE["blake3::Hasher::update(&bytes)
XOF mode -> fill 2048 bytes"] U16["Interpret as 1024 u16 LE values"] PADDW["paddw (wrapping_add) into
category LtHash16 accumulator"] RMP --> BLAKE --> U16 --> PADDW end subgraph Categories["11 L1 Hash Categories (enum at 0x65fb61)"] direction TB CAT0["0: CValidator
validator set changes"] CAT1["1: CSigner
signer key changes"] CAT2["2: Na
general: trades, orders, balances"] CAT3["3: LiquidatedCross
cross-margin liquidations"] CAT4["4: LiquidatedIsolated
isolated-margin liquidations"] CAT5["5: Settlement
funding settlements"] CAT6["6: NetChildVaultPositions
child vault netting"] CAT7["7: SpotDustConversion
dust sweeps"] CAT8["8: BoleMarketLiquidation"] CAT9["9: BoleBackstopLiquidation"] CAT10["10: BolePartialLiquidation"] end subgraph EvmHash["3 EVM Categories (raw byte concat, NOT msgpack)"] direction TB EVM_A["accounts_hash
addr(20B) || balance(32B) || nonce(8B) || code_hash(32B) = 92B"] EVM_C["contracts_hash
addr(20B) || code(variable)"] EVM_S["storage_hash
addr(20B) || slot(32B) || value(32B) = 84B"] end subgraph Finalize["StateHasher::finalize()"] direction TB COMBINE["Combine all 14 LtHash16 accumulators
via paddw (wrapping add)"] SHA["SHA-256(combined 2048 bytes)"] APPHASH["32-byte app_hash
used in VoteAppHash consensus"] COMBINE --> SHA --> APPHASH end Responses --> RMP PADDW --> Categories PADDW --> EvmHash Categories --> COMBINE EvmHash --> COMBINE style Responses fill:#1c2128,stroke:#30363d,color:#c9d1d9 style HashPipeline fill:#1e1235,stroke:#7c3aed,color:#a78bfa style Categories fill:#161b22,stroke:#7c3aed,color:#c9d1d9 style EvmHash fill:#161b22,stroke:#30363d,color:#c9d1d9 style Finalize fill:#1e1235,stroke:#7c3aed,color:#a78bfa
06 Gossip Protocol
Wire protocol for Hyperliquid gossip. Port 4001 for block streaming, port 4002 for peer discovery.
Validated from live mainnet packet captures. Code in
hl-network/src/wire.rs,
hl-network/src/connection.rs, and hl-network/src/gossip.rs.
sequenceDiagram
participant C as hlx Client
participant P as HL Peer (port 4001)
Note over C,P: Port 4001: Block Streaming Protocol
C->>P: Greeting: [u32 BE = 2][00][00][00] (7 bytes)
P->>C: Response: [u32 BE = 1][00][chain=0x04] (6 bytes)
Note over C,P: chain: 0x04 = Mainnet
loop Stream Frames
P->>C: [u32 BE body_len][u8 kind=0x01][body]
alt Bootstrap ABCI State (~1.16 GB)
Note right of C: FrameKindGuess::BootstrapAbciStateCandidate
C->>C: Skip or save large frame
else Bootstrap EVM KVs (~4 GB)
Note right of C: FrameKindGuess::BootstrapEvmKvsCandidate
C->>C: Skip or save large frame
else Heartbeat Snapshot
Note right of C: WireChunkInspection::heartbeat_like()
C->>C: WireCodec::decode_lz4_size_prepended()
C->>C: Inspect for concise_lt_hashes,
validator_set_snapshot,
random_id, evm_block_number C->>C: heartbeat_tx.try_send(payload) else Block Data C->>C: decode_wire_block(&chunk) C->>C: block_tx.try_send(WireBlock) else Concise Control Frame Note right of C: MsgConcise/OutMsgConcise (tag 27/28) C->>C: parse_concise_control_frame() end C->>C: data_tx.send(chunk) [broadcast raw] C->>C: peer_manager.record_message() end Note over C,P: On disconnect: exponential backoff
[1s, 3s, 10s, 30s, 60s] C->>P: Reconnect (stream_with_reconnect)
validator_set_snapshot,
random_id, evm_block_number C->>C: heartbeat_tx.try_send(payload) else Block Data C->>C: decode_wire_block(&chunk) C->>C: block_tx.try_send(WireBlock) else Concise Control Frame Note right of C: MsgConcise/OutMsgConcise (tag 27/28) C->>C: parse_concise_control_frame() end C->>C: data_tx.send(chunk) [broadcast raw] C->>C: peer_manager.record_message() end Note over C,P: On disconnect: exponential backoff
[1s, 3s, 10s, 30s, 60s] C->>P: Reconnect (stream_with_reconnect)
07 Bootstrap Flow
Fast ABCI state bootstrap using streaming msgpack decoding.
Memory-maps the .rmp file for zero-copy access. Target: <10s for a 461MB mainnet snapshot.
Code in
hl-engine/src/bootstrap_fast.rs.
Hash accumulators are NOT stored in snapshots -- they are rebuilt from state.
flowchart TD
subgraph Input["Snapshot Input"]
FILE[".rmp State Snapshot
(e.g. 461MB mainnet)"] end subgraph FastBootstrap["bootstrap_from_rmp_fast()"] direction TB MMAP["memmap2::Mmap::map(file)
zero-copy memory mapping"] CURSOR["Cursor<&[u8]> for streaming read"] ROOT["Read root map: {'exchange': ...}"] subgraph Exchange_Parse["parse_exchange()"] direction TB LOCUS["parse_locus()
main state container"] PERP["parse_perp_dexs()
order books"] STAKE["parse_staking()
validator state"] CSTAKE["parse_c_staking()
cold staking state"] LOCUS --> PERP --> STAKE --> CSTAKE end subgraph Locus_Parse["parse_locus() sub-sections"] direction TB CTX["parse_context() -> locus.ctx
height, hardfork, next_oid"] CLS["parse_cls_array()
clearinghouse entries (users, positions)"] UST["parse_ust_array()
user state entries"] FTR["parse_fee_tracker()
fee tier state"] BLP["parse_blp()
BOLE lending positions"] CHN["chn -> chain identifier
configure_resp_hash_for_chain()"] CTX --> CLS --> UST --> FTR --> BLP --> CHN end MMAP --> CURSOR --> ROOT --> Exchange_Parse LOCUS --> Locus_Parse end subgraph Hydration["State Hydration"] direction TB EXCH["Exchange struct populated:
round, hardfork_version"] CLEAR["Clearinghouse:
balances, positions, oracle_prices,
margin_tables, mark_prices"] MATCH["MatchingEngine:
books per asset, next_oid"] STKNG["StakingService:
stakes, delegations,
active_validators, broadcasters"] HASH_REBUILD["rebuild_state_hash()
walk positions + balances +
resting orders + validators
-> LtHash16 accumulators"] EXCH --> CLEAR --> MATCH --> STKNG --> HASH_REBUILD end subgraph Output["Bootstrap Complete"] STATS["BootstrapStats:
round, users_loaded,
positions_loaded,
oracle_prices_loaded,
books_loaded, book_levels"] READY["Exchange ready for
process_block()"] end FILE --> MMAP Exchange_Parse --> Hydration HASH_REBUILD --> STATS STATS --> READY style Input fill:#1c2128,stroke:#30363d,color:#c9d1d9 style FastBootstrap fill:#0d1117,stroke:#7c3aed,color:#c9d1d9 style Exchange_Parse fill:#1e1235,stroke:#7c3aed,color:#a78bfa style Locus_Parse fill:#161b22,stroke:#7c3aed,color:#c9d1d9 style Hydration fill:#161b22,stroke:#30363d,color:#c9d1d9 style Output fill:#1c2128,stroke:#30363d,color:#c9d1d9
(e.g. 461MB mainnet)"] end subgraph FastBootstrap["bootstrap_from_rmp_fast()"] direction TB MMAP["memmap2::Mmap::map(file)
zero-copy memory mapping"] CURSOR["Cursor<&[u8]> for streaming read"] ROOT["Read root map: {'exchange': ...}"] subgraph Exchange_Parse["parse_exchange()"] direction TB LOCUS["parse_locus()
main state container"] PERP["parse_perp_dexs()
order books"] STAKE["parse_staking()
validator state"] CSTAKE["parse_c_staking()
cold staking state"] LOCUS --> PERP --> STAKE --> CSTAKE end subgraph Locus_Parse["parse_locus() sub-sections"] direction TB CTX["parse_context() -> locus.ctx
height, hardfork, next_oid"] CLS["parse_cls_array()
clearinghouse entries (users, positions)"] UST["parse_ust_array()
user state entries"] FTR["parse_fee_tracker()
fee tier state"] BLP["parse_blp()
BOLE lending positions"] CHN["chn -> chain identifier
configure_resp_hash_for_chain()"] CTX --> CLS --> UST --> FTR --> BLP --> CHN end MMAP --> CURSOR --> ROOT --> Exchange_Parse LOCUS --> Locus_Parse end subgraph Hydration["State Hydration"] direction TB EXCH["Exchange struct populated:
round, hardfork_version"] CLEAR["Clearinghouse:
balances, positions, oracle_prices,
margin_tables, mark_prices"] MATCH["MatchingEngine:
books per asset, next_oid"] STKNG["StakingService:
stakes, delegations,
active_validators, broadcasters"] HASH_REBUILD["rebuild_state_hash()
walk positions + balances +
resting orders + validators
-> LtHash16 accumulators"] EXCH --> CLEAR --> MATCH --> STKNG --> HASH_REBUILD end subgraph Output["Bootstrap Complete"] STATS["BootstrapStats:
round, users_loaded,
positions_loaded,
oracle_prices_loaded,
books_loaded, book_levels"] READY["Exchange ready for
process_block()"] end FILE --> MMAP Exchange_Parse --> Hydration HASH_REBUILD --> STATS STATS --> READY style Input fill:#1c2128,stroke:#30363d,color:#c9d1d9 style FastBootstrap fill:#0d1117,stroke:#7c3aed,color:#c9d1d9 style Exchange_Parse fill:#1e1235,stroke:#7c3aed,color:#a78bfa style Locus_Parse fill:#161b22,stroke:#7c3aed,color:#c9d1d9 style Hydration fill:#161b22,stroke:#30363d,color:#c9d1d9 style Output fill:#1c2128,stroke:#30363d,color:#c9d1d9
08 EVM Integration
HyperCore (L1) and HyperEVM bridge via precompiles (read) and CoreWriter (write).
Read precompiles at
0x0800+, CoreWriter at 0x3333...3333.
Code in hl-evm/src/precompiles.rs and hl-evm/src/core_writer.rs.
EVM state hashed via raw byte concat (NOT msgpack) into 3 LtHash16 accumulators.
flowchart TD
subgraph L1["HyperCore (L1) State"]
EXCHANGE["Exchange
(clearinghouse, matching,
oracle, staking)"] STATE_HASH["StateHasher
(14 LtHash16 accumulators)"] end subgraph ReadPath["Read Path: Precompiles (0x0800+)"] direction TB PC_ADDR["Precompile Base: 0x0000...0800"] PROVIDER["L1StateProvider trait
(EngineStateProvider impl)"] PC1["0x0001: PERP_CLEARINGHOUSE
get_position(user, asset)"] PC2["0x0002: SPOT_BALANCES
get_spot_balance(user, token)"] PC3["0x0003: VAULT_EQUITY"] PC4["0x0004: STAKING_DELEGATIONS"] PC5["0x0005: ORACLE_PRICE
get_oracle_price(asset)"] PC6["0x0006: L1_BLOCK_NUMBER
get_l1_block_number()"] PC7["0x0007: MARK_PRICE
get_mark_price(asset)"] GAS["Gas: 2000 + 65 * (input_len + output_len)"] end subgraph WritePath["Write Path: CoreWriter (0x3333...3333)"] direction TB CW_ADDR["CoreWriter: 0x3333...3333"] CW_GAS["Gas: ~25,000"] CW_ACTIONS["15 Action Types:
PlaceOrder, CancelOrder,
VaultTransfer, Delegation,
SpotSend, UsdTransfer,
Withdraw, ApproveAgent,
CreateSubAccount,
SubAccountTransfer,
SetReferrer, UpdateLeverage,
UpdateIsolatedMargin,
SendAsset, ClaimRewards"] CW_EMIT["EVM Log -> CoreWriterEvent
decoded by L1"] CW_NOTE["Actions are INTENTS
not immediate state changes.
Failed actions don't revert EVM tx."] end subgraph EvmHash["EVM State Hashing"] direction TB EH_ACC["evm_accounts: LtHash16
addr(20) || balance(32) || nonce(8) || code_hash(32) = 92B"] EH_CON["evm_contracts: LtHash16
addr(20) || code(variable)"] EH_STO["evm_storage: LtHash16
addr(20) || slot(32) || value(32) = 84B"] EH_NOTE["Raw byte concat,
NOT msgpack serialization"] end subgraph CrossLayer["Cross-Layer Actions"] direction TB SEND_EVM["sendToEvmWithData
L1 -> EVM with calldata"] EVM_RAW["evmRawTx
raw EVM tx forwarded through L1"] EVM_MOD["EvmUserModify
system-generated position adjustment"] FIN_CON["FinalizeEvmContract
finalize EVM contract deployment"] end EXCHANGE -->|"Arc<Mutex<Exchange>>"| PROVIDER PROVIDER --> PC1 & PC2 & PC3 & PC4 & PC5 & PC6 & PC7 CW_EMIT -->|"decoded log"| EXCHANGE CW_ACTIONS --> CW_EMIT EXCHANGE --> EH_ACC & EH_CON & EH_STO EH_ACC & EH_CON & EH_STO --> STATE_HASH style L1 fill:#1c2128,stroke:#7c3aed,color:#c9d1d9 style ReadPath fill:#161b22,stroke:#30363d,color:#c9d1d9 style WritePath fill:#161b22,stroke:#30363d,color:#c9d1d9 style EvmHash fill:#1e1235,stroke:#7c3aed,color:#a78bfa style CrossLayer fill:#1c2128,stroke:#30363d,color:#c9d1d9
(clearinghouse, matching,
oracle, staking)"] STATE_HASH["StateHasher
(14 LtHash16 accumulators)"] end subgraph ReadPath["Read Path: Precompiles (0x0800+)"] direction TB PC_ADDR["Precompile Base: 0x0000...0800"] PROVIDER["L1StateProvider trait
(EngineStateProvider impl)"] PC1["0x0001: PERP_CLEARINGHOUSE
get_position(user, asset)"] PC2["0x0002: SPOT_BALANCES
get_spot_balance(user, token)"] PC3["0x0003: VAULT_EQUITY"] PC4["0x0004: STAKING_DELEGATIONS"] PC5["0x0005: ORACLE_PRICE
get_oracle_price(asset)"] PC6["0x0006: L1_BLOCK_NUMBER
get_l1_block_number()"] PC7["0x0007: MARK_PRICE
get_mark_price(asset)"] GAS["Gas: 2000 + 65 * (input_len + output_len)"] end subgraph WritePath["Write Path: CoreWriter (0x3333...3333)"] direction TB CW_ADDR["CoreWriter: 0x3333...3333"] CW_GAS["Gas: ~25,000"] CW_ACTIONS["15 Action Types:
PlaceOrder, CancelOrder,
VaultTransfer, Delegation,
SpotSend, UsdTransfer,
Withdraw, ApproveAgent,
CreateSubAccount,
SubAccountTransfer,
SetReferrer, UpdateLeverage,
UpdateIsolatedMargin,
SendAsset, ClaimRewards"] CW_EMIT["EVM Log -> CoreWriterEvent
decoded by L1"] CW_NOTE["Actions are INTENTS
not immediate state changes.
Failed actions don't revert EVM tx."] end subgraph EvmHash["EVM State Hashing"] direction TB EH_ACC["evm_accounts: LtHash16
addr(20) || balance(32) || nonce(8) || code_hash(32) = 92B"] EH_CON["evm_contracts: LtHash16
addr(20) || code(variable)"] EH_STO["evm_storage: LtHash16
addr(20) || slot(32) || value(32) = 84B"] EH_NOTE["Raw byte concat,
NOT msgpack serialization"] end subgraph CrossLayer["Cross-Layer Actions"] direction TB SEND_EVM["sendToEvmWithData
L1 -> EVM with calldata"] EVM_RAW["evmRawTx
raw EVM tx forwarded through L1"] EVM_MOD["EvmUserModify
system-generated position adjustment"] FIN_CON["FinalizeEvmContract
finalize EVM contract deployment"] end EXCHANGE -->|"Arc<Mutex<Exchange>>"| PROVIDER PROVIDER --> PC1 & PC2 & PC3 & PC4 & PC5 & PC6 & PC7 CW_EMIT -->|"decoded log"| EXCHANGE CW_ACTIONS --> CW_EMIT EXCHANGE --> EH_ACC & EH_CON & EH_STO EH_ACC & EH_CON & EH_STO --> STATE_HASH style L1 fill:#1c2128,stroke:#7c3aed,color:#c9d1d9 style ReadPath fill:#161b22,stroke:#30363d,color:#c9d1d9 style WritePath fill:#161b22,stroke:#30363d,color:#c9d1d9 style EvmHash fill:#1e1235,stroke:#7c3aed,color:#a78bfa style CrossLayer fill:#1c2128,stroke:#30363d,color:#c9d1d9
09 Validator Lifecycle
From registration through staking, active set membership, block production, jailing, and epoch transitions.
Code in
hl-engine/src/services.rs::StakingService.
Binary RE: c_staking(23) and staking(6) fields.
Undelegation cooldown: 7 days (~8.6M blocks at 70ms block time).
flowchart TD
REG["Registered
registerValidator"] STK["Staked
tokenDelegate"] ACT["Active set
minimum stake + not jailed"] PROD["Producing / voting
selected for round"] APP["VoteAppHash cadence
checkpoint voting"] JAIL["Jailed
self-jail or system jail"] JISO["Jailed isolated
isolated signer jail"] UNJ["Manual unjail
rate-limited by cooldown"] UNDEL["Undelegating
tokenDelegate undelegate"] UNLOCK["Unlocked
7 day cooldown elapsed"] EPOCH["Epoch transition
elapsed duration or ForceIncreaseEpoch"] RECOMP["Recompute active set
stake + jail filtering"] REG --> STK --> ACT ACT --> PROD --> ACT ACT --> APP --> ACT ACT --> JAIL ACT --> JISO JAIL --> UNJ --> ACT JISO --> UNJ ACT --> UNDEL --> UNLOCK ACT --> EPOCH --> RECOMP --> ACT META["State anchors
active_validators
broadcasters
stakes
delegations"] ACT -.-> META
registerValidator"] STK["Staked
tokenDelegate"] ACT["Active set
minimum stake + not jailed"] PROD["Producing / voting
selected for round"] APP["VoteAppHash cadence
checkpoint voting"] JAIL["Jailed
self-jail or system jail"] JISO["Jailed isolated
isolated signer jail"] UNJ["Manual unjail
rate-limited by cooldown"] UNDEL["Undelegating
tokenDelegate undelegate"] UNLOCK["Unlocked
7 day cooldown elapsed"] EPOCH["Epoch transition
elapsed duration or ForceIncreaseEpoch"] RECOMP["Recompute active set
stake + jail filtering"] REG --> STK --> ACT ACT --> PROD --> ACT ACT --> APP --> ACT ACT --> JAIL ACT --> JISO JAIL --> UNJ --> ACT JISO --> UNJ ACT --> UNDEL --> UNLOCK ACT --> EPOCH --> RECOMP --> ACT META["State anchors
active_validators
broadcasters
stakes
delegations"] ACT -.-> META
10 perpDeploy / HIP-3
HIP-3 permissionless perpetual market deployment. The
perpDeploy action contains
sub-actions for oracle configuration, margin table setup, and market registration.
Binary tag: "perpDeploy" (also referenced as "hip3Deploy").
Dispatch returns PerpDeployResult: Applied, Skipped, or Rejected.
Code in hl-engine/src/exchange.rs::process_perp_deploy().
flowchart TD
subgraph DeployerActions["Deployer Flow"]
direction TB
DEPLOYER["Deployer (user)"]
PD_ACTION["perpDeploy action
(HlAction::PerpDeploy)"] SUB_ACTIONS["Sub-Actions within perpDeploy"] end DEPLOYER --> PD_ACTION --> SUB_ACTIONS subgraph SubActionTypes["perpDeploy Sub-Actions"] direction TB SET_ORACLE["setOracle
configure oracle price source"] INSERT_MT["insertMarginTable
set margin tiers for new market"] SET_FEE["setFeeRecipient
deployer fee address"] SET_FEE_SCALE["setFeeScale
deployer fee rate"] REG_ASSET["registerAsset
register asset ID + metadata"] SET_MARGIN_IDS["setMarginTableIds
link asset to margin table"] SET_OI_CAPS["setOpenInterestCaps
max open interest limits"] SET_FUND_MULT["setFundingMultipliers
per-asset funding multiplier"] SET_MARGIN_MODES["setMarginModes
Enabled/Blocked/NoCross/StrictIsolated"] SET_ANNOT["setPerpAnnotation
market metadata/description"] end SUB_ACTIONS --> SET_ORACLE & INSERT_MT & SET_FEE & SET_FEE_SCALE & REG_ASSET SUB_ACTIONS --> SET_MARGIN_IDS & SET_OI_CAPS & SET_FUND_MULT & SET_MARGIN_MODES & SET_ANNOT subgraph Processing["process_perp_deploy()"] direction TB PARSE_SUB["Parse sub-action type"] RESULT["PerpDeployResult"] APPLIED["Applied
state updated successfully"] SKIPPED["Skipped(reason)
recognized but not wired"] REJECTED["Rejected(reason)
invalid parameters"] PARSE_SUB --> RESULT RESULT --> APPLIED RESULT --> SKIPPED RESULT --> REJECTED end subgraph StateEffects["State Effects"] direction TB ORACLE_UP["oracle.set_price(asset, px)
clearinghouse.set_oracle_price()"] MARGIN_UP["clearinghouse.margin_tables
insert new tier table"] FEE_UP["fee_schedule updates
deployer fee routing"] BOOK_UP["matching.get_or_create_book(asset)
initialize order book"] ROUTING_UP["routing.observe_universe()
register DEX universe"] MARK_UP["clearinghouse.mark_prices
initial mark from oracle"] end SET_ORACLE --> ORACLE_UP INSERT_MT --> MARGIN_UP SET_FEE --> FEE_UP REG_ASSET --> BOOK_UP REG_ASSET --> ROUTING_UP SET_ORACLE --> MARK_UP subgraph PostDeploy["Post-Deploy"] direction TB LIVE["Market live on HyperCore
tradeable via order action"] STALE["refresh_hip3_stale_mark_pxs()
begin_block effect 4:
refresh stale HIP-3 mark prices"] FUND["Funding enabled
via funding multiplier config"] end APPLIED --> LIVE LIVE --> STALE SET_FUND_MULT --> FUND style DeployerActions fill:#1c2128,stroke:#30363d,color:#c9d1d9 style SubActionTypes fill:#161b22,stroke:#7c3aed,color:#c9d1d9 style Processing fill:#1e1235,stroke:#7c3aed,color:#a78bfa style StateEffects fill:#161b22,stroke:#30363d,color:#c9d1d9 style PostDeploy fill:#1c2128,stroke:#30363d,color:#c9d1d9
(HlAction::PerpDeploy)"] SUB_ACTIONS["Sub-Actions within perpDeploy"] end DEPLOYER --> PD_ACTION --> SUB_ACTIONS subgraph SubActionTypes["perpDeploy Sub-Actions"] direction TB SET_ORACLE["setOracle
configure oracle price source"] INSERT_MT["insertMarginTable
set margin tiers for new market"] SET_FEE["setFeeRecipient
deployer fee address"] SET_FEE_SCALE["setFeeScale
deployer fee rate"] REG_ASSET["registerAsset
register asset ID + metadata"] SET_MARGIN_IDS["setMarginTableIds
link asset to margin table"] SET_OI_CAPS["setOpenInterestCaps
max open interest limits"] SET_FUND_MULT["setFundingMultipliers
per-asset funding multiplier"] SET_MARGIN_MODES["setMarginModes
Enabled/Blocked/NoCross/StrictIsolated"] SET_ANNOT["setPerpAnnotation
market metadata/description"] end SUB_ACTIONS --> SET_ORACLE & INSERT_MT & SET_FEE & SET_FEE_SCALE & REG_ASSET SUB_ACTIONS --> SET_MARGIN_IDS & SET_OI_CAPS & SET_FUND_MULT & SET_MARGIN_MODES & SET_ANNOT subgraph Processing["process_perp_deploy()"] direction TB PARSE_SUB["Parse sub-action type"] RESULT["PerpDeployResult"] APPLIED["Applied
state updated successfully"] SKIPPED["Skipped(reason)
recognized but not wired"] REJECTED["Rejected(reason)
invalid parameters"] PARSE_SUB --> RESULT RESULT --> APPLIED RESULT --> SKIPPED RESULT --> REJECTED end subgraph StateEffects["State Effects"] direction TB ORACLE_UP["oracle.set_price(asset, px)
clearinghouse.set_oracle_price()"] MARGIN_UP["clearinghouse.margin_tables
insert new tier table"] FEE_UP["fee_schedule updates
deployer fee routing"] BOOK_UP["matching.get_or_create_book(asset)
initialize order book"] ROUTING_UP["routing.observe_universe()
register DEX universe"] MARK_UP["clearinghouse.mark_prices
initial mark from oracle"] end SET_ORACLE --> ORACLE_UP INSERT_MT --> MARGIN_UP SET_FEE --> FEE_UP REG_ASSET --> BOOK_UP REG_ASSET --> ROUTING_UP SET_ORACLE --> MARK_UP subgraph PostDeploy["Post-Deploy"] direction TB LIVE["Market live on HyperCore
tradeable via order action"] STALE["refresh_hip3_stale_mark_pxs()
begin_block effect 4:
refresh stale HIP-3 mark prices"] FUND["Funding enabled
via funding multiplier config"] end APPLIED --> LIVE LIVE --> STALE SET_FUND_MULT --> FUND style DeployerActions fill:#1c2128,stroke:#30363d,color:#c9d1d9 style SubActionTypes fill:#161b22,stroke:#7c3aed,color:#c9d1d9 style Processing fill:#1e1235,stroke:#7c3aed,color:#a78bfa style StateEffects fill:#161b22,stroke:#30363d,color:#c9d1d9 style PostDeploy fill:#1c2128,stroke:#30363d,color:#c9d1d9