--- title: Order lifecycle description: Every state an order can reach, which ones are terminal, and what to show while it moves. --- import { MAX_FULFILL_ATTEMPTS } from "/snippets/vars.mdx"; An order is created when the indexer sees a deposit carrying a known quote hash. Until then there is a quote but no order, and `GetOrder` returns the quote alone. ## States ```mermaid flowchart TD created --> awaiting_finality awaiting_finality --> ready_to_fulfill ready_to_fulfill --> fulfilling fulfilling --> fulfilled fulfilling --> pending_refund fulfilling --> awaiting_refund_window pending_refund --> refunding refunding --> refunded refunding --> awaiting_refund_window awaiting_refund_window --> refunded awaiting_refund_window --> fulfilled ``` | State | Meaning | Terminal | | --- | --- | --- | | `created` | Deposit recorded, order opened. | | | `awaiting_finality` | Confirmations accruing on the source chain. | | | `ready_to_fulfill` | Finality reached, waiting for a relayer worker. | | | `fulfilling` | Claimed by a worker, destination transaction submitted. | | | `fulfilled` | Destination transaction confirmed. The user has their funds. | yes | | `pending_refund` | Cannot be fulfilled: mismatched deposit, or {MAX_FULFILL_ATTEMPTS} failed attempts. | | | `refunding` | Refund transaction submitted on the source chain. | | | `refunded` | Refund confirmed. | yes | | `awaiting_refund_window` | A submitted transaction reverted or had its nonce burnt; the outcome is being resolved. | | | `refund_skipped` | Refund would cost more than the deposit is worth. | yes | | `deposit_missing` | The deposit vanished from the canonical chain, typically a reorg. | yes | | `refund_failed` | The refund transaction itself reverted or was burnt. | yes | | `unsupported_deposit` | Deposit cannot be auto-refunded: unregistered token, or a mismatched deposit against a same-chain quote. | yes | Same-chain swaps skip the middle entirely. The router performs the swap inside the deposit transaction, and the order goes to `fulfilled` once the finality indexer confirms it. ## Collapsing states for a UI Four outcomes are enough for most interfaces, and the SDK maps them for you: ```ts import { orderOutcome, isTerminalState } from "@mayanfinance/hular-sdk"; orderOutcome(order.state); // "pending" | "fulfilled" | "refunded" | "failed" isTerminalState(order.state); // stop polling when true ``` `failed` covers `refund_skipped`, `deposit_missing`, `refund_failed`, and `unsupported_deposit`: the cases where a human needs to get involved. Everything not in the table above maps to `pending`. ## Progress while pending Two fields on the order carry real progress and are worth surfacing: | Field | Use | | --- | --- | | `src_confirmations` | Confirmations seen so far. | | `required_confirmations` | Confirmations needed before fulfillment starts. | Requirements vary by chain and by trade size, so render the ratio rather than a fixed count. Once `src_confirmations` reaches the requirement, the remaining wait is submission and destination confirmation, which is typically seconds. ## Ambiguous outcomes `awaiting_refund_window` is not a failure. It means a transaction reverted or was replaced and the operator refuses to guess what landed. After a quiet period both transaction hashes are re-polled and the order moves to whichever state matches the chain: `fulfilled` if the fulfillment actually landed, `refunded` if the refund did, or back into the refund path. Treat it as pending in a UI, and keep polling. Never treat a destination transaction hash as proof of settlement. `dst_tx_hash` is populated when a transaction is submitted, not when it confirms. The order state is the authority. See [Track an order](/integration/track) for polling patterns, and [Refunds](/concepts/refunds) for what the refund branch pays out.