The loop
One cycle, repeated: take a reference price, compute the ladder, compare it with your live orders, send the difference as one atomicbatch.
What you build:
1. Account and key
- Use a dedicated account. An implicit account (the hex of an ed25519 public key) works with
intents.nearas is; a named account first registers its key withintents.nearadd_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:tick_size away from the market: bids down, asks up.
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 yourintents.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:
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.nearreceipt’sorder_createdandplace_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;pruneis 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 incanceland the full ladder inplace. amountandpriceare JSON strings.
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
internalyour internal balance moved by the proceeds. - Read the events.
tradelists every maker fill (f) and closed order (c);place_skippedtells 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.