---
name: hodldex-trading
description: >-
  Trade spot and perpetual futures on HODLDEX, a trustless DEX on the Nano
  ledger. Use when asked to check a token price, market depth, open interest or
  funding on HODLDEX; to buy, sell, stake or transfer a HodlGame token; or to
  open, monitor or close a leveraged long/short position. Also covers reading a
  wallet's positions and computing liquidation prices.
license: Apache-2.0
---

# Trading on HODLDEX

HODLDEX is a terminal for a token economy that lives entirely on public Nano
blocks. Nano has no smart contracts, so the ledger is not *executed* — it is
**replayed**. Every operation is one signed Nano block whose 32-byte `link`
field carries the payload, and anyone who folds those blocks in canonical order
reaches a byte-identical state. That is the whole trust model.

Consequences you must internalise before trading:

- **There is no exchange account and no deposit.** You hold a Nano seed. Every
  action is a block you sign yourself.
- **Nothing custodies your money.** On zero-custody markets your XNO never
  leaves your wallet; it is *earmarked* as collateral against a signed balance.
- **There is no admin who can reverse anything.** A broadcast block is final.
- **Reads may lag writes.** The ledger confirms in under a second; the indexer
  that serves prices replays on its own cadence. After a write, poll — do not
  assume a read taken immediately afterwards reflects it.

Base URL: `https://www.hodldex.app`

## 1 · Read the market

```bash
# every market, newest state
curl -s https://www.hodldex.app/api/state | jq '.tokens[] | {symbol, price, marketCap, direct}'

# one market in full: price series, trade tape, holders, live futures book.
# Pass an account to get that wallet's balance, stake, collateral and positions.
curl -s "https://www.hodldex.app/api/token?token=<tokenId>&account=<nano_...>"

# the running competition and its leaderboard. No entry step exists: trading
# inside the window is what enters you, and the table is folded from the public
# event feed on every read rather than stored, so you can recompute it yourself.
curl -s https://www.hodldex.app/api/competition | jq '{phase: .competition.phase, top: .standings[0]}'
```

Every numeric field crossing the wire is a **decimal string of a raw integer**,
never a JSON number — a JSON number cannot hold 30-decimal XNO without silently
losing precision. Parse with a bigint, always.

| scale | meaning |
|---|---|
| XNO amounts | raw, 10^30 per XNO |
| token amounts | raw, 10^`decimals` per token (`decimals` is pinned on-chain at launch) |
| `price` | XNO raw per **whole** token |
| `futures.spot`, `futures.twap` | ×10^12, XNO raw per **raw** token unit |

Key fields on a market:

- `direct` — `true` means zero custody: no pool account exists at all.
- `poolXno` / `poolTokens` — the constant-product reserves. On a `direct`
  market these are a **declared virtual curve**: they set the price, they are not
  a pile of money someone is holding.
- `queueHead` — a seller waiting to be paid. If set, your buy pays them
  wallet-to-wallet; if null, your XNO stays in your own wallet as collateral.
- `myFloor` — XNO you must keep in your wallet to back your position. **Sending
  below this shrinks the position it backs.** Nothing stops the send; the ledger
  simply voids the collateral proportionally.
- `futures` — `spot`, `twap` (the mark), `book` (resting orders), `pairs`
  (open positions), `recent` (settlement receipts).

## 2 · Quote before you trade

The AMM is constant product with a **1% fee on buys**, retained in the reserves.
There is no sell fee.

```
buy:   out = (xno × 0.99 × poolTokens) / (poolXno + xno × 0.99)
sell:  out = (tokens × poolXno) / (poolTokens + tokens)
```

Price impact of a buy of `a` XNO is `((X + 0.99a)/X)² − 1`. **Compute this before
every trade.** These markets vary in depth by orders of magnitude — the same
1 XNO order is 0.2% on one market and 900% on another. Refuse to trade, and say
why, when impact exceeds what the user asked for.

