@xochi/client 0.0.0-stage → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/LICENSE +21 -0
  3. package/README.md +120 -2
  4. package/dist/cjs/claim.d.ts +33 -0
  5. package/dist/cjs/claim.js +44 -0
  6. package/dist/cjs/fee.d.ts +247 -0
  7. package/dist/cjs/fee.js +295 -0
  8. package/dist/cjs/index.d.ts +18 -0
  9. package/dist/cjs/index.js +33 -0
  10. package/dist/cjs/machine/claimInfo.d.ts +29 -0
  11. package/dist/cjs/machine/claimInfo.js +26 -0
  12. package/dist/cjs/machine/decisions.d.ts +452 -0
  13. package/dist/cjs/machine/decisions.js +665 -0
  14. package/dist/cjs/machine/deposit.d.ts +113 -0
  15. package/dist/cjs/machine/deposit.js +265 -0
  16. package/dist/cjs/machine/index.d.ts +23 -0
  17. package/dist/cjs/machine/index.js +53 -0
  18. package/dist/cjs/machine/machine.d.ts +411 -0
  19. package/dist/cjs/machine/machine.js +1627 -0
  20. package/dist/cjs/machine/phase.d.ts +24 -0
  21. package/dist/cjs/machine/phase.js +45 -0
  22. package/dist/cjs/machine/quoteChecks.d.ts +41 -0
  23. package/dist/cjs/machine/quoteChecks.js +82 -0
  24. package/dist/cjs/machine/receipt.d.ts +151 -0
  25. package/dist/cjs/machine/receipt.js +69 -0
  26. package/dist/cjs/machine/recipientEcho.d.ts +16 -0
  27. package/dist/cjs/machine/recipientEcho.js +27 -0
  28. package/dist/cjs/machine/solverPin.d.ts +110 -0
  29. package/dist/cjs/machine/solverPin.js +214 -0
  30. package/dist/cjs/machine/state.d.ts +167 -0
  31. package/dist/cjs/machine/state.js +31 -0
  32. package/dist/cjs/machine/transition.d.ts +15 -0
  33. package/dist/cjs/machine/transition.js +20 -0
  34. package/dist/cjs/machine/validateRequest.d.ts +15 -0
  35. package/dist/cjs/machine/validateRequest.js +127 -0
  36. package/dist/cjs/machine/view.d.ts +3 -0
  37. package/dist/cjs/machine/view.js +74 -0
  38. package/dist/cjs/machine/wire.d.ts +527 -0
  39. package/dist/cjs/machine/wire.js +280 -0
  40. package/dist/cjs/package.json +1 -0
  41. package/dist/cjs/shared/api.d.ts +9 -0
  42. package/dist/cjs/shared/api.js +5 -0
  43. package/dist/cjs/shared/chains.d.ts +243 -0
  44. package/dist/cjs/shared/chains.js +250 -0
  45. package/dist/cjs/shared/eip712.d.ts +44 -0
  46. package/dist/cjs/shared/eip712.js +64 -0
  47. package/dist/cjs/shared/index.d.ts +16 -0
  48. package/dist/cjs/shared/index.js +32 -0
  49. package/dist/cjs/shared/intentStream.d.ts +74 -0
  50. package/dist/cjs/shared/intentStream.js +59 -0
  51. package/dist/cjs/shared/intents.d.ts +88 -0
  52. package/dist/cjs/shared/intents.js +227 -0
  53. package/dist/cjs/shared/schemas.d.ts +928 -0
  54. package/dist/cjs/shared/schemas.js +524 -0
  55. package/dist/cjs/shared/shieldedPreview.d.ts +841 -0
  56. package/dist/cjs/shared/shieldedPreview.js +128 -0
  57. package/dist/cjs/shared/stealth.d.ts +53 -0
  58. package/dist/cjs/shared/stealth.js +68 -0
  59. package/dist/cjs/shared/trading.d.ts +153 -0
  60. package/dist/cjs/shared/trading.js +185 -0
  61. package/dist/cjs/shared/types.d.ts +37 -0
  62. package/dist/cjs/shared/types.js +7 -0
  63. package/dist/cjs/shared/validators.d.ts +30 -0
  64. package/dist/cjs/shared/validators.js +79 -0
  65. package/dist/cjs/transport/client.d.ts +234 -0
  66. package/dist/cjs/transport/client.js +1456 -0
  67. package/dist/esm/claim.d.ts +33 -0
  68. package/dist/esm/claim.js +41 -0
  69. package/dist/esm/fee.d.ts +247 -0
  70. package/dist/esm/fee.js +285 -0
  71. package/dist/esm/index.d.ts +18 -0
  72. package/dist/esm/index.js +17 -0
  73. package/dist/esm/machine/claimInfo.d.ts +29 -0
  74. package/dist/esm/machine/claimInfo.js +23 -0
  75. package/dist/esm/machine/decisions.d.ts +452 -0
  76. package/dist/esm/machine/decisions.js +640 -0
  77. package/dist/esm/machine/deposit.d.ts +113 -0
  78. package/dist/esm/machine/deposit.js +260 -0
  79. package/dist/esm/machine/index.d.ts +23 -0
  80. package/dist/esm/machine/index.js +23 -0
  81. package/dist/esm/machine/machine.d.ts +411 -0
  82. package/dist/esm/machine/machine.js +1621 -0
  83. package/dist/esm/machine/phase.d.ts +24 -0
  84. package/dist/esm/machine/phase.js +42 -0
  85. package/dist/esm/machine/quoteChecks.d.ts +41 -0
  86. package/dist/esm/machine/quoteChecks.js +78 -0
  87. package/dist/esm/machine/receipt.d.ts +151 -0
  88. package/dist/esm/machine/receipt.js +64 -0
  89. package/dist/esm/machine/recipientEcho.d.ts +16 -0
  90. package/dist/esm/machine/recipientEcho.js +24 -0
  91. package/dist/esm/machine/solverPin.d.ts +110 -0
  92. package/dist/esm/machine/solverPin.js +210 -0
  93. package/dist/esm/machine/state.d.ts +167 -0
  94. package/dist/esm/machine/state.js +28 -0
  95. package/dist/esm/machine/transition.d.ts +15 -0
  96. package/dist/esm/machine/transition.js +17 -0
  97. package/dist/esm/machine/validateRequest.d.ts +15 -0
  98. package/dist/esm/machine/validateRequest.js +123 -0
  99. package/dist/esm/machine/view.d.ts +3 -0
  100. package/dist/esm/machine/view.js +71 -0
  101. package/dist/esm/machine/wire.d.ts +527 -0
  102. package/dist/esm/machine/wire.js +266 -0
  103. package/dist/esm/shared/api.d.ts +9 -0
  104. package/dist/esm/shared/api.js +4 -0
  105. package/dist/esm/shared/chains.d.ts +243 -0
  106. package/dist/esm/shared/chains.js +236 -0
  107. package/dist/esm/shared/eip712.d.ts +44 -0
  108. package/dist/esm/shared/eip712.js +61 -0
  109. package/dist/esm/shared/index.d.ts +16 -0
  110. package/dist/esm/shared/index.js +16 -0
  111. package/dist/esm/shared/intentStream.d.ts +74 -0
  112. package/dist/esm/shared/intentStream.js +55 -0
  113. package/dist/esm/shared/intents.d.ts +88 -0
  114. package/dist/esm/shared/intents.js +213 -0
  115. package/dist/esm/shared/schemas.d.ts +928 -0
  116. package/dist/esm/shared/schemas.js +521 -0
  117. package/dist/esm/shared/shieldedPreview.d.ts +841 -0
  118. package/dist/esm/shared/shieldedPreview.js +124 -0
  119. package/dist/esm/shared/stealth.d.ts +53 -0
  120. package/dist/esm/shared/stealth.js +64 -0
  121. package/dist/esm/shared/trading.d.ts +153 -0
  122. package/dist/esm/shared/trading.js +182 -0
  123. package/dist/esm/shared/types.d.ts +37 -0
  124. package/dist/esm/shared/types.js +6 -0
  125. package/dist/esm/shared/validators.d.ts +30 -0
  126. package/dist/esm/shared/validators.js +74 -0
  127. package/dist/esm/transport/client.d.ts +234 -0
  128. package/dist/esm/transport/client.js +1447 -0
  129. package/package.json +61 -4
