Skip to main content

Activation notice — block 25,599,540

info

LIVE since block 25,599,540. Node 0.9.16 swapped in at block 25,599,539 on 2026-10-04, and every node rule below turned on one block later. Gateway 0.9.16 went live in the same window, a few minutes after the node swap. The gateway rows below shipped with it. This page is the record of what changed at that boundary.

{"type":"account_state","address":"0x…"} carries the live height if you need to check where the chain is.

Every change below moves at ONE height, so the chain gains one boundary for the whole release.

Why one block above the swap. The outgoing node commits block 25,599,539 and only then halts, so that block still ran the old rules. The new rules apply from the first block after it.

order_status answers for a batch_cancel leg​

SurfaceBefore the heightNow
order_status for an order a batch_cancel leg removedunknowncanceled

Why. A batch_cancel carries a verdict per leg, so the node can prove which legs removed an order. It records only those. A refused leg leaves the order's earlier terminal state untouched.

What to do. Nothing.

contractAddress on a deployment receipt​

SurfaceBefore the heightNow
eth_getTransactionReceipt contractAddress for a successful deploymentnullthe address of the deployed contract

Why. The node derives the address from the sender and the nonce when it stores the receipt of a successful deployment. A call, a failed deployment and a receipt stored before the height keep null. There is no backfill.

What to do. For a receipt stored before the height, compute the address locally from the sender and the nonce.

mtfStatus for two transactions at one nonce​

One EVM block can hold two transactions from one sender at the same nonce. When the node refuses the first before it runs (for example insufficient_funds), the nonce stays free, and the second transaction runs.

SurfaceBefore the heightNow
mtfStatus and status of the second transactionbad_nonce, 0x0what really happened to it, for example success, 0x1

Why. Before the height, the node counted the refused transaction as if it used the nonce.

What to do. Nothing. A bad_nonce receipt from before the height, next to a refused transaction at the same nonce, can be wrong. Read the sender's nonce or the contract code to confirm.

An open-interest cap on every native perp market​

SurfaceBefore the heightNow
markets_meta oi_cappresent only on a market with a governance-set cap. No market had one, so every market was uncappedpresent on every native perp market: the lower of the governance-set cap and the capacity cap. On a deployer market, the cap its deployer set: see below
markets_meta oi_cap_usd and oi_cap_boundabsentpresent with oi_cap. oi_cap_bound reads "deployer" on a deployer market
markets_meta max_market_order_ntl and active_asset_data max_trade_sizenull on every marketa number on every native perp market that has a mark, and on every deployer market that has a cap

Why. A liquidation can leave a deficit, and the protocol pays it from the insurance fund and its other backstops. With no cap, open interest had no bound against that money. The chain now derives a cap from what the backstops can pay, and it recomputes the cap every block. No vote was needed: the cap applies from the height. See how the capacity cap works. The capacity cap covers native perp markets only. The Metaliquidity vault backstop never takes a deployer market's risk, so a deployer market gets its cap from its deployer instead.

What to do.

  • Keep the null branch for max_market_order_ntl and max_trade_size. A market that has never had a mark still reads null.
  • Expect MARKET_OI_CAP on an order that opens, extends or flips a position and is priced through the committed mark, while the market is at its cap or when the order's new exposure would pass the cap. On a self-priced market, every such order is refused. A passive order rests, and the chain cancels it if the mark moves through it while the market is at its cap. An order that can only close its owner's position passes.
  • Do not cache oi_cap. It changes as the capacity and the mark change.

The cap never closes a position.

A deployer sets its market's open-interest cap​

SurfaceBefore the heightNow
perp_set_oi_capunknown variantaccepted from the market's deployer, or from a delegate that holds bit 9
perp_set_sub_deployer_perms permissions with bit 9 setrefused: bits 9-15 were reservedaccepted. 1023 is every bit
The mask of a delegate added with perp_set_sub_deployers5111023
perp_activate_market on a market that has a capset the cap to the governance default max_oikeeps the cap. Only a market with no cap starts at max_oi
markets_meta oi_cap_bound on a deployer marketabsent"deployer"
A Metaliquidity vault order that opens, extends or flips a position on a deployer marketacceptedrefused, PRECONDITION_FAILED: metaliquidity vault cannot open or extend a position on a MIP-3 market

Why. A deployer market prices from its own deployer, and the protocol's backstops never take its risk. The capacity cap measures what those backstops can pay, so it does not fit a deployer market. The deployer owns the market's risk, so the deployer sets the cap. For the same reason, the Metaliquidity vault does not trade a deployer market. Its depositors did not deposit to carry a price the protocol does not control.

