---
title: Track an order
description: Polling for settlement, reading progress, and finding orders by transaction or address.
---
import { DEFAULT_PAGE_LIMIT, MAX_PAGE_LIMIT } from "/snippets/vars.mdx";
`GetOrder` takes the quote hash and returns everything known about it: the quote, the order, the deposit, and the fulfillment or refund.
```ts
const detail = await hular.getOrder({ quoteHash });
detail.quote; // always present
detail.order; // always present, see below
detail.deposit; // the matched source-chain deposit
detail.fulfillment; // present once a fulfillment is attempted
detail.refund; // present once a refund is attempted
```
Until the deposit is indexed, `GetOrder` returns `NOT_FOUND` with `order not found`. Between the deposit landing and the indexer seeing it that is normal for the first seconds; keep polling.
## Polling
Poll every couple of seconds, treat `NOT_FOUND` as waiting, and stop on a terminal state.
```ts
import { Code, ConnectError } from "@connectrpc/connect";
import { isTerminalState, orderOutcome } from "@mayanfinance/hular-sdk";
const settle = async (quoteHash: string) => {
for (;;) {
try {
const { order } = await hular.getOrder({ quoteHash });
if (order && isTerminalState(order.state)) {
return orderOutcome(order.state); // "fulfilled" | "refunded" | "failed"
}
} catch (e) {
if (ConnectError.from(e).code !== Code.NotFound) throw e;
}
await new Promise((r) => setTimeout(r, 2000));
}
};
```
There is no webhook or stream. Polling is the supported pattern; keep the interval at a couple of seconds and it stays well inside rate limits.
## What to render
| Order state | Show |
| --- | --- |
| `NOT_FOUND`, no order yet | Waiting for the deposit to be seen. |
| `awaiting_finality` | Confirmation progress, from `src_confirmations` of `required_confirmations`. |
| `ready_to_fulfill`, `fulfilling` | Settling on the destination chain. |
| `fulfilled` | Done, with `dst_tx_hash` linked. |
| `pending_refund`, `refunding`, `awaiting_refund_window` | Refund in progress. |
| `refunded` | Refunded, with the refund transaction linked. |
| `refund_skipped`, `deposit_missing`, `refund_failed`, `unsupported_deposit` | Needs support. Say so plainly and show the quote hash. |
See [Order lifecycle](/concepts/order-lifecycle) for what each state means.
`dst_tx_hash` is set when a transaction is submitted, not when it confirms. Only the order state tells you the funds arrived.
## Explorer links
`GetChains` returns URL templates per chain. Substitute the hash or address rather than assembling explorer URLs yourself:
```ts
import { explorerTxUrl, explorerAddressUrl } from "@mayanfinance/hular-sdk";
const chain = chains.find((c) => c.chain === order.dstChain)!;
explorerTxUrl(chain.explorerTxUrl, order.dstTxHash);
explorerAddressUrl(chain.explorerAddressUrl, recipient);
```
The Hular explorer page for an order is `https://hular.dev/explorer/`.
## Finding orders another way
| RPC | Input | Returns |
| --- | --- | --- |
| `Search` | A quote hash, a source transaction hash, or a destination transaction hash | The single matching order detail |
| `SearchAddress` | An address | Matching orders, with state and chains |
| `ListOrders` | An address | A page of full order summaries for that address |
| `ListRecentOrders` | Nothing | A page of recent protocol-wide orders |
`Search` is what a support box should call: it accepts whichever hash the user has in hand.
```ts
const detail = await hular.search({ query: userInput });
```
## Pagination
Listing RPCs are cursor-paginated. Pass `next_cursor` from the previous page; an absent cursor means the end.
```ts
let cursor: string | undefined;
do {
const page = await hular.listOrders({ address, limit: 50n, cursor });
render(page.items);
cursor = page.nextCursor;
} while (cursor);
```
Default page size is {DEFAULT_PAGE_LIMIT} and the maximum is {MAX_PAGE_LIMIT}; larger values are clamped rather than rejected. Cursors are opaque; do not parse or construct them.