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:- 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
EIN0051rather than guess. - 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
refundEvmOrder:
- 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; - switches the wallet to the source chain;
- 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.
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
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
refundinstruction for this order, and holds nothing else, except withunwrap, exactly one SPL TokenCloseAccountthat pays the user.
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 aRefundError, 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:
refundableenables the button, andnot-yetshows how long is left.summarizeOrder’srefundEstimateis the hub’s advisory estimate, not a check. - Pass the history row’s
refundableAfterto 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.