An order nobody fills stays in the source chain’s escrow until its deadline. After that, the user can take it back. The escrow pays back what it holds: on the transaction lanes (wrap-native, swap-token) that is the wrapped native (WETH, WCTM…), never the coin or the token the user paid with. A refund only ever pays the order’s own user. The underlying rules are on Refunds in the API reference. This page covers what the SDK does with them.
The refund calls read the hub’s on-chain views, which take your secret key or your project id: they run on your server, from a page through your backend’s proxy, or from a page with your project id.

When an order is refundable

Two clocks decide it, and the wall clock is neither of them:
  1. The destination chain’s watermark. The hub’s observer on the destination chain must have read at least up to the deadline. Before that, “not filled” is not known yet, and the hub answers EIN0051 rather than guess.
  2. The source chain’s own clock. The source chain’s latest block must be strictly past the deadline in force, max(expires, refundableAfter). Before that, the escrow reverts the refund, and the user still pays the gas.
refundableAfter comes from the order’s history row (chain.refundableAfter in GET /orders). Pass it on when you have it, so a deadline the hub extended counts.
An order’s expired phase starts at the fill deadline, which comes before refunds open. Expired does not mean refundable yet.

EVM-origin orders

Checking

Without a history row (an order your organization did not submit, or one found by transaction hash), the check reads the deadline and the destination chains from the escrow’s own order.

Sending the refund

Before the wallet sees anything, refundEvmOrder:
  1. reads the escrow’s order from the hub (GET /onchain/{id}/order) and refuses a settler the catalog does not declare for that chain, an order opened on another chain, and bytes that do not decode as an order;
  2. switches the wallet to the source chain;
  3. checks on chain that the order hashes to this id on this settler (orderIdentifier), that the escrow still holds the deposit (orderStatus), and that the chain’s clock is past the deadline in force.
It then sends refund(order) to the settler from the user’s wallet (the user pays the gas) and waits for the receipt. A reverted refund throws WalletError with reason reverted. client.prepareEvmRefund(onchainOrderId) returns the same plan without sending anything: the settler, the decoded order and the transaction.

Solana-origin orders

The hub builds the Solana refund (GET /onchain/{id}/refund): an unsigned transaction whose fee payer and only signer is the order’s user. It reads the escrow and the cluster’s clock when you ask, and simulates the transaction before serving it. The transaction lives about a minute (lastValidBlockHeight), so refundSolanaOrder fetches it right before signing. unwrap: true also closes the user’s wrapped-SOL account in the same transaction, so SOL comes back as SOL. It closes the whole account: wrapped SOL the user already held there becomes SOL too. Before the wallet sees it, the SDK checks that the transaction:
  • names this order, on a Solana chain, through an escrow program the catalog declares;
  • is unsigned, with one signature slot, and its fee payer is the order’s user and the only signer;
  • carries the disclosed blockhash and no address lookup tables;
  • starts with the escrow’s refund instruction for this order, and holds nothing else, except with unwrap, exactly one SPL Token CloseAccount that pays the user.
It also checks that the connected wallet is the order’s user. The wallet needs a little SOL for the fee, or for the rent of a token account the refund recreates. client.prepareSolanaRefund(onchainOrderId, unwrap) fetches and checks the transaction without signing it. There is no separate check step on Solana: the hub checks the escrow and the cluster’s clock when it builds the transaction, and says why when it will not.

When a refund is refused

Every refusal is a RefundError, before any wallet prompt unless noted: adviseError(error) turns the first three into a retry-later advice, with retryAfterMs when the wait is known. See Errors.

In a UI

  • Offer a refund only from the check: refundable enables the button, and not-yet shows how long is left. summarizeOrder’s refundEstimate is the hub’s advisory estimate, not a check.
  • Pass the history row’s refundableAfter to both calls.
  • Tell the user what they get back: the wrapped coin on the transaction lanes, and on Solana, SOL or wSOL depending on unwrap.
  • On EVM, the user needs gas on the source chain. On Solana, a little SOL.
The bridge demo’s Orders tab implements this flow. To make an order refundable on a test hub, see Testing.