To find the size that moves price by `r`: `a = X(√(1+r) − 1)` to lift it,
`t = T(1/√(1−r) − 1)` to drop it.

## 3 · Write: every op is a signed block

An op rides in the block's `link`:

```
[ opcode: 1B ][ tokenId: 16B ][ amount: 15B ]
```

Ops whose payload does not fit (a transfer's 32-byte recipient, any op with two
amounts) ride as a **fragment pair**: two blocks, strictly chained, where
`fragB.previous === fragA.hash`. **Never let any other block land between them** —
a replayer binds fragment B positionally, so an interleaved block silently voids
the whole op with no error anywhere.

Opcodes: `launch 0x01`, `transfer 0x02`, `buy 0x03`, `sell 0x04`, `stake 0x05`,
`unstake 0x06`, `claim 0x07`, `seedLiq 0x08`, `addLiq 0x09`, `withdraw 0x0a`,
`launchDirect 0x0b`, `futOpen 0x0c`, `futClose 0x0d`.

Broadcast through the whitelisted RPC proxy:

```bash
curl -s -X POST https://www.hodldex.app/api/rpc \
  -H 'content-type: application/json' \
  -d '{"action":"account_info","account":"nano_..."}'
```

Allowed actions: `account_info`, `account_history`, `blocks_info`,
`block_info`, `pending`, `receivable`, `work_generate`, `process`.

**Poll gently.** The node plan behind this endpoint enforces a hard ceiling of
10 requests/second and answers a breach with
`{"error":429,"limit_type":"PLAN_BURST_PER_SECOND"}`. Honour `retry_after`
rather than retrying into it — repeated breaches escalate to a multi-day IP ban.

For BALANCES, use `GET /api/balances?account=nano_...` instead of polling
`account_info`. It answers `{balance, receivable:{count,totalRaw}}` and folds
every caller in the same moment into one batched node call, so it costs the
network a fraction of what per-account polling does. Use `account_info`
directly only when you are about to BUILD A BLOCK and need the live frontier —
a stale frontier forks your chain, so that read is never cached or batched.

Read endpoints accept `?fresh=1` to bypass every cache layer, including the
upstream fold. It is for reconciling after a write; it forces a full chain
replay, so do not poll with it.

**Proof of work is yours to produce.** `work_generate` may refuse; when it does,
compute the nonce locally and verify it before use:
`blake2b-8(work_le ‖ root)` read as a little-endian u64 must be ≥ the threshold
(`fffffff800000000` for sends, `fffffe0000000000` for receives).