What to do.

  • As a deployer, set your cap with perp_set_oi_cap. The cap is in whole units of the base asset, not lots and not USD. A market you did not set carries the governance default it started at.
  • Send 0 to remove the cap. Activation fills an empty cap with the governance default, so send 0 again after you deactivate and activate the market.
  • To let a delegate set the cap, grant bit 9. A delegate added with perp_set_sub_deployers holds every bit, bit 9 included.
  • Accept "deployer" as a value of oi_cap_bound.
  • A lower cap closes no position. It stops new exposure only, by the at-cap rules.
  • The reference market maker refuses to start when its list names a deployer market. Remove such a market from the list.

A deficit is charged to the market that produced it​

SurfaceBefore the heightNow
The insurance fund a cross account's deficit drawsthe fund of one market: the highest asset id among the markets the liquidation touchedthe fund of each market where the account realized a loss in the liquidation run, in proportion to that loss

Why. The open-interest cap on a native market is sized from the insurance fund of that market. A loss on one market must not drain the insurance fund of another market. See which market pays.

What to do. Nothing. No action or read changes.

Liquidation and price rules​

SurfaceBefore the heightNow
The ADL haircutreached only the gains that the netting step itself realizedreaches every gain realized on the market in the current 60-second window or the one before it: any fill, netting at mark or a delisting settlement. It never takes a winner below its maintenance margin
The index of a self-priced marketthe mid of the best bid and the best ask over every resting order, whatever its agea mid of the bid and ask VWAPs over rested depth only, smoothed over several updates
The 60 s count that marks an oracle price staleran only while at least one asset got a fresh priceruns on the block clock, so it also runs when no asset gets a fresh price
A delisting vote that names no priceclosed every position at the risk markcloses every position at the six-hour average, moved into the maintenance band of the risk mark, while the oracle price is fresh
A delisting vote that names a priceany pricerefused when the price is more than 20% from the six-hour average and outside the maintenance band, while the oracle price is fresh

Why. Each rule closes a way to move a price, or a loss, onto other traders. A short burst of orders no longer moves a self-priced index, and a delisting settles near a recent average rather than at one instant.

What to do. Nothing. No action shape changes. A delisting vote that names a price outside both bands is now refused before it counts.

A bridge re-issue lane and spot tokens as portfolio-margin collateral​

SurfaceBefore the heightNow
A stranded_on_retired_domain withdrawalno recovery lanegovernance can re-issue it with a new nonce
A disputed withdrawal that reads releasedno recovery lanegovernance can re-issue it the same way
bridge_withdrawal_history after a re-issue—a NEW entry: same amount_units and dst_addr, a higher nonce, awaiting_cosignatures. The old entry stays terminal
markets_meta spot.tokensa spot token that governance registers was absentthe token appears, with its size and wei decimals
A spot token as portfolio-margin collateralnever countedcounts when governance sets a positive collateral weight for that token and its price perpetual, native, listed and not self-priced, gives its mark. No token has a weight yet
The perpetual that gives a collateral token its mark—the perpetual governance names for the token or, when none is named, the perpetual with the same symbol. A token whose symbol differs from its perpetual, such as a bridged gBTC priced by BTC, needs the named mapping
node_gov asset on a pm_collateral_haircut or pm_collateral_price_asset vote—a spot token id, not a market id
node_gov changes[].field on a SetDynamicRiskParam enactment—can be pm_collateral_price_asset, the perpetual that gives the token its mark
node_gov category on a re-issue vote—bridge_reissue
An unsigned queued withdrawal that must not payno lanegovernance can void it and refund the full debit, fee included
bridge_withdrawal_history after a void—the entry finishes with status: "voided" and open: false
node_bridge_outbox removed record after a void—status: "voided"
node_gov category on a void vote—bridge_void
A queued withdrawal while withdrawals are haltedvalidators kept signing itvalidators sign nothing until the halt lifts; it stays awaiting_cosignatures
node_gov category on an ArmFeatures or SPAN shock-grid votecircle_promotion_attestarm_features or pm_shock_grid
node_gov category on a collateral weight or price-perpetual vote—pm_collateral, not dynamic_risk. coin is empty: asset is a spot token id
A SetDynamicRiskParam vote that sets a collateral field AND a market setting—refused. A collateral vote carries only collateral fields

