--- 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.