The reference implementation of all of this is
[`app/lib/chain.ts`](https://github.com/dhyabi2/hodldex) — read it rather than
reimplementing the encoding.

## 4 · Spot

**Buying** takes one of three paths, and you must pick by inspecting the market:

1. `direct` **and** `queueHead` set → send the XNO straight to the queued
   seller, then chain a buy op after it. Cap the spend at what they are owed.
2. `direct` **and** no queue → nothing moves. Declare the amount in a fragment
   pair; the network checks it against your signed block balance. Your XNO stays
   in your wallet as collateral and your `myFloor` ratchets up.
3. not `direct` (legacy pooled) → send XNO to `pool`, then chain the buy op.

**Selling** on a direct market settles its principal instantly from your own
collateral; only realised appreciation queues, to be paid by later buys with a
coverage haircut. Quote both parts before selling and tell the user which is
which — "you will receive X now and Y when buyers arrive" is the honest framing.

Always set a slippage bound. It becomes an on-chain `minTokens` / `minXno`, not
a UI check.

**A slippage of 0 means "at least the quoted amount", not "no bound".** Passing
0 to get the tightest possible fill is the natural reading and it is now the
true one; earlier it encoded an UNBOUNDED order, which was the most dangerous
setting available and looked like the safest.

## 5 · Perpetual futures

Token-margined inverse contracts. **Margin is the token itself**, taken from your
spot balance — which is what makes it enforceable by replay with no escrow and no
oracle. Buy the token before you can open a position.

```
long  pnl = size × (price − entry) / price
short pnl = size × (entry − price) / price
```

Rules that will reject your order if you ignore them:

| rule | value |
|---|---|
| max leverage | 5× (`size ≤ margin × 5`) |
| minimum position | `supply / 10000` |
| maintenance margin | 5% of size — below it, at **both** spot and mark, the pair liquidates |
| closing fee | 0.5% of closed size, paid by the **closer** to the counterparty |
| open interest cap | per side, `min(25% of supply, 10% of pool tokens)` |
| resting orders | 32 per side per market, **2 per account** |
| price guard | **mandatory** — an unguarded open is rejected outright |

**Not every market can carry a perpetual.** The smallest legal position is
`supply / 10_000`, and it must fit inside the open-interest cap, which is 10%
of the tokens in the curve. On a market where it does not, the chain rejects
every order at any margin and any leverage — check this before offering a
position, rather than reporting a failed broadcast.

**Leverage is not symmetric between the two sides of a pair.** A leg's maximum
LOSS is its own margin; its maximum GAIN is the counterparty's. So a maker
quoting at 2× against a taker at 5× hands that taker 2.5:1 risk/reward, and two
accounts holding both sides can sit flat between themselves, drag the mark
either way, and extract the difference with no directional view. If you quote,
quote at the same leverage your counterparty may use. If you take, this
asymmetry is yours to collect — and it is the reason a maker should not
under-lever.

**The guard is not optional and not cosmetic.** A single large swap dislocates
spot away from the mark, and the taker is always given the worse of the two — so
an unguarded order can be harvested by a maker who moved the price on purpose.
A long fills only at `entry ≤ guard`; a short only at `entry ≥ guard`.

**The mark price has no clock.** A time-weighted average would be
non-deterministic here — a Nano block's timestamp is when a node first saw it, is
not covered by the signature, and differs between nodes, so a TWAP would make two
honest replayers disagree. The reference instead moves with **volume**:
`mark += (spot − mark) × min(1, 4 × moved / pool)`. Moving it takes a comparable
fraction of the pool's own liquidity through the curve, paying the fee and the
slippage both ways. Do not expect it to track the last tick.

Liquidation price for a position:

```
long:   P ≤ size × entry / (margin + 0.95 × size)
short:  P ≥ size × entry / (1.05 × size − margin)
```

Positions are strictly **paired** against a real counterparty; an open matches
FIFO against resting opposite-side orders and the remainder rests. `futClose`
with size `0` cancels every resting order **and** closes every position. Closing
is FIFO: closing your third position also closes the first two.

**The most anyone can lose is the margin they locked.** A winner is never owed
more than the loser actually posted, which is why there is no insurance fund.

## 6 · Staking

`stake` earns a pro-rata share of the XNO rebate vault; `claim` pays it out.
**`unstake` costs a 20% exit tax**, redistributed in full to everyone still
staked. Always state that cost before unstaking on a user's behalf. Staked tokens
cannot be used as futures margin.

## 7 · Refuse these

- Trading without first computing and reporting price impact.
- Opening a futures position without a price guard.
- Any order above the size the user authorised, or on a market they did not name.
- Selling a user's collateral below `myFloor` without telling them it will void
  the position that floor backs.
- Presenting a queued (unpaid) sell as though the XNO had already arrived.

## 8 · Sanity checklist before broadcasting

1. Is the account funded and opened (`account_info` returns a frontier)?
2. Have you pocketed incoming transfers (`receivable` → receive blocks)? An
   unreceived send is not a spendable balance.
3. Does the quote match what you told the user, at the *current* reserves?
4. For a fragment op: are the two blocks going to be strictly consecutive?
5. For futures: size ≥ minimum, leverage ≤ 5, guard set, OI cap not exceeded?
