> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rondo.finance/llms.txt
> Use this file to discover all available pages before exploring further.

# Building an interface

> What the contract cannot check for you, and therefore what your UI has to.

The contract refuses bad fills — but a refusal costs gas and reads as a failure. An interface must
perform every check below so the user does not sign a transaction that was already known to revert.

## Market identity first

Treat `(chainId, swapperAddress, orderId)` as the order identity. Every market starts its own ids at
1, so `orderId` alone collides across books. Switching the UI market must replace the proxy, base
token, feed, deployment block and lens together, clear any selected order and reset form input.

Persist one of `jpyc-usdt`, `idrx-usdt`, or `idrp-usdt` and support direct links with `?market=`.
Read token decimals and symbols on-chain; never reuse JPYC's 18-decimal assumption for IDRX (2) or
IDRP (6).

<AccordionGroup>
  <Accordion title="Mirror the arithmetic, including the rounding modes">
    A UI that rounds differently quotes a number the chain will not honour. Mirror every quote path
    with the same `Ceil`/`Floor` choices as the contract, and still confirm displayed quotes against
    `previewFill` before building a transaction.
  </Accordion>

  <Accordion title="Cap the input at what the order can absorb">
    Past `sellRemaining` the fill clamps anyway, so a larger figure on screen states a payment that
    will not happen. Cap the field instead of showing a number the settlement will contradict.
  </Accordion>

  <Accordion title="Pin the exact output for take-all">
    `sellAmountOut` is the argument, so pinning it to `sellRemaining` lands on the remainder
    **exactly**. Buffering the payment instead only lands *near* it and leaves dust in the order.
  </Accordion>

  <Accordion title="Check the balance against maxAmountIn, not the quote">
    A pegged fill may cost anything up to the ceiling. Comparing the balance to the quote lets a
    taker whose balance sits between the two approve and sign a fill that reverts on
    `transferFrom` — gas spent on a failure the ticket could see coming. For a fixed order the
    quote *is* the exposure, because it cannot move.
  </Accordion>

  <Accordion title="Let the taker set the slippage tolerance">
    It is their exposure, so it is their number. Offer explicit choices such as 0.1% / 0.5% / 1%
    and feed the selected value into `maxAmountIn`.
  </Accordion>

  <Accordion title="Warn near expiry">
    An order that lapses while the transaction is in flight reverts on arrival. Warn rather than
    block — the window is the taker's to judge.
  </Accordion>

  <Accordion title="Page the book at one pinned block">
    `EnumerableSet` swap-and-pop means an order cancelled between two page reads slides backwards
    into a page you already passed and is never seen. Read count, id pages and order data at a
    single block number.
  </Accordion>

  <Accordion title="Explain every blocked fill before the wallet opens">
    Hide a below-floor PEG order on a best-offer route, or mark it **PAUSED** in a full order book;
    either way, prevent submission and explain that it resumes when the oracle recovers. When a
    size is below the minimum, show **take the minimum** and **take it all** with their real payment
    amounts instead of a neutral error alone.
  </Accordion>
</AccordionGroup>

## Reading the book

```ts theme={"system"}
const {swapper: address} = selectedMarket;
const block = await client.getBlockNumber();
const read = (fn, args) => client.readContract({address, abi, functionName: fn, args, blockNumber: block});

const total = await read("openOrderCount", []);
const ids   = (await Promise.all(pagesOf(total).map(o => read("openOrderIds", [o, 200n])))).flat();
const orders = await read("getOrders", [ids]);
```

## Quoting a fill

```ts theme={"system"}
// The contract's own quote decides the transaction arguments.
const [amountIn, fee, netOut] = await read("previewFill", [orderId, sellAmountOut]);

// Pegged orders reprice between signing and mining; fixed ones cannot.
const maxAmountIn = isPegged
  ? (amountIn * (10_000n + toleranceBps)) / 10_000n
  : amountIn;

await write("fillOrder", [orderId, sellAmountOut, maxAmountIn]);
```

<Warning>
  Approve `maxAmountIn`, not `amountIn`. An ordinary oracle tick between approving and filling would
  otherwise leave the allowance a few wei short.
</Warning>