Why. A deployment rotation can strand a withdrawal, and a dispute on the destination contract can make one unpayable. Before the height, neither had a recovery lane. A re-issue pays the original withdrawal once, on the destination chain. It never debits the user again and never credits the user on the exchange. Governance re-issues a withdrawal once. A withdrawal that no validator has signed can instead be voided: governance removes it and refunds the full debit on the exchange, and nothing pays it on the destination chain.

A spot token takes its mark from its price perpetual: the one governance names for the token or, when none is named, the perpetual with the same symbol. So the collateral and a position on that perpetual share one price in the scenario grid. A self-priced perpetual has no external price, so its token never counts.

What to do.

  • Before any re-issue, confirm on the destination chain that the old withdrawal was not paid. See the danger box.
  • After a re-issue, track the NEW entry. Its message_id is new.
  • Read the collateral weight per token, not per market. The planned weights are 0.95 for BTC and ETH and 0.8 for SOL and BNB, once those spot tokens exist. They are planned, not set.
  • Expect no credit from a token while the oracle of its perpetual is stale, or before the oracle has sourced a price for it.

node_gov labels every governance round​

SurfaceBefore the heightNow
node_gov category on a fixed-round vote with no label of its own, for example DisableDexthe label of the nearest labeled base below it, such as oracle_weightsthe label of its own round, such as disable_dex
node_gov sub_id on that votethe distance from that lower base, such as 120000000
node_gov round on a Listing, MintTreasury, BurnTreasury or SetPopulationTarget votea round that another action also used: 13,000,000, 13,000,000, 14,000,000 and 15,000,000, in that ordera round of its own: 48,000,000, 49,000,000, 50,000,000 and 51,000,000, in that order

Why. Before the height, a node labeled only eight bases. Thirty fixed rounds had no base of their own, so each one read as a different vote kind. The category table gives every round its own label.

Every round now has one action. Before the height, three rounds were shared by more than one action. A validator has one vote slot per round, so its vote for one of those actions replaced its vote for the other.

What to do.

  • Filter a vote kind by action. action is the exact filter on every record.
  • Do not split a vote kind by category on a record written before the height. The old label on those records does not change.

Reads after the fill-tape retirement​

From this height, the node keeps no committed fill tape: no trade ring for a market and no fill ring for an account. The node_trades and node_fills streams do not change, so the archive keeps every print. The reads below read the archive or the gateway's own 24-hour trade window instead of the node rings.

SurfaceServed byNow
markets day_ntl_vlm, perp and spotnodeA node holds no 24-hour window. It serves "0" with day_ntl_vlm_lower_bound_from equal to the top-level time
markets timenodeNew top-level field: the block time of the read
markets day_ntl_vlmgatewayThe gateway replaces day_ntl_vlm with the sum from its own window. It removes day_ntl_vlm_lower_bound_from only when its data covers the whole 24 hours. Otherwise the marker is the oldest instant its data covers
markets spot prev_day_pxgatewayWhen the gateway data covers the whole 24 hours, the price of the first print in that window
WebSocket markets day_ntl_vlm and spot prev_day_pxgatewayThe same two rules on every snapshot and delta row. The window ends at the row's time
trades, un-rangedgatewayThe ask also reaches the archive. The answer merges the node answer, the gateway window and the archive, with no duplicate tid
trades, a row from the gateway windowgatewayNo hash key and no block key, the same as an archive row that has no block
WS trades on-subscribe snapshotgatewayThe node snapshot is empty, so the gateway serves the snapshot from its own window
user_twap_slice_fillsgatewayThe answer merges the node answer with the archive fills of the same address that carry a twap_id
order_status for a filled ordergatewayThe legs come from the archive when the ask carries address
WS l2_book and bbo time on a spot pairnodeThe time of the newest print the serving node saw since it started. 0 until the first print

Why. The node rings were bounded, and they were part of the committed state. The archive and the gateway already held the same prints, and they hold more of them. A ring of 256 prints covered minutes on a busy market, so the gateway's 24-hour window is the fuller figure.

What to do.

  • Read a markets row that still carries day_ntl_vlm_lower_bound_from as a lower bound. A marker within seconds of the current time means the serving layer held no window: "0" is then no data, never a quiet market.
  • Send address on order_status. Without it, the answer carries no fills, and a filled order can answer unknown.
  • Keep reading trades rows with and without hash. A missing hash means "not recorded".
  • Expect no type change. No field changes its type, and no field is removed.

user_ledger_updates is removed​

