BLK ———

Maker onboarding

This guide takes you from a fresh wallet to a live, hedged quote. Everything here can also be done from the makers dashboard or with the SDK.

1. Use two keys

KeyHoldsCan do
Owneryour inventoryregister, deposit, withdraw, add or revoke operators, pause and unpause
Operatornothing (gas only)update quotes, pause your maker

The operator key lives on your quoting server and is treated as compromisable: it can never move funds. This mirrors Hyperliquid's agent wallets. Keep the owner key offline or in a hardware wallet.

2. Register and add an operator

import { makerClient } from '@quotient/sdk';

const owner = makerClient({ wallet: ownerWallet, chainId: 99801 });
await owner.registerMaker();
await owner.setOperator(operatorAddress, true); // the newest operator becomes your active quoter

Registration is permissionless. A maker has exactly one active quoter at a time. Adding a new operator makes it the quoter; revoking the active quoter falls back to the owner's own quotes. That makes key rotation instant: add the new key, then revoke the old one.

3. Deposit inventory

Inventory is per pool and per currency. Native HYPE is deposited with msg.value; ERC-20s need an approval for the hook first.

await owner.deposit(poolId, USDC, 5_000n * 10n ** 6n);
await owner.deposit(poolId, NATIVE, 50n * 10n ** 18n); // sends 50 HYPE as value

Deposits are converted into ERC-6909 claims inside the Uniswap v4 PoolManager and credited to you. Withdraw any time with withdraw(poolId, currency, amount, to), even while the venue or your maker is paused. On mainnet, deposits are capped per maker and per pool during the launch period.

4. Post quotes

A quote is one 32-byte slot per pool: mid price, bid and ask spreads, a per-block size cap, a linear size-impact slope, a per-block spread decay, a time-to-live in L2 blocks, a sequence number and flags. See the quote encoding spec for every bit.

import { quoteFromHuman, encodeQuote } from '@quotient/sdk';

const slot = encodeQuote(quoteFromHuman({
  price: 77.21, decimals0: 18, decimals1: 6,
  bidBps: 2, askBps: 2,
  maxFill: 250,            // HYPE per L2 block
  impactAtMaxBps: 6,       // +6 bp at a 250 HYPE fill, linear in size
  decayBpsPerBlock: 0.5,   // +0.5 bp per block of quote age
  ttlBlocks: 20,           // fillable for 20 blocks after inclusion
  seq: 1,
}));
await operator.updateQuotes([{ poolIdx: 0, slot }]);

The contract stamps each slot with the L2 block it was included in, read from ArbSys.arbBlockNumber() (on Elysium, Solidity's block.number is the parent chain's block). Age, expiry and strict mode are measured from that stamp, so you never guess your inclusion block.

Cost

Through the packed entry (updateQuotesPacked344079(), the SDK's default) one pool costs about 29,300 gas including the 21,000 intrinsic; each extra pool in the same batch adds under 6,000. At testnet gas prices (0.1 gwei) that is about 0.000003 HYPE per update. The status page shows the measured numbers.

Choosing protections

ProtectionWhat it doesSuggested start
ttlBlocksquote unfillable after N blocks10–30
decayPerBlockspread widens with every block of age0.3–1 bp
maxFilltoken0 you'll fill per L2 block0.5–2x your typical hedge size
impactAtMaxextra spread at a full-size fill2–5x your base spread
strict modeonly fillable in the update block or the nexton, if you refresh every block

The reference bot's defaults come from experiment E5. A sniper watched Hyperliquid and hit stale quotes while the maker's updates were stalled for 20 seconds every minute; each configuration ran interleaved on testnet. This is a partial run, 10–15 minutes per configuration, because the testnet halted mid-experiment.

ProtectionsSniper profit (= maker loss) per hour
none$13.53
expiry (40 blocks)$3.51
expiry + 0.5 bp/block decay$0.00
+ per-block cap$0.11
strict mode$0.00, at twice the update traffic

Expiry alone is not enough: a quote stays snipeable for its whole lifetime. Age decay is what made stale quotes unprofitable. The bot ships with a 40-block TTL, 0.5 bp/block decay and a per-block cap sized to its hedge capacity, with strict mode off.

5. Priority fees and ordering

Elysium orders transactions by priority fee inside each block, with no lane for quote updates. A swap that pays more than your update can execute against your previous quote in the same block.

Measured on testnet (experiment E3, 200 simultaneous update/swap pairs, 99% in the same block):

  • 1 wei decides it. With the update tipping 1 wei more, it executed first in 97% of same-block pairs; with the swap tipping 1 wei more, the update went first in only 5%; at 10× the tip, 100%.
  • Equal tips are a coin flip. At equal tips the order was close to arbitrary (69% / 50% for the update depending on which request left first), not first-come-first-served.
  • Tips are charged in full. The effective gas price is the base fee plus the whole tip.

The reference bot therefore tips 1 wei on quote updates (BOT_PRIORITY_FEE_WEI): it wins ties against takers who tip nothing, at no meaningful cost. Any taker can outbid it, so expiry, age decay and the per-block cap are what actually bound adverse selection. Testnet ordering may differ from mainnet and will change when Elysium's keeper lane and Timeboost ship.

There is no public mempool: you cannot see pending swaps. Watch new blocks (newHeads) and receipts.

6. Run the reference bot

The open-source bot in packages/bot quotes from Hyperliquid's order book, pushes one batched update per block when the mid moves past a threshold, detects fills from receipts, and hedges the net delta on Hyperliquid through a trade-only agent wallet. It has a kill switch: pnpm --filter @quotient/bot kill pauses your maker on-chain and cancels open Hyperliquid orders.

7. Pause and withdraw

Your operator can pause your maker instantly (for example on a stale price feed). Only the owner can unpause. Withdrawals are never blocked: not by your pause, not by a pool pause, not by the guardian.