@@ -0,0 +1,527 @@
1
+ /**
2
+ * Wire shapes and decoders.
3
+ *
4
+ * The worker speaks the canonical snake_case contract (`QuoteResponseSchema` in
5
+ * `@xochi/shared`, `public/openapi.yaml`); everything past this module is
6
+ * camelCase. Decoding happens HERE, once, so the trade widget and `@xochi/sdk`
7
+ * read a quote, an execute result and an intent status through the same code
8
+ * rather than through two mappers that drift.
9
+ */
10
+ import type { ChainId, SelectableChainId, SettlementType, DestinationType, FeeBreakdown, FeeDenomination, Eip712TypedData, SettlementOption, QuoteResponseWire, ExecuteResponseWire, IntentStatus } from "../shared/index.js";
11
+ /** Full destination specification */
12
+ export interface IntentDestination {
13
+ type: DestinationType;
14
+ chainId?: ChainId;
15
+ shielded: boolean;
16
+ /**
17
+ * Recipient address on the destination chain, for a non-EVM settlement (Tron).
18
+ * The user types it; EVM routes settle to the connected wallet and omit it.
19
+ */
20
+ recipientAddress?: string;
21
+ }
22
+ export interface QuoteRequest {
23
+ wallet?: string;
24
+ /** Ask for pricing only. Preview quotes cannot be signed or executed. */
25
+ preview?: boolean;
26
+ /** Idempotency key for one explicit Tron deposit-route allocation. */
27
+ allocationKey?: string;
28
+ fromChainId: SelectableChainId;
29
+ toChainId: SelectableChainId;
30
+ fromToken: string;
31
+ toToken: string;
32
+ fromAmount: string;
33
+ deadline: number;
34
+ slippageBps: number;
35
+ trustScore?: number;
36
+ settlementPreference: SettlementType;
37
+ /** Recipient on the destination chain (non-EVM routes); omit for EVM. */
38
+ recipientAddress?: string;
39
+ /**
40
+ * Refund destination on the ORIGIN chain, for a deposit-route origin (Tron).
41
+ * Omit for EVM origins, where a refund goes back to the connected wallet.
42
+ * See xochi#358: without it a refund pays the on-chain deposit sender, which
43
+ * strands funds when the deposit came from an exchange.
44
+ */
45
+ refundAddress?: string;
46
+ destination?: IntentDestination;
47
+ /** Stealth meta-address for stealth settlements */
48
+ stealthMetaAddress?: {
49
+ spendingPubKey: string;
50
+ viewingPubKey: string;
51
+ chainId: ChainId;
52
+ };
53
+ }
54
+ /** Stealth settlement claim info (factory + salt for counterfactual SimpleAccount) */
55
+ export interface ClaimInfo {
56
+ factoryAddress: string;
57
+ salt: number;
58
+ }
59
+ export interface QuoteResponse {
60
+ success: boolean;
61
+ intentId: string;
62
+ quoteId: string;
63
+ canSolve: boolean;
64
+ /** True when this is a non-persisted, non-executable public price preview. */
65
+ preview?: boolean;
66
+ toAmount: string;
67
+ minToAmount: string;
68
+ xochiFee: string;
69
+ xochiFeeRate: string;
70
+ /**
71
+ * All-in cost of this quote in basis points, measured server-side by the same
72
+ * fixed-cost guard that refuses an uneconomical order. Distinct from
73
+ * `xochiFeeRate`, which is the flat schedule rate: this one moves with route
74
+ * and size. Absent when the guard could not run -- render nothing, not zero.
75
+ */
76
+ costBps?: number;
77
+ /** Itemized layer split; absent until the worker/Riddler emit it. */
78
+ feeBreakdown?: FeeBreakdown;
79
+ /**
80
+ * Which token `feeBreakdown`'s layer AMOUNTS are measured in: `"origin"` =
81
+ * from-token base units, `"destination"` = to-token base units.
82
+ *
83
+ * On the wire so the renderer reads the denomination instead of inferring it
84
+ * from which branch produced the breakdown. Formatting a fee with the wrong
85
+ * token's decimals misstates it by the exchange rate (by 10^12 on an
86
+ * 18-decimal origin), and every live route today is 6 decimals on both sides,
87
+ * so the mistake is invisible until it is not.
88
+ *
89
+ * Absent means UNKNOWN, never a default. The worker emits it whenever it
90
+ * forwards a served breakdown and omits it when there is no breakdown.
91
+ */
92
+ feeDenomination?: FeeDenomination;
93
+ estimatedGasCost: string;
94
+ /**
95
+ * Subscribe capability for this intent's live status + race stream, served
96
+ * once with the quote. TREAT AS A BEARER TOKEN: possession is the entire
97
+ * authorization and the relay replays its recent buffer to each new
98
+ * subscriber. Never log it and never put it in a shared URL.
99
+ *
100
+ * Absent means no stream (an intent quoted before the field existed), which
101
+ * the client must read as "poll only" rather than substituting the intentId.
102
+ * That substitution is the bug this field exists to prevent: the id is public
103
+ * by construction.
104
+ */
105
+ streamTopic?: string;
106
+ expiry: string;
107
+ gasless: boolean;
108
+ gaslessFee?: string;
109
+ paymentMethod?: PaymentMethod;
110
+ eip712Data?: Eip712TypedData;
111
+ /**
112
+ * Origin-pull authorization served by the solver. The user signs this in
113
+ * addition to eip712Data; the signature is sent as pullSignature on execute.
114
+ * Present when the route requires origin collection (erc3009 / permit2).
115
+ */
116
+ pullAuthorization?: Eip712TypedData;
117
+ /**
118
+ * Non-EVM origin (Tron): the per-intent address the user manually sends
119
+ * TRC-20 to. Mutually exclusive with pullAuthorization. Only ever surfaced to
120
+ * the UI after depositAttestation verifies against the pinned signer.
121
+ */
122
+ depositAddress?: string;
123
+ /** Solver attestation over the deposit binding; verified fail-closed. */
124
+ depositAttestation?: string;
125
+ /** Deposit-acceptance deadline (unix seconds) for a deposit route. */
126
+ depositDeadline?: number;
127
+ /**
128
+ * The RESOLVED settlement type the quote is served under, read from
129
+ * `route.settlement_type`. Distinct from the value REQUESTED: the solver may
130
+ * serve a different one, and the v2 deposit attestation signs the resolved
131
+ * value. Undefined when the solver did not state it, which a deposit route
132
+ * must treat as a refusal rather than assuming "public" -- guessing here would
133
+ * silently diverge from the signed message on every quote.
134
+ */
135
+ settlementType?: SettlementType;
136
+ /**
137
+ * The served `escrow` envelope, verbatim, when the quote carried the key.
138
+ * Not modelled: it is held only so the `@xochi/signer` checks the machine
139
+ * runs at ingest can see it. This client does not run the escrow binding, so
140
+ * a quote whose `escrow` is anything but absent or `null` is refused before
141
+ * it can reach the wallet.
142
+ */
143
+ escrow?: unknown;
144
+ /**
145
+ * Echo of the requested destination recipient (non-EVM/Tron routes). The
146
+ * client asserts a solvable quote echoes exactly the address it asked for
147
+ * (fail-closed on mismatch); absent for EVM routes. See #113.
148
+ */
149
+ recipientAddress?: string;
150
+ settlementOptions: SettlementOption[];
151
+ claimInfo?: ClaimInfo;
152
+ error?: string;
153
+ /**
154
+ * Stable machine key for a declined quote (served beside `error`). Branch on
155
+ * this, never on the prose: the wire contract documents the vocabulary and
156
+ * says an unrecognized value is a non-retryable decline.
157
+ */
158
+ errorCategory?: string;
159
+ /**
160
+ * Smallest order (USD) that clears this route right now; served on an
161
+ * `amount_too_small` decline. THE number to retry with (it already covers
162
+ * the solver floor and the fixed-cost threshold). Moves with corridor cost,
163
+ * so never cache it.
164
+ */
165
+ minimumAmountUsd?: number;
166
+ /**
167
+ * The solver's own live floor (USD), for transparency copy only. An order at
168
+ * exactly this figure delivers almost nothing; never offer it as the retry.
169
+ */
170
+ minFillableUsd?: number;
171
+ destination?: IntentDestination;
172
+ }
173
+ export interface ExecuteRequest {
174
+ intentId: string;
175
+ quoteId: string;
176
+ signature: string;
177
+ nonce: number;
178
+ pullSignature?: string;
179
+ aztecProof?: string;
180
+ }
181
+ export interface ExecuteResponse {
182
+ success: boolean;
183
+ intentId: string;
184
+ status: IntentStatus;
185
+ txHash?: string;
186
+ /** Destination fill hash (cross-chain); usually arrives later via status/WS. */
187
+ receivingTxHash?: string;
188
+ /** Public fill receipt (Riddler #632) + its chain, when a fill resolves inline. */
189
+ settlementTxHash?: string;
190
+ settlementChainId?: ChainId;
191
+ bridgeTxHash?: string;
192
+ /** In-doubt: funds may be in flight but unconfirmed. */
193
+ reconciling?: boolean;
194
+ error?: string;
195
+ /**
196
+ * `NOT_EXECUTED_SUBSTATUS` on a `failed` the Worker wrote knowing nothing
197
+ * moved; the only `failed` the machine offers Retry on after an execute.
198
+ */
199
+ substatus?: string;
200
+ /** Note commitment for shielded settlements */
201
+ noteCommitment?: string;
202
+ /** Stealth settlement info */
203
+ stealthAddress?: string;
204
+ ephemeralPubKey?: string;
205
+ viewTag?: number;
206
+ /**
207
+ * Per-intent read capability (xochi#563), served once and only by a server
208
+ * that issues it (xochi#565, merged as `0317e84`, once deployed). Keep it
209
+ * with the intent and pass it as `readToken` to `getIntent` /
210
+ * `getIntentStatus`: a stealth intent read without it omits its on-chain
211
+ * legs. A token read of a shielded intent gets only the status-only view, as
212
+ * an anonymous read does (SPEC.md §5.1, `IntentReadOptions`).
213
+ * A bearer secret: never log it or put it in a URL.
214
+ */
215
+ readToken?: string;
216
+ }
217
+ /**
218
+ * An execute response as the machine receives it in `ExecuteReplied`: the
219
+ * bearer `readToken` removed (`const { readToken, ...response } = ...`) and
220
+ * kept outside the machine, so no message, model, effect or replay log holds
221
+ * it. The machine derives the fund-safety verdict (#226) from this response
222
+ * itself; a host relays it and never decides it.
223
+ */
224
+ export type ExecuteReply = Omit<ExecuteResponse, "readToken"> & {
225
+ readToken?: never;
226
+ };
227
+ export type IntentMessageType = "intent_status";
228
+ /**
229
+ * The status update `useIntent` applies. In-app only, never a wire shape: the
230
+ * status poll builds it from `/api/intent/:id/status`, and `mapStatusFrame`
231
+ * builds it from the stream's `IntentStatusFrame` (`@xochi/shared`).
232
+ */
233
+ export interface IntentWebSocketMessage {
234
+ type: IntentMessageType;
235
+ intentId: string;
236
+ status: IntentStatus;
237
+ txHash?: string;
238
+ /** Destination fill hash (broadcast on cross-chain completion). */
239
+ receivingTxHash?: string;
240
+ /** Public fill receipt (Riddler #632) + its chain, broadcast on completion. */
241
+ settlementTxHash?: string;
242
+ settlementChainId?: ChainId;
243
+ /** In-doubt signal (broadcast when an execute lands in reconcile). */
244
+ reconciling?: boolean;
245
+ /**
246
+ * Riddler's granular fill substatus, verbatim; see IntentState.substatus. On
247
+ * a `failed`, `NOT_EXECUTED_SUBSTATUS` is the Worker's "nothing moved".
248
+ */
249
+ substatus?: string;
250
+ error?: string;
251
+ /** Machine-readable refund reason code (deposit routes) on a `refunded` status. */
252
+ refundReason?: string;
253
+ timestamp: string;
254
+ }
255
+ /** How the token pull + gas execution is handled */
256
+ export type PaymentMethod = "erc3009" | "permit2" | "pimlico" | "x402" | "mpp" | "approval" | "deposit";
257
+ export declare function isQuoteResponseWire(value: unknown): value is QuoteResponseWire & {
258
+ readonly escrow?: unknown;
259
+ };
260
+ export declare function isExecuteResponseWire(value: unknown): value is ExecuteResponseWire;
261
+ /**
262
+ * Read the resolved settlement type out of the solver's opaque `route` bag.
263
+ *
264
+ * `route` is `z.record(z.string(), z.unknown())` in the contract: a passthrough
265
+ * the worker does not model, so it arrives untyped. Narrow it to the closed
266
+ * settlement vocabulary here rather than casting, and return undefined for
267
+ * anything unrecognised. Callers that need it (the v2 deposit attestation) must
268
+ * treat undefined as a refusal: a wrong guess produces a message that fails to
269
+ * recover, which reads as a solver outage rather than as our bug.
270
+ */
271
+ export declare function readResolvedSettlementType(route: Record<string, unknown> | undefined): SettlementType | undefined;
272
+ export declare function mapQuoteResponse(w: QuoteResponseWire & {
273
+ readonly escrow?: unknown;
274
+ }): QuoteResponse;
275
+ export declare function mapExecuteResponse(w: ExecuteResponseWire): ExecuteResponse;
276
+ export declare const RACE_MESSAGE_TYPES: readonly ["race_started", "solver_bid", "race_won"];
277
+ export type RaceMessageType = (typeof RACE_MESSAGE_TYPES)[number];
278
+ /**
279
+ * Live race telemetry relayed from Riddler while an intent fills.
280
+ *
281
+ * DISPLAY ONLY. Intent status comes from lifecycle (`status`) frames and the status
282
+ * poll; nothing here may advance a swap or change what the user is told they
283
+ * received. `solver` is a pseudonymous handle, never an operator identity or an
284
+ * address, and `elapsedMs` is the server's clock (the local one is decoration).
285
+ */
286
+ export interface RaceWebSocketMessage {
287
+ type: RaceMessageType;
288
+ intentId: string;
289
+ solver?: string;
290
+ /** Destination amount this bid delivers (base units, string). */
291
+ amountOut?: string;
292
+ /** Price improvement against the accepted quote, in bps. */
293
+ improvementBps?: number;
294
+ /** Solvers active on this route when the race opened (race_started only). */
295
+ solversActive?: number;
296
+ /** Server-side ms since the race opened. */
297
+ elapsedMs?: number;
298
+ timestamp: string;
299
+ }
300
+ export declare function isRaceWebSocketMessage(value: unknown): value is RaceWebSocketMessage;
301
+ /**
302
+ * Granular origin-pull states the solver may emit that the `IntentStatus`
303
+ * union doesn't model, bucketed into the nearest known state. Unknown/granular
304
+ * strings otherwise reach `phaseFromStatus` (which drops them to the confirm
305
+ * screen) and fall outside the poll's in-flight set (freezing the row mid-flight
306
+ * -- the dead-spinner bug).
307
+ */
308
+ export declare const GRANULAR_WIRE_STATUS: Readonly<Record<string, IntentStatus>>;
309
+ /**
310
+ * A raw `/status` value, decoded.
311
+ *
312
+ * `mapped` is a status this build models: a union member, or one of Riddler's
313
+ * granular strings bucketed above. `unmapped` is any other string, and it is
314
+ * deliberately NOT a guess at an outcome: it buckets to `executing` so the poll
315
+ * runs on to a real terminal (an unrecognized string must never freeze the row),
316
+ * and the machine shows it as "Still confirming" (reconciling) rather than as a
317
+ * normal in-flight step. It can never decode to `completed`. This is the
318
+ * fail-closed half of the CLAUDE.md "Status-vocabulary sync" rule.
319
+ *
320
+ * `absent` is a body with no status string at all: not a Riddler status, so
321
+ * not a vocabulary signal. It keeps the in-flight bucket and nothing else.
322
+ */
323
+ export type DecodedWireStatus = {
324
+ kind: "mapped";
325
+ status: IntentStatus;
326
+ } | {
327
+ kind: "unmapped";
328
+ status: "executing";
329
+ raw: string;
330
+ } | {
331
+ kind: "absent";
332
+ status: "executing";
333
+ };
334
+ export declare function decodeWireStatus(raw: unknown): DecodedWireStatus;
335
+ /**
336
+ * The normalized status of an in-flight intent, read from `GET
337
+ * /api/intent/:id/status`.
338
+ */
339
+ export interface IntentStatusData {
340
+ /**
341
+ * The intent this status is about, echoed by the worker (`intent_id` on every
342
+ * branch of `buildStatusDeliverable`). Carried so a consumer that merges the
343
+ * response into a stored record can check it is merging into the right one;
344
+ * null when an older worker omits it.
345
+ */
346
+ intentId: string | null;
347
+ status: IntentStatus;
348
+ /**
349
+ * True when the wire status was outside this build's vocabulary and `status`
350
+ * is only the in-flight bucket (`decodeWireStatus`). Absent means mapped.
351
+ */
352
+ unmapped?: boolean;
353
+ txHash: string | null;
354
+ receivingTxHash: string | null;
355
+ /** Public fill receipt (Riddler #632) + its chain; the receipt CTA keys off these. */
356
+ settlementTxHash: string | null;
357
+ settlementChainId: number | null;
358
+ error: string | null;
359
+ /** Machine-readable refund reason code (deposit routes); null otherwise. */
360
+ refundReason: string | null;
361
+ /**
362
+ * Riddler's granular origin-pull/fill substatus, passed through verbatim
363
+ * (pulling | pulled | filling | filled | refunding | ...), or on a `failed`
364
+ * the Worker wrote knowing nothing moved, `NOT_EXECUTED_SUBSTATUS`. NOT a
365
+ * status: the top-level `status` is what advances the lifecycle.
366
+ *
367
+ * It is read for two things. A `failed` after an execute offers Retry only
368
+ * with `not_executed` (README "Fund safety"). And it tells a user whose
369
+ * destination leg has already landed (`filled`) apart from one still
370
+ * waiting: Riddler can hold an intent at top-level `settling` with
371
+ * `substatus: filled` indefinitely, which is exactly the state a stuck
372
+ * stealth swap sits in (xochi#376): the money is delivered and the app was
373
+ * showing an undifferentiated "settling".
374
+ */
375
+ substatus: string | null;
376
+ terminal: boolean;
377
+ /**
378
+ * Present (true) when the server withheld the on-chain legs of a stealth or
379
+ * shielded intent from this read (xochi#563; served by xochi#565, merged as
380
+ * `0317e84`, once deployed): the read carried no owner proof. The tx hashes
381
+ * and `settlementChainId` are then null because they were withheld, not
382
+ * because they do not exist, and the server omits `substatus` unless it is
383
+ * `not_executed`. A wallet SIWE session for the intent's wallet reads them,
384
+ * and so does the read token of a stealth intent only: a token read of a
385
+ * shielded intent gets this view too (SPEC.md §5.1).
386
+ */
387
+ legsWithheld?: true;
388
+ /**
389
+ * Present only with `legsWithheld`: true once the destination fill receipt
390
+ * exists, standing in for the withheld `settlementTxHash`; never true on a
391
+ * terminal failure (`withheldSettlementConfirmed`). The machine shows it
392
+ * exactly as `substatus: "filled"` (delivered); it does not move `status` or
393
+ * `terminal`.
394
+ */
395
+ settlementConfirmed?: boolean;
396
+ }
397
+ /**
398
+ * Discriminated result of a status poll:
399
+ * - `IntentStatusData` on a 200,
400
+ * - `{ notFound: true }` on a definitive 404 (a definitive absence, NOT a
401
+ * verdict: only a pre-dispatch row may be terminalized on it, see
402
+ * `isSweepablePreDispatch`, and a "not sent yet" intent ends on it as
403
+ * nothing moved once past its finish, see `IntentState.notSent`),
404
+ * - `null` on a transient failure (retry on the next tick).
405
+ */
406
+ export type IntentStatusResult = IntentStatusData | {
407
+ notFound: true;
408
+ } | null;
409
+ /**
410
+ * Statuses that are settled: no further status polling is needed. Xochi's own
411
+ * lifecycle vocabulary (`IntentStatus`), and the fallback the status decoder
412
+ * uses when the worker omits `terminal`.
413
+ */
414
+ export declare const TERMINAL_INTENT_STATUSES: Readonly<Partial<Record<IntentStatus, true>>>;
415
+ export declare function isTerminalIntentStatus(status: IntentStatus): boolean;
416
+ /**
417
+ * Whether an anonymous read's `settlement_confirmed` (xochi#563) counts as a
418
+ * delivered fill. Only on a read that withheld the legs (the marker stands in
419
+ * for the withheld receipt), and only when `status` decodes (`decodeWireStatus`)
420
+ * to a status this build models that is not a terminal failure and not part of
421
+ * a refund: never on `refunded` / `failed` / `expired`, on a granular status
422
+ * bucketed to one (`fill_failed`, `refund_failed`), on any `refund*` status
423
+ * (`refunding`, `refund_pending`), or on an unmapped or absent status.
424
+ */
425
+ export declare function withheldSettlementConfirmed(read: {
426
+ status: unknown;
427
+ legsWithheld: unknown;
428
+ settlementConfirmed: unknown;
429
+ }): boolean;
430
+ export interface IntentRecord {
431
+ id: string;
432
+ walletAddress: string;
433
+ status: IntentStatus;
434
+ fromChainId: ChainId;
435
+ toChainId: ChainId;
436
+ fromToken: string;
437
+ toToken: string;
438
+ /**
439
+ * Absent when the server did not serve it, never defaulted: a withheld or
440
+ * missing amount is not zero (SPEC.md §5.1). The same for `toAmount`,
441
+ * `createdAt` and `updatedAt`.
442
+ */
443
+ fromAmount?: string;
444
+ toAmount?: string;
445
+ quoteId: string;
446
+ tier: string;
447
+ feeRate: number;
448
+ settlementType: SettlementType;
449
+ aztecNoteHash?: string;
450
+ txHash?: string;
451
+ /** Public fill receipt (Riddler #632) + its chain, persisted at completion. */
452
+ settlementTxHash?: string;
453
+ settlementChainId?: ChainId;
454
+ createdAt?: string;
455
+ updatedAt?: string;
456
+ error?: string;
457
+ /** `NOT_EXECUTED_SUBSTATUS` on a `failed` the Worker wrote knowing nothing moved. */
458
+ substatus?: string;
459
+ destinationType?: DestinationType;
460
+ /** Never true on a full record: see `WithheldIntentRecord`. */
461
+ legsWithheld?: false;
462
+ }
463
+ /**
464
+ * `GET /api/intent/:id` read without owner proof (xochi#563; served by
465
+ * xochi#565 once deployed): the server's status-only view, for a stealth
466
+ * intent read without the read token or an owner session, and for a shielded
467
+ * intent read without an owner session, token or not (SPEC.md §5.1,
468
+ * `IntentReadOptions`). Tokens, amounts, wallet, quote, tier, fee, timestamps
469
+ * and every tx hash are withheld, so they are absent here rather than
470
+ * defaulted, and `substatus` is present only when it is `not_executed`; read
471
+ * with the token (stealth), or a wallet SIWE session for the intent's wallet,
472
+ * for the full `IntentRecord`.
473
+ */
474
+ export interface WithheldIntentRecord {
475
+ legsWithheld: true;
476
+ id: string;
477
+ /**
478
+ * The served status decoded (`decodeWireStatus`): a granular one
479
+ * (`fill_failed`) as its bucket, an unmodelled one as `executing` with
480
+ * `unmapped: true`.
481
+ */
482
+ status: IntentStatus;
483
+ /** True when the served status was outside this build's vocabulary. Absent means mapped. */
484
+ unmapped?: true;
485
+ fromChainId?: ChainId;
486
+ toChainId?: ChainId;
487
+ settlementType?: SettlementType;
488
+ error?: string;
489
+ /** As on `IntentRecord`. */
490
+ substatus?: string;
491
+ /**
492
+ * True once the destination fill receipt exists (it is withheld itself);
493
+ * never on a terminal failure (`withheldSettlementConfirmed`).
494
+ */
495
+ settlementConfirmed: boolean;
496
+ }
497
+ export interface IntentRecordWire {
498
+ id: string;
499
+ wallet_address?: string;
500
+ status: string;
501
+ from_chain_id?: ChainId;
502
+ to_chain_id?: ChainId;
503
+ from_token?: string;
504
+ to_token?: string;
505
+ from_amount?: string;
506
+ to_amount?: string;
507
+ quote_id?: string;
508
+ tier?: string;
509
+ fee_rate?: number;
510
+ settlement_type?: SettlementType;
511
+ aztec_note_hash?: string;
512
+ tx_hash?: string;
513
+ settlement_tx_hash?: string | null;
514
+ settlement_chain_id?: ChainId | null;
515
+ created_at?: string;
516
+ updated_at?: string;
517
+ error?: string;
518
+ substatus?: string | null;
519
+ legs_withheld?: boolean;
520
+ settlement_confirmed?: boolean;
521
+ }
522
+ /**
523
+ * One `GET /api/intent/:id` body: the status-only `WithheldIntentRecord` when
524
+ * the server says it withheld the legs, else the full record.
525
+ */
526
+ export declare function mapIntentRead(w: IntentRecordWire): IntentRecord | WithheldIntentRecord;
527
+ export declare function mapIntentRecord(w: IntentRecordWire): IntentRecord;