SurfaceBefore the heightNow
POST /info user_ledger_updates200 with updates: []410 with code: "UNKNOWN_TYPE" and details.use: "user_non_funding_ledger_updates". A node that you call directly answers 400 UNKNOWN_TYPE

Why. This read could only answer []. The node keeps no per-account ledger history. The archive keeps every balance movement, in the stream's own record shape: a signed delta and a token coin. user_non_funding_ledger_updates serves those records. One question, one read.

What to do.

  • Call user_non_funding_ledger_updates for the balance ledger history of an account.
  • Read delta as a signed change, not as an unsigned amount.
  • For live balance movement, keep the ledger_updates WS channel. It does not change.

Referral codes, a referee discount and caps​

The height changed no fee. Each new parameter started at a value that reproduced the old behaviour: a 10% share, no discount, codes off, no cap. Governance turns the program on with votes. Two rules changed at the height itself, with no vote: a liquidation fill pays no referrer share, and an account that already has referees cannot bind to a referrer.

SurfaceBefore the heightNow
register_referral_codeunknown variantaccepted while codes are on. Refused with referral codes are not enabled while they are off
set_referrer_by_codeunknown variantbinds the sender to the account that holds the code
set_referrer from an account that has refereesacceptedrefused: an account with referees cannot set a referrer
set_referrer or set_referrer_by_code from an account that holds a referral code—refused: an account with a referral code cannot set a referrer
set_referrer to an address with no referral code, while codes are on—refused: referrer has no referral code
The referrer sharea fixed 10% of the taker feereferrer_share_bps of the taker fee actually paid. Default 1000, which is 10%
The referee discountnonereferee_discount_permille off the taker rate. The larger of it and the staking discount applies, never their sum. Default 0
The share and the discount after the caps—each stops when the referee's taker volume since the bind reaches its cap. Default 0, which is no cap
A liquidation fill of a refereepaid the referrer sharepays no share and gets no referee discount
vote_global kinds 131 to 135unknown kindset_referrer_share_bps, set_referee_discount_permille, set_referral_code_min_volume_usd, set_referee_discount_cap_usd, set_referrer_reward_cap_usd
fee_schedulereferrer_share_bps onlyalso referee_discount_permille, referral_code_min_volume_usd, referee_discount_cap_usd, referrer_reward_cap_usd, and user.referee_discount_permille
referral_stateuser, address, claimable_rewards, referreralso referrer_code, code, referee, referrer_stats, code_requirement
referral_code, referral_referees, referral_leaderboardUNKNOWN_TYPEnew reads
node_actions action_type—can be RegisterReferralCode or SetReferrerByCode

Why. A referrer can now hand out a short code instead of an address, and a referee can see its discount, its counters and its caps. Governance can set the share, the discount and the caps, so the program changes without a release. The code minimum makes each referrer identity trade before it earns, and while codes are on, a bind by address needs a code holder, so a trader cannot bind a fresh address to itself for free. Referrals are single-level: before the height the node checked only one direction, and now it checks both. A liquidation fill is not flow the referrer brought, so it pays no share. See the referral program.

What to do.

  • Bind by code. Governance turned the program on 2026-10-05, with a 10% share, a 50‰ (5%) discount, a 10,000 USDC code minimum, a 25,000,000 USDC discount cap and a 1,000,000,000 USDC share cap. Since then, set_referrer to an address that holds no code fails with referrer has no referral code. Read the values in force from fee_schedule.
  • Send a referral code in lowercase. The node refuses an uppercase letter and does not fold it.
  • A binding made before the height keeps its referrer. It reads bound_ms: 0 and zero counters, and its caps count from the height.
  • referral_referees lists every referee, a referee bound before the height included. Its counters start at the height.

One claim for the referral and broker credits​

SurfaceBefore the heightNow
claim_referral_rewardsdrained the referral credit onlydrains the referral credit and the broker-code credit
claim_broker_rewards and claim_builder_rewardsdrained the broker-code credit onlydrain the broker-code credit and the referral credit
node_actions result of a ClaimReferralRewards or ClaimBuilderRewards rownullclaimed, referral and broker, in USDC
referral_state referrer_stats.claimed—grows by the referral part only, whichever action the sender used

Why. One fill can pay an account a referral credit and a broker credit. A trader who earns both wants one claim, not two. No action is added, no action is removed and no signing type changes. Both actions now do the same thing.

What to do.

  • Show one claimable figure: the sum of claimable_rewards from referral_state and from broker_state.
  • Send one claim action. A second claim is harmless: it claims 0.
  • The /exchange reply still reports no amount. Read the amount from the claim's node_actions row.