A market maker on hexbook is a handful of contract calls and signed intents. This page shows how they fit together, so you can build one in any language.

The loop

One cycle, repeated: take a reference price, compute the ladder, compare it with your live orders, send the difference as one atomic batch. What you build:

1. Account and key

  • Use a dedicated account. An implicit account (the hex of an ed25519 public key) works with intents.near as is; a named account first registers its key with intents.near add_public_key.
  • One key signs every intent. Keep it out of code and config files.
  • Submitting through the solver relay needs a NEAR Intents API key; submitting through your own transactions needs NEAR for gas (reserve).

2. Prices and amounts

The contract works in raw units. Convert with integer or decimal arithmetic, never floats:
Round to the book’s tick_size away from the market: bids down, asks up.
Sizes: an ask’s amount is base, a bid’s is quote (price × the base you want to buy). Each must be at least the book’s min_order_amount for its side (get_active_pairs).

3. Funding

Your tokens must first be in your intents.near balance: deposit them into NEAR Intents (see Deposits in the NEAR Intents docs). Then move inventory from intents.near into the book’s internal balance once, with a deposit per token:
Then set payout mode internal, so fills come back to the same balance and back new orders without another deposit.

4. Quoting with batch

Keep the ladder you want as a list of { side, price, amount } (prices already on the tick, amounts above the minimum, at most one order per side and price) and compare it with your orders in the book. Read them with get_orders_by_owner, which returns dead orders too, and get_order_meta for liveness and expiry:
  • One batch per tick: the whole requote lands or none of it. Wait for its outcome (the hexbook.near receipt’s order_created and place_skipped) before the next one.
  • Requote only when it matters (the fair moved more than a fraction of your half-spread, or a level filled): an unchanged order keeps its time priority.
  • Every order carries expires_at, a few tick intervals out: if your process stops, the ladder dies on its own. Re-place orders that expire within about a tick.
  • Close your dead orders. Expired and stale-epoch orders still count toward the 200-per-owner and 500-per-level limits and the 128-order live-price scan. List them in the same batch’s cancel (it accepts dead orders), as above; prune is a fallback. A hash that was closed meanwhile (a trade met it, someone pruned it) makes the batch panic: read your orders right before building it.
  • Replace everything with bump_epoch: true, the old hashes in cancel and the full ladder in place.
  • amount and price are JSON strings.
The batch is an auth_call with msg {"action":"batch","data":{...}}, signed with NEP-413.

5. Reading your state

6. Tracking fills

Use both:
  • Diff your state between ticks. An order that is gone or smaller was filled or closed; with payout mode internal your internal balance moved by the proceeds.
  • Read the events. trade lists every maker fill (f) and closed order (c); place_skipped tells you a placement did not rest.

7. Lifecycle

The whole life of a market maker, in order:
1

Account and key

A dedicated account whose key intents.near knows (1).
2

Fund

Deposit the tokens into NEAR Intents, then deposit each into the book’s internal balance (3).
3

Payout mode

set_payout_mode internal, so fills come back into the balance your batches spend.
4

Post the ladder

batch with bump_epoch: true, your old hashes (if any) in cancel and up to 32 placements with a short expires_at.
5

Requote

Each tick: read your orders, diff, one batch with the stale, expiring and dead hashes in cancel and the new placements; wait for its outcome (4).
6

Kill switch

cancel_all (or a batch with bump_epoch: true and no places) when the reference is stale or jumps, or a limit trips (Risk). Then cancel the now-dead hashes in your next batch.
7

Limits

Cap what you hold per asset and stop quoting the side that would grow past it.
8

Exit

cancel_all, a batch cancelling every remaining hash (or prune), then claim_balance (every token in one call) or withdraw per token.
Details in Risk.