@coinlist-co/react 0.11.0 → 0.11.1-rc.10770e8

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 (34) hide show
  1. package/dist/{chunk-UIIXXLA7.js → chunk-7CTH4KPU.js} +734 -198
  2. package/dist/chunk-7CTH4KPU.js.map +1 -0
  3. package/dist/{chunk-B2HCVPCQ.js → chunk-LSPZETDH.js} +81 -33
  4. package/dist/chunk-LSPZETDH.js.map +1 -0
  5. package/dist/chunk-UZUQALFY.js +279 -0
  6. package/dist/chunk-UZUQALFY.js.map +1 -0
  7. package/dist/client/index.cjs +5415 -2413
  8. package/dist/client/index.cjs.map +1 -1
  9. package/dist/client/index.d.cts +2439 -1209
  10. package/dist/client/index.d.ts +2439 -1209
  11. package/dist/client/index.js +4457 -2178
  12. package/dist/client/index.js.map +1 -1
  13. package/dist/collections-BBI_XydI.d.cts +116 -0
  14. package/dist/collections-BrX9rRWc.d.ts +116 -0
  15. package/dist/{config-B5mwS_2l.d.cts → config-CMl1bR3F.d.cts} +1183 -150
  16. package/dist/{config-B5mwS_2l.d.ts → config-CMl1bR3F.d.ts} +1183 -150
  17. package/dist/server/index.cjs +1005 -265
  18. package/dist/server/index.cjs.map +1 -1
  19. package/dist/server/index.d.cts +81 -4
  20. package/dist/server/index.d.ts +81 -4
  21. package/dist/server/index.js +120 -51
  22. package/dist/server/index.js.map +1 -1
  23. package/dist/shared/index.cjs +798 -204
  24. package/dist/shared/index.cjs.map +1 -1
  25. package/dist/shared/index.d.cts +98 -16
  26. package/dist/shared/index.d.ts +98 -16
  27. package/dist/shared/index.js +22 -6
  28. package/package.json +3 -2
  29. package/dist/chunk-B2HCVPCQ.js.map +0 -1
  30. package/dist/chunk-KDGNDAHA.js +0 -146
  31. package/dist/chunk-KDGNDAHA.js.map +0 -1
  32. package/dist/chunk-UIIXXLA7.js.map +0 -1
  33. package/dist/collections-BhDkYmzV.d.cts +0 -65
  34. package/dist/collections-CZhHoQHr.d.ts +0 -65
@@ -1,4 +1,4 @@
1
- import { Hex } from 'viem';
1
+ import { Hash, Hex } from 'viem';
2
2
 
3
3
  declare const __brand: unique symbol;
4
4
  type Newtype<Base, Branding> = Base & {
@@ -20,12 +20,544 @@ declare const ClientId: (value: string) => ClientId;
20
20
  type ClientSecret = Newtype<string, 'ClientSecret'>;
21
21
  declare const ClientSecret: (value: string) => ClientSecret;
22
22
 
23
+ /**
24
+ * A classified wallet or transaction failure. The SDK derives this from
25
+ * whatever the {@link EvmWallet} throws, so hosts get a stable, typed error
26
+ * shape regardless of the wallet library underneath.
27
+ *
28
+ * Lives here rather than beside its classifier because it is the failure half
29
+ * of the {@link EvmWallet} contract, and because both `@/shared` and
30
+ * `@/client` name it: {@link LogCause} carries one, and classifying a thrown
31
+ * error into one needs viem's error classes, which are browser-side.
32
+ */
33
+ type WalletError = {
34
+ type: 'user_rejected';
35
+ } | {
36
+ type: 'insufficient_funds';
37
+ } | {
38
+ type: 'contract_reverted';
39
+ /**
40
+ * What the contract said, when it said anything: a `require` string, a
41
+ * panic description, a custom error's name, or the four-byte selector of
42
+ * an error the ABI could not decode.
43
+ *
44
+ * `null` when a revert carried no data, since the string the RPC node
45
+ * offers in its place is not the contract's - see `classifyWalletError`.
46
+ * Nullable rather than optional so that every arm has to answer the
47
+ * question: "it reverted and said nothing" is a fact about the revert,
48
+ * and a caller should have to read it rather than miss it.
49
+ *
50
+ * Never the custom error's arguments. A name is a compile-time
51
+ * identifier; its operands are runtime values - an address, a balance -
52
+ * and those are `'debug'` material.
53
+ */
54
+ reason: string | null;
55
+ } | {
56
+ type: 'timeout';
57
+ hash: Hash;
58
+ } | {
59
+ type: 'unknown';
60
+ cause: unknown;
61
+ };
62
+ /**
63
+ * A {@link WalletError} as it may be reported above `'debug'`: the
64
+ * classification, never the throw.
65
+ *
66
+ * Only the `unknown` arm differs, and it differs because it is the one arm
67
+ * carrying a value the SDK did not author - the raw error the wallet library
68
+ * threw, whose message and metadata hold the transaction's `from`, `to`,
69
+ * `value` and calldata. The other four carry compile-time constants or a
70
+ * server-minted identifier already, so they pass through as they stand.
71
+ *
72
+ * This exists as a type rather than as a scrub some function remembers to
73
+ * apply: `LogCause` names it, so an arm that grew a raw field would fail the
74
+ * build instead of quietly reaching {@link Logger.error}.
75
+ */
76
+ type RedactedWalletError = Exclude<WalletError, {
77
+ type: 'unknown';
78
+ }> | {
79
+ type: 'unknown';
80
+ };
81
+ declare const RedactedWalletError: {
82
+ fromWalletError: (error: WalletError) => RedactedWalletError;
83
+ };
84
+
85
+ /**
86
+ * The observability seam a host implements to see inside the SDK.
87
+ *
88
+ * Pass an implementation as {@link Config.logger} and the SDK reports every
89
+ * request it makes, every failure it classifies, and every state a hook
90
+ * settles into. Leave it out and the SDK logs nothing at all - there is no
91
+ * fallback to `console`, at any level, on any codepath.
92
+ *
93
+ * Four methods over a {@link LogEvent} lambda, plus {@link level}. An event is
94
+ * a constant `msg`, the {@link LogScope} it came from, the accumulated
95
+ * {@link LogBindings}, and a bag of `fields` - so a host forwards structured
96
+ * records rather than parsing strings.
97
+ *
98
+ * ```ts
99
+ * const logger: Logger = {
100
+ * level: () => 'info',
101
+ * debug: () => {},
102
+ * info: (event) => sink.info(toRecord(event())),
103
+ * warn: (event) => sink.warn(toRecord(event())),
104
+ * error: (event) => sink.error(toRecord(event())),
105
+ * };
106
+ * ```
107
+ *
108
+ * {@link pinoClientLogger} and {@link pinoServerLogger} are ready-made
109
+ * implementations over pino, and are what most hosts should reach for.
110
+ *
111
+ * ## Where to run it
112
+ *
113
+ * **Not in production, as the default answer.** Development, tests, staging,
114
+ * and reproducing a reported bug are where this seam earns its keep. Left
115
+ * undefined elsewhere it discloses nothing, and that holds no matter what a
116
+ * later revision of the SDK puts on a line.
117
+ *
118
+ * Where you do run one in production, `'info'` and above is the only sane
119
+ * choice, and the shipped implementations make the alternative unrepresentable
120
+ * rather than merely discouraged: `isDev: false` narrows the level to
121
+ * {@link ProductionLogLevel}, so `'debug'` does not compile. That narrowing is
122
+ * a guard for the partner who ignored this paragraph, not an endorsement of
123
+ * the practice.
124
+ *
125
+ * The distinction worth holding onto: `'info'` and above are **redacted by
126
+ * construction** - see the next section for exactly what that covers, and the
127
+ * type that enforces it. Redacted is a mechanism the SDK can hold itself to. A
128
+ * blanket "safe in production" is a promise about your environment that the
129
+ * SDK is in no position to make, and one that a single future field could
130
+ * quietly break.
131
+ *
132
+ * `'debug'` is for reproducing a bug: a developer's machine, tests, and beta
133
+ * or staging environments where the data flowing through the SDK is not real
134
+ * customer data.
135
+ *
136
+ * ## What each level may disclose
137
+ *
138
+ * **`'debug'` is unredacted. Every other level is not.**
139
+ *
140
+ * At `'debug'` the SDK reports request and response bodies, request headers,
141
+ * the full URL including its query string, and operation parameters
142
+ * **verbatim** - bearer tokens, KYC answers, tax-document fields, wallet
143
+ * signatures, the lot. There is no denylist and no redaction: see ADR-8 for
144
+ * why a partial guarantee was rejected in favour of an honest absence of one.
145
+ *
146
+ * At `'info'`, `'warn'` and `'error'` the SDK emits only its own
147
+ * classification of what happened and identifiers the server minted: a method
148
+ * and a bare path, a status, a duration, a {@link RequestId}, a frontline
149
+ * error code and event id, a {@link LogCause} arm. No request body, no
150
+ * response body, no query string, no host-supplied parameters, and no message
151
+ * text read off a thrown error the SDK did not author.
152
+ *
153
+ * The type carries that boundary rather than merely documenting it:
154
+ * {@link SafeEvent} constrains every field value to a {@link LogValue} scalar,
155
+ * so spreading a body, a DTO or an operation's params into an `info`, `warn`
156
+ * or `error` line does not compile. Only {@link DebugEvent} accepts arbitrary
157
+ * values.
158
+ *
159
+ * The one deliberate exception is an SDK-authored validation message, which
160
+ * quotes the single scalar that failed to map - `Unsupported chain:
161
+ * "solana_mainnet"`. It is a hand-written template naming one offending value,
162
+ * not a payload, and it is the whole reason this seam exists.
163
+ *
164
+ * ## Your responsibility
165
+ *
166
+ * The SDK hands you events. What happens next is yours: **you own the final
167
+ * filtering, redaction and retention** before anything is written to a file, a
168
+ * console, or a third-party sink such as Sentry or Datadog. In particular, if
169
+ * you implement {@link debug} and forward it anywhere durable, you have taken
170
+ * on storing live credentials and personal data, deliberately. Implement
171
+ * `debug` as a no-op - or return a level below it from {@link level} - in any
172
+ * environment where that is not acceptable.
173
+ *
174
+ * **Every event is a lambda, and that is load bearing.** The SDK consults
175
+ * {@link level} first and never invokes the lambda when the level forbids it,
176
+ * so an expensive debug event - a serialized DTO, a loop over balances - costs
177
+ * nothing in production where the level is `'error'` or `'none'`. Build the
178
+ * event inside the lambda, never outside it.
179
+ *
180
+ * ## Every method must be total
181
+ *
182
+ * **None of the five methods may throw, and that includes {@link level}.** The
183
+ * SDK calls them inline, on the codepath of the work it is reporting, and it
184
+ * does not catch them: a `logger.error` that throws while reporting a failed
185
+ * request turns that request's rejection into your logger's exception, and a
186
+ * `level()` that throws breaks the call it was asked about even when nothing
187
+ * else went wrong.
188
+ *
189
+ * That is deliberate. Swallowing would hide a logger that is broken in every
190
+ * environment, and the SDK cannot tell a transport blip in your sink from a
191
+ * bug in your implementation. So the contract is on this side of the seam:
192
+ * catch inside your implementation, and never let an exception out. The
193
+ * shipped pino implementations do exactly that.
194
+ *
195
+ * ```ts
196
+ * error: (event) => {
197
+ * try {
198
+ * const e = event();
199
+ * Sentry.captureMessage(e.msg, { extra: e });
200
+ * } catch {
201
+ * // never rethrow: the SDK is on the other side of this call
202
+ * }
203
+ * },
204
+ * ```
205
+ */
206
+ interface Logger {
207
+ /**
208
+ * The most detailed level this logger wants. Consulted before **every** log
209
+ * call, so keep it cheap and pure: return a captured constant or read a
210
+ * plain field. Do not read an environment variable, hit storage, or call
211
+ * out to a feature-flag service here.
212
+ *
213
+ * Returning `'none'` silences the SDK as completely as omitting the logger.
214
+ *
215
+ * **Must not throw.** It is consulted inside every SDK operation, so an
216
+ * exception here fails the operation itself.
217
+ */
218
+ level(): LogLevel;
219
+ /**
220
+ * Fine-grained detail: request and response bodies, full URLs, headers,
221
+ * operation parameters, hook transitions.
222
+ *
223
+ * **Unredacted.** This is the only level that may carry credentials or
224
+ * personal data, and it always may. Its event is a {@link DebugEvent}, whose
225
+ * fields accept arbitrary values for exactly that reason. Do not forward it
226
+ * to a durable or third-party sink outside development and beta.
227
+ *
228
+ * **Must not throw**, here and on every method below.
229
+ */
230
+ debug(event: () => DebugEvent): void;
231
+ /** Notable, expected events: a request completing, a flow advancing. */
232
+ info(event: () => SafeEvent): void;
233
+ /**
234
+ * Recoverable trouble: a retried request, a renewed session, a poll tick
235
+ * that failed while the last value still stands.
236
+ *
237
+ * Carries a {@link LogCause} when one was classified, the same as
238
+ * {@link error} does. What separates the two levels is whether anything
239
+ * actually broke, not how much is known about it.
240
+ */
241
+ warn(event: () => SafeEvent): void;
242
+ /**
243
+ * A failure. {@link SafeEvent.cause} carries what the SDK knows about it,
244
+ * classified into {@link LogCause}, and is absent when the failure is not an
245
+ * error the SDK caught - a flow reporting the step it stopped at, for
246
+ * instance.
247
+ *
248
+ * Every part of the event is SDK-authored classification or server-authored
249
+ * identifiers, so forwarding it as it stands is safe. `scope` says which
250
+ * part of the SDK the line came from, which is what to route or filter on.
251
+ */
252
+ error(event: () => SafeEvent): void;
253
+ }
254
+ /**
255
+ * A value the SDK is willing to put in a structured field above `'debug'`.
256
+ *
257
+ * Scalars only, and that is the redaction boundary made structural rather than
258
+ * documentary. Every leak ADR-8 found was an *object* spread onto a line that
259
+ * a host forwards to a tracker - an operation's params, a thrown wallet error,
260
+ * a response body. None of them is assignable to this, so each is now a
261
+ * compile error rather than a review catch.
262
+ *
263
+ * It does not stop a call site writing `{ token: accessToken }`, and it is not
264
+ * meant to: a string is a string. What it stops is the mistake that actually
265
+ * happened.
266
+ */
267
+ type LogValue = string | number | boolean | null;
268
+ /**
269
+ * Fields safe at every level: SDK-authored classification and server-minted
270
+ * identifiers, constrained to {@link LogValue}.
271
+ */
272
+ type SafeFields = Readonly<Record<string, LogValue>>;
273
+ /**
274
+ * Fields for `'debug'` only: bodies, headers, full URLs, operation params,
275
+ * raw thrown values. Arbitrary values, because that is the point of the level.
276
+ *
277
+ * The SDK's own implementations render these into a form that cannot fail to
278
+ * serialize - a `bigint`, a cycle or a getter that throws must never turn a
279
+ * log line into a second failure - but they are otherwise passed through.
280
+ */
281
+ type UnredactedFields = Readonly<Record<string, unknown>>;
282
+ /**
283
+ * One narrowing applied to a logger by {@link InternalLogger.child}.
284
+ *
285
+ * A union of single-key objects rather than a positional `string`, because the
286
+ * three things the SDK narrows by are not the same kind of thing: a request id
287
+ * correlates retries of one wire call, a flow name names an on-chain
288
+ * procedure, a hook name names a React binding. Rendered into one `[SCOPE:x]`
289
+ * prefix they were indistinguishable; as fields they must not be.
290
+ *
291
+ * `requestId` keeps its {@link RequestId} newtype rather than widening to
292
+ * `string` at the seam.
293
+ */
294
+ type LogBinding = {
295
+ readonly requestId: RequestId;
296
+ } | {
297
+ readonly flow: string;
298
+ } | {
299
+ readonly hook: string;
300
+ } | {
301
+ readonly op: string;
302
+ };
303
+ /**
304
+ * Every {@link LogBinding} applied so far, merged. This is what reaches a
305
+ * host, so a line from `prepareOndoSell` carries `flow: 'prepareSell'` as its
306
+ * own key and is filterable without parsing anything.
307
+ */
308
+ type LogBindings = {
309
+ readonly [K in LogBinding as keyof K]?: K[keyof K];
310
+ };
311
+ type BaseEvent = {
312
+ /**
313
+ * What happened, as a **constant string literal**.
314
+ *
315
+ * Never interpolated. A log aggregator groups by message, so a `msg` that
316
+ * embeds a status, a count or a reason produces one group per distinct
317
+ * value and is not groupable at all. Every varying value belongs in
318
+ * `fields`, which is what a host filters and facets on.
319
+ *
320
+ * This is a convention, not a checked rule: deslop reasons about import
321
+ * edges and cannot see a template literal, and the type-level approximations
322
+ * catch only some interpolations. ADR-8 records why a partial guarantee was
323
+ * refused here as elsewhere.
324
+ */
325
+ readonly msg: string;
326
+ /**
327
+ * Which part of the SDK reported this. Stamped by
328
+ * {@link internalLogger}, never by a call site.
329
+ */
330
+ readonly scope: LogScope;
331
+ /** Every {@link LogBinding} applied by {@link InternalLogger.child}. */
332
+ readonly bindings: LogBindings;
333
+ };
334
+ /**
335
+ * An event for `'info'`, `'warn'` and `'error'` - the levels a host may run in
336
+ * production.
337
+ *
338
+ * Its fields are {@link SafeFields}, so nothing but a scalar can reach it.
339
+ */
340
+ type SafeEvent = BaseEvent & {
341
+ readonly fields: SafeFields;
342
+ /**
343
+ * What the SDK knows about an accompanying failure, classified. Absent when
344
+ * there is nothing to classify.
345
+ */
346
+ readonly cause?: LogCause;
347
+ };
348
+ /**
349
+ * An event for `'debug'`, the unredacted level. Its fields accept anything.
350
+ */
351
+ type DebugEvent = BaseEvent & {
352
+ readonly fields: UnredactedFields;
353
+ };
354
+ /**
355
+ * How much the SDK may report, from silent to everything.
356
+ *
357
+ * Ordered: each level admits itself and everything before it, so `'info'`
358
+ * shows errors, warnings and info but not debug. `'none'` admits nothing.
359
+ *
360
+ * `'debug'` is the unredacted level, and it is deliberately the bottom of the
361
+ * ladder: the guarantee "`debug` is unredacted and every other level is not"
362
+ * only closes if there is nothing below it. That is why the SDK keeps its own
363
+ * five levels rather than adopting pino's seven, which put `trace` underneath.
364
+ */
365
+ type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
366
+ /**
367
+ * The redacted levels: the only ones to consider running in production.
368
+ *
369
+ * Every level except `'debug'`, which is unredacted by design. Used by the
370
+ * shipped pino implementations to make a production `'debug'` logger
371
+ * unrepresentable rather than merely discouraged - see
372
+ * {@link PinoLoggerOptions}.
373
+ *
374
+ * The name marks which levels are *redacted*, not a verdict that running them
375
+ * in production is safe. The SDK's advice is to leave {@link Config.logger}
376
+ * undefined there; this type is what stops the partner who does not take it
377
+ * from reaching the unredacted level as well.
378
+ */
379
+ type ProductionLogLevel = Exclude<LogLevel, 'debug'>;
380
+ /**
381
+ * How to build one of the SDK's pino-backed loggers.
382
+ *
383
+ * A union rather than `{ level, isDev }`, and that is the whole point:
384
+ * `{ isDev: false, level: 'debug' }` does not typecheck, so a production
385
+ * build cannot ship a logger that prints bearer tokens to a browser console.
386
+ *
387
+ * `isDev` does exactly one thing - decide whether `'debug'` is a legal level.
388
+ * It changes no formatting: dev output and production output are the same
389
+ * shape, so what you debug is what you ship. Pretty-printing is a pipe the
390
+ * host owns (`node server.js | npx pino-pretty`), not a dependency the SDK
391
+ * takes.
392
+ */
393
+ type PinoLoggerOptions = {
394
+ readonly isDev: true;
395
+ readonly level: LogLevel;
396
+ } | {
397
+ readonly isDev: false;
398
+ readonly level: ProductionLogLevel;
399
+ };
400
+ /**
401
+ * Which part of the SDK a line came from, carried as the `scope` field on
402
+ * every event.
403
+ *
404
+ * It names the seam that **reported** the line, not the layer that raised what
405
+ * it reports: an error from `OFFERS` surfaced by `useOffers` is reported on a
406
+ * `HOOKS` line, because that is the hook telling you what it did with it. The
407
+ * `OFFERS` line for the same error is emitted separately, by the namespace.
408
+ *
409
+ * A closed union rather than a free string so that `ONDO` and `Ondo` cannot
410
+ * both appear, so that adding a namespace without giving it a scope fails to
411
+ * compile, and so that a host can `switch` on it when routing. Anything finer
412
+ * - an operation, a flow, a hook - is a {@link LogBinding} and appears as its
413
+ * own field.
414
+ */
415
+ type LogScope = 'HTTP' | 'AUTH' | 'OFFERS' | 'REQUIREMENTS' | 'WALLETS' | 'ERC20' | 'TOKEN_SALE' | 'SUPERSTATE' | 'ONDO' | 'TOKENS' | 'SUPPORT' | 'HOOKS';
416
+ /**
417
+ * Correlates every line one logical request produces. The first attempt, each
418
+ * retry, and the re-send after a session renewal all carry the same id, so a
419
+ * failure can be read back to the request that caused it.
420
+ */
421
+ type RequestId = Newtype<string, 'RequestId'>;
422
+ /**
423
+ * The id frontline keys its own logs by, minted server-side and returned as
424
+ * `event_id` on an error envelope. Quote it when raising a ticket with
425
+ * CoinList support: it is what leads from a partner's log line to the request
426
+ * as the backend saw it.
427
+ *
428
+ * A {@link Newtype} rather than a bare `string` because it sits next to
429
+ * {@link RequestId} and frontline's `code` on the same cause, and the three
430
+ * are not interchangeable: an id swapped for a code reads plausibly and points
431
+ * a support engineer at nothing.
432
+ */
433
+ type FrontlineEventId = Newtype<string, 'FrontlineEventId'>;
434
+ /**
435
+ * What the SDK knows about a failure, classified.
436
+ *
437
+ * **Safe to forward as it stands.** Every arm is SDK-authored classification
438
+ * or a server-authored identifier: no request or response body, no host-
439
+ * supplied parameter, and no value read off a thrown error the SDK did not
440
+ * author. There is nothing here to strip before handing it to an error
441
+ * tracker, which is deliberate - a `LogCause` reaches {@link Logger.error},
442
+ * the level a host actually runs in production, and a guarantee that depends
443
+ * on the host remembering to strip a field is not a guarantee.
444
+ *
445
+ * It is the SDK's **only** structured classification of a thrown error, and it
446
+ * reaches {@link Logger.warn} as well as {@link Logger.error}: a recoverable
447
+ * failure is no less worth naming than a fatal one, and rendering it into a
448
+ * string instead would have meant interpolating into `msg`.
449
+ *
450
+ * Bodies are a `'debug'`-only disclosure and stay on the `HTTP` lines, which
451
+ * {@link RequestId} points at.
452
+ *
453
+ * The arms are the remedies, not the exception classes: a `validation` cause
454
+ * means the backend sent something the SDK does not understand and is worth
455
+ * reporting to CoinList, while an `invariant` cause means the SDK itself
456
+ * computed something impossible and is worth reporting as an SDK bug. Reading
457
+ * `type` should tell you who has to fix it.
458
+ *
459
+ * It carries no scope of its own. The {@link SafeEvent} it arrives on has one,
460
+ * and a cause could only ever have repeated it: the scope is stamped by the
461
+ * logger that *emits* the line, not by the layer that raised the error, so a
462
+ * `validation` failure from `OFFERS` surfaced by `useOffers` would have been
463
+ * tagged `HOOKS` on the hook's line. Forward the event, not the cause alone.
464
+ */
465
+ type LogCause =
466
+ /**
467
+ * A request reached the backend and came back non-2xx.
468
+ *
469
+ * Deliberately carries no response body. `requestId` is how you get the
470
+ * rest: the `HTTP` lines for the same request carry the method, the path,
471
+ * the duration, every retry, and at `'debug'` the body itself. `code` and
472
+ * `eventId` are frontline's own - quote `eventId` when raising a ticket with
473
+ * CoinList support.
474
+ */
475
+ {
476
+ type: 'http';
477
+ requestId: RequestId | null;
478
+ status: number;
479
+ code: string | null;
480
+ eventId: FrontlineEventId | null;
481
+ }
482
+ /**
483
+ * The wire succeeded; the payload was not a shape the SDK can map.
484
+ *
485
+ * `message` is an SDK-authored template and may quote the single scalar that
486
+ * failed - `Unsupported chain: "solana_mainnet"`, `expires_at: not a date
487
+ * ("13/40/2026")`. That one value is the point of the arm; it is never a
488
+ * body and never a whole field set.
489
+ */
490
+ | {
491
+ type: 'validation';
492
+ message: string;
493
+ }
494
+ /**
495
+ * The SDK computed something impossible. An SDK bug - please report it.
496
+ * `message` is an SDK-authored template, like `validation`'s.
497
+ */
498
+ | {
499
+ type: 'invariant';
500
+ message: string;
501
+ }
502
+ /**
503
+ * Every operand was well-formed and the arithmetic on them still had no
504
+ * answer: a division by zero, a result outside the range its type can hold.
505
+ * `message` is an SDK-authored template, like `validation`'s.
506
+ *
507
+ * Separate from `invariant` because the remedy differs. An `invariant` says
508
+ * the SDK computed something impossible and is a bug to report against the
509
+ * SDK; a `math` says the operation was well-posed and the operands, which
510
+ * came off the wire, admit no result - so it points at the response the same
511
+ * way `validation` does, one step later.
512
+ */
513
+ | {
514
+ type: 'math';
515
+ message: string;
516
+ }
517
+ /** An API-backed call was made without a logged-in user. */
518
+ | {
519
+ type: 'not-authenticated';
520
+ }
521
+ /** A code path the SDK has not shipped yet. */
522
+ | {
523
+ type: 'not-implemented';
524
+ }
525
+ /**
526
+ * The user's wallet, or the chain, refused.
527
+ *
528
+ * {@link RedactedWalletError} rather than the full `WalletError`: its
529
+ * `unknown` arm arrives with its `cause` dropped, that field holding the raw
530
+ * wallet-library error whose message and metadata carry the transaction's
531
+ * `from`, `to`, `value` and calldata. It is still on the flow's own returned
532
+ * result, and on the `'debug'` line the flow seam emits beside this one.
533
+ *
534
+ * A type rather than a scrub applied at the call site, so an arm that grew a
535
+ * raw field would fail the build.
536
+ */
537
+ | {
538
+ type: 'wallet';
539
+ error: RedactedWalletError;
540
+ }
541
+ /**
542
+ * Something the SDK does not recognise - usually a host-supplied lambda
543
+ * (`getAccessToken`, an {@link EvmWallet} method) throwing, or a
544
+ * network-level `fetch` failure.
545
+ *
546
+ * `name` is the error's class name and nothing else. The message and the
547
+ * thrown value itself are host-authored, so they are a `'debug'` disclosure:
548
+ * the paired debug line renders them in full.
549
+ */
550
+ | {
551
+ type: 'generic-error';
552
+ name: string;
553
+ };
554
+
23
555
  /**
24
556
  * The exhaustive chain set: a new {@link EthereumChain} is a compile error
25
557
  * here until it is listed, which is what keeps the constructor total.
26
558
  */
27
559
  declare const ETHEREUM_CHAINS: Record<EthereumChain, true>;
28
- type EthereumChain = 'ethereum_mainnet' | 'ethereum_sepolia';
560
+ type EthereumChain = 'ethereum_mainnet' | 'ethereum_sepolia' | 'base_mainnet' | 'base_sepolia';
29
561
  /**
30
562
  * Validates that a raw backend string names a chain the SDK supports.
31
563
  *
@@ -147,11 +679,25 @@ declare const MAX_UINT_256: bigint;
147
679
  */
148
680
  type Uint256 = bigint;
149
681
  /**
150
- * Asserts a raw bigint falls within uint256 bounds, throwing otherwise. Use at
151
- * on-chain arithmetic boundaries (bps math, price computation) where a computed
152
- * value could underflow below zero or overflow above 2^256-1.
682
+ * Asserts a computed bigint falls within uint256 bounds. Use at on-chain
683
+ * arithmetic boundaries (bps math, price computation) where the SDK's own
684
+ * arithmetic could underflow below zero or overflow above 2^256-1.
685
+ *
686
+ * Throws {@link InvariantError}, because reaching it means the SDK computed
687
+ * something no chain could represent. **For a value that came off the wire,
688
+ * use {@link parseUint256} instead**: the backend sending an unrepresentable
689
+ * number is a {@link ValidationError}, and the two have different remedies.
153
690
  */
154
691
  declare const assertUint256: (value: bigint) => Uint256;
692
+ /**
693
+ * {@link assertUint256} at a wire boundary: the same bounds check, reported as
694
+ * a {@link ValidationError} against the named field, because a value out of
695
+ * range here is the backend's, not the SDK's.
696
+ *
697
+ * `label` names the field the way a DTO mapper's other failures do, so a log
698
+ * line says which one - `SwapPreview.pay_input_amount`, not `a number`.
699
+ */
700
+ declare const parseUint256: (value: bigint, label: string) => Uint256;
155
701
  type BlockchainAmount = Newtype<{
156
702
  raw: Uint256;
157
703
  decimals: AssetDecimals;
@@ -159,7 +705,7 @@ type BlockchainAmount = Newtype<{
159
705
  /**
160
706
  * Constructs a {@link BlockchainAmount} and exposes arithmetic helpers.
161
707
  * TypeScript has no operator overloading, so use `BlockchainAmount.add(a, b)`
162
- * instead of `+`/`-` on the objects directly.
708
+ * instead of `+`/`-`/`*`/`/` on the objects directly.
163
709
  */
164
710
  declare const BlockchainAmount: ((value: {
165
711
  raw: Uint256;
@@ -167,7 +713,57 @@ declare const BlockchainAmount: ((value: {
167
713
  }) => BlockchainAmount) & {
168
714
  add: (a: BlockchainAmount, b: BlockchainAmount) => BlockchainAmount;
169
715
  sub: (a: BlockchainAmount, b: BlockchainAmount) => BlockchainAmount;
716
+ mul: typeof multiplyAmounts;
717
+ div: typeof divideAmounts;
170
718
  };
719
+ /**
720
+ * `a * b`, denominated in `a`'s decimals - a price times a quantity, an amount
721
+ * times a rate.
722
+ *
723
+ * The exact inverse of {@link divideAmounts}, and it reads the same way round:
724
+ * the answer stays in `a`'s scale and `b`'s divides back out, so the two
725
+ * compose - `mul(div(a, b), b)` is `a` again, short only what truncation took.
726
+ * That is what makes `price x quantity` land in the currency the price was
727
+ * quoted in rather than at some product of two exponents no token uses.
728
+ *
729
+ * The multiplication happens *before* the division by `10^b.decimals`, so the
730
+ * full precision of both operands survives into the one rounding at the end.
731
+ * Like `div` it truncates towards zero on both signs, which is the safe
732
+ * direction for money: a total is never inflated past what the parts hold.
733
+ *
734
+ * Total - unlike `div`, there is nothing here to reject. The scale it divides
735
+ * by is a power of ten, never zero, and a product is not bounds-checked for
736
+ * the same reason a quotient is not: whether one past uint256 is a corrupt
737
+ * response or an expected magnitude belongs to the caller.
738
+ */
739
+ declare function multiplyAmounts(a: BlockchainAmount, b: BlockchainAmount): BlockchainAmount;
740
+ /**
741
+ * `a / b`, denominated in `a`'s decimals - a price, a ratio, a rate.
742
+ *
743
+ * Unlike {@link combineAmounts}, the two operands may be at different scales,
744
+ * and usually are: dividing dollars by shares is the point of the operation,
745
+ * and the two tokens rarely share an exponent. `b`'s scale divides back out -
746
+ * the numerator is scaled by `10^b.decimals` *before* the division, so nothing
747
+ * is lost to integer truncation early and the quotient lands in `a`'s scale,
748
+ * which is the one the amounts it is rendered beside are in.
749
+ *
750
+ * Truncates towards zero on both signs, which is what BigInt division already
751
+ * does and the safe direction for money: the total a price implies never
752
+ * exceeds, in magnitude, the amount that actually moved.
753
+ *
754
+ * A zero divisor is the only rejection. The quotient is deliberately *not*
755
+ * bounds-checked the way {@link combineAmounts} checks a sum: scaling the
756
+ * numerator carries even legal pairs past uint256 (a maximal amount over a
757
+ * single base unit), and whether that reads as a corrupt response or an
758
+ * expected magnitude is the caller's to judge - see `computeOndoBuyPrice` and
759
+ * `computeOndoSellPrice`, which bounds-check the price they build out of this.
760
+ *
761
+ * @throws MathError when `b` is zero. `add` and `sub` throw a bare `Error`
762
+ * because a scale mismatch is a programming error caught in review; a zero
763
+ * divisor arrives from a server response and is divided by during render, so
764
+ * it is a named failure a screen can catch and map to a failed state.
765
+ */
766
+ declare function divideAmounts(a: BlockchainAmount, b: BlockchainAmount): BlockchainAmount;
171
767
  type AssetSymbol = Newtype<string, 'AssetSymbol'>;
172
768
  declare const AssetSymbol: (value: string) => AssetSymbol;
173
769
  /**
@@ -225,6 +821,13 @@ declare const OAuthSession: {
225
821
  };
226
822
 
227
823
  type HttpRequestAttributes = {
824
+ /**
825
+ * Correlates the log lines of one logical request. Minted by
826
+ * {@link HttpClient} on the first attempt and carried onto every retry and
827
+ * post-renewal re-send by {@link concat}, which is what lets a reader tell
828
+ * three attempts of one request from three separate requests.
829
+ */
830
+ requestId?: RequestId;
228
831
  protected?: boolean;
229
832
  userAgent?: boolean;
230
833
  idempotencyKey?: boolean;
@@ -272,10 +875,25 @@ type HttpResponse<TBody = unknown> = {
272
875
  status: number;
273
876
  headers?: Record<string, string>;
274
877
  body: TBody | null;
878
+ /**
879
+ * The id of the request that produced this response, stamped by
880
+ * {@link HttpClient} so that whoever turns a non-2xx into an
881
+ * {@link HttpError} can carry it without threading the request alongside.
882
+ * Absent when the response did not come from an `HttpClient`.
883
+ */
884
+ requestId?: RequestId;
275
885
  };
276
886
  declare class HttpError<TBody = unknown> extends Error {
277
887
  readonly response: HttpResponse<TBody>;
278
888
  constructor(response: HttpResponse<TBody>);
889
+ /**
890
+ * Correlates this failure with the `[HTTP]` log lines for the same request,
891
+ * which carry the method, the URL, the duration and every retry. `null` when
892
+ * the response did not come from an {@link HttpClient}.
893
+ *
894
+ * Worth quoting in a bug report: it is what makes a log excerpt readable.
895
+ */
896
+ get requestId(): RequestId | null;
279
897
  }
280
898
  /**
281
899
  * The machine-readable `code` frontline attaches to an error, or `null` for
@@ -304,6 +922,15 @@ interface Sender {
304
922
 
305
923
  interface SharedNamespaceContext {
306
924
  readonly api: Sender;
925
+ /**
926
+ * The host's logger, or `null` when they supplied none.
927
+ *
928
+ * Deliberately the raw port rather than a pre-scoped {@link InternalLogger}:
929
+ * one context serves every namespace, and each namespace owns its own scope.
930
+ * A namespace turns it into something usable in its constructor, with
931
+ * `internalLogger(ctx.logger, 'OFFERS')`.
932
+ */
933
+ readonly logger: Logger | null;
307
934
  ensureUserAuthenticated(): Promise<void>;
308
935
  }
309
936
 
@@ -430,11 +1057,89 @@ interface Erc20Namespace {
430
1057
  }
431
1058
  declare class Erc20NamespaceImpl implements Erc20Namespace {
432
1059
  private readonly ctx;
1060
+ private readonly log;
433
1061
  constructor(ctx: SharedNamespaceContext);
434
1062
  getAllowance(params: GetTokenAllowanceParams): Promise<TokenAllowance>;
435
1063
  getBalance(params: GetTokenBalanceParams): Promise<TokenBalance>;
436
1064
  }
437
1065
 
1066
+ /**
1067
+ * The logging surface SDK code uses. Never exported to hosts: they implement
1068
+ * {@link Logger}, which is deliberately smaller.
1069
+ */
1070
+ type InternalLogger = {
1071
+ /**
1072
+ * A logger narrowed by one more {@link LogBinding}, which every line it
1073
+ * emits carries as its own field.
1074
+ */
1075
+ child(binding: LogBinding): InternalLogger;
1076
+ /**
1077
+ * The unredacted level. Request and response bodies, full URLs, headers,
1078
+ * operation params and raw thrown errors belong here and **only** here.
1079
+ */
1080
+ debug(event: () => InternalDebugEvent): void;
1081
+ info(event: () => InternalSafeEvent): void;
1082
+ warn(event: () => InternalSafeEvent): void;
1083
+ /**
1084
+ * Reports a failure whose cause is already known, or has none. Prefer
1085
+ * {@link failure}, which classifies a thrown error for you.
1086
+ */
1087
+ error(event: () => InternalSafeEvent): void;
1088
+ /**
1089
+ * Reports a thrown error, classifying it into a {@link LogCause}. Prefer
1090
+ * this over `error` at a `catch`: classification only happens when the level
1091
+ * admits it.
1092
+ *
1093
+ * The `error` line names the error's class but never its message, since an
1094
+ * error the SDK did not author may say anything at all. The verbatim
1095
+ * rendering goes onto a paired `debug` line instead.
1096
+ *
1097
+ * For a failure the SDK recovers from, use {@link warning} instead. An
1098
+ * `error` line a host cannot act on is worse than no line at all: it trains
1099
+ * them to ignore the ones that matter.
1100
+ */
1101
+ failure(event: () => InternalSafeEvent, error: unknown): void;
1102
+ /**
1103
+ * {@link failure} for trouble that did not break anything: a poll tick that
1104
+ * failed while the last value still stands, a retried request.
1105
+ *
1106
+ * Carries the classified {@link LogCause} exactly as `failure` does. The two
1107
+ * differ in level and in nothing else, because what separates them is
1108
+ * whether anything actually broke - not how much is known about it.
1109
+ */
1110
+ warning(event: () => InternalSafeEvent, error: unknown): void;
1111
+ /**
1112
+ * Runs one namespace operation with logging around it: the call and its
1113
+ * params at `debug`, and any throw at `error` with the cause classified.
1114
+ * Every line carries `op` as a binding.
1115
+ *
1116
+ * **The params never reach the `error` line.** They routinely hold the
1117
+ * things this SDK must not put in front of a production error tracker - an
1118
+ * app bearer token, a document's signing fields, a wallet signature - and
1119
+ * the `debug` line plus the request id is how you get them back. The type
1120
+ * says so: `params` is `unknown`, which is not a {@link SafeFields} value.
1121
+ *
1122
+ * Always rethrows. Logging observes behaviour, it never changes it.
1123
+ */
1124
+ wrap<T>(op: string, params: unknown, run: () => Promise<T>): Promise<T>;
1125
+ };
1126
+ /**
1127
+ * What a call site hands to `info`, `warn`, `error`, `failure` or `warning` -
1128
+ * a {@link SafeEvent} minus the scope and bindings the logger stamps itself.
1129
+ */
1130
+ type InternalSafeEvent = {
1131
+ /** A **constant** string literal. Every varying value belongs in `fields`. */
1132
+ readonly msg: string;
1133
+ readonly fields?: SafeFields;
1134
+ readonly cause?: LogCause;
1135
+ };
1136
+ /** What a call site hands to `debug`. Its fields accept anything. */
1137
+ type InternalDebugEvent = {
1138
+ /** A **constant** string literal. Every varying value belongs in `fields`. */
1139
+ readonly msg: string;
1140
+ readonly fields?: UnredactedFields;
1141
+ };
1142
+
438
1143
  /**
439
1144
  * The product an offer is checked out through. The backend sends one compound
440
1145
  * `{supplier}::{saleType}` string, verbatim as spelled below: a double colon
@@ -456,6 +1161,12 @@ type OfferDto = {
456
1161
  logo_url: string;
457
1162
  starts_at: string;
458
1163
  ends_at: string | null;
1164
+ tokens: OfferTokenDto[];
1165
+ };
1166
+ type OfferTokenDto = {
1167
+ role: 'funding' | 'distribution' | 'swap';
1168
+ chain: string;
1169
+ address: string;
459
1170
  };
460
1171
 
461
1172
  type OfferId = Newtype<string, 'OfferId'>;
@@ -472,10 +1183,20 @@ type Offer = {
472
1183
  logoUrl: string;
473
1184
  startsAt: Date;
474
1185
  endsAt: Date | null;
1186
+ tokens: OfferToken[];
475
1187
  };
476
1188
  declare const Offer: {
477
1189
  fromDto: (dto: OfferDto) => Offer;
478
1190
  };
1191
+ type TokenRole = 'funding' | 'distribution' | 'swap';
1192
+ type OfferToken = {
1193
+ role: TokenRole;
1194
+ chain: Chain;
1195
+ address: EvmContractAddress;
1196
+ };
1197
+ declare const OfferToken: {
1198
+ fromDto: (dto: OfferTokenDto) => OfferToken;
1199
+ };
479
1200
 
480
1201
  /**
481
1202
  * The cursor-paginated envelope every list endpoint returns.
@@ -563,7 +1284,7 @@ type OfferDetailDto = {
563
1284
  asset: AssetDto;
564
1285
  faqs: OfferDetailFaqDto[];
565
1286
  funding_assets: AssetDto[];
566
- tokens: OfferDetailTokenDto[];
1287
+ tokens: OfferTokenDto[];
567
1288
  id: string;
568
1289
  links: OfferDetailLinkDto[];
569
1290
  milestones: OfferDetailMilestoneDto[];
@@ -608,11 +1329,6 @@ type OfferDetailTermDto = {
608
1329
  key: string | null;
609
1330
  value: string | null;
610
1331
  };
611
- type OfferDetailTokenDto = {
612
- role: 'funding' | 'distribution' | 'swap';
613
- chain: string;
614
- address: string;
615
- };
616
1332
 
617
1333
  type OfferOptionId = Newtype<string, 'OfferOptionId'>;
618
1334
  declare const OfferOptionId: (value: string) => OfferOptionId;
@@ -684,15 +1400,6 @@ type Milestone = {
684
1400
  declare const Milestone: {
685
1401
  fromDto: (dto: OfferDetailMilestoneDto) => Milestone;
686
1402
  };
687
- type TokenRole = 'funding' | 'distribution' | 'swap';
688
- type OfferToken = {
689
- role: TokenRole;
690
- chain: Chain;
691
- address: EvmContractAddress;
692
- };
693
- declare const OfferToken: {
694
- fromDto: (dto: OfferDetailTokenDto) => OfferToken;
695
- };
696
1403
 
697
1404
  /** Unique identifier for a participation. */
698
1405
  type ParticipationId = Newtype<string, 'ParticipationId'>;
@@ -812,6 +1519,7 @@ interface CoinListTokenSaleNamespace {
812
1519
  }
813
1520
  declare class CoinListTokenSaleNamespaceImpl implements CoinListTokenSaleNamespace {
814
1521
  private readonly ctx;
1522
+ protected readonly log: InternalLogger;
815
1523
  constructor(ctx: SharedNamespaceContext);
816
1524
  list(offerId?: OfferId): Promise<Participation[]>;
817
1525
  listPage(params: ParticipationsPaginationParams): Promise<PaginatedResponse<Participation>>;
@@ -855,53 +1563,98 @@ type GetOndoQuoteParams = {
855
1563
  duration?: OndoQuoteDuration;
856
1564
  } & OndoQuoteSize;
857
1565
  /**
858
- * What it takes to turn an indicative price into signed, fillable calldata.
859
- *
860
- * Sized by `amount` alone - the coin being spent, in its own base units - with
861
- * no `notionalValue` alternative: the calldata authorises a specific ERC-20
862
- * pull, so the number that ends up on chain has to be the number the caller
863
- * meant, not one derived from a dollar figure. It is the **gross**: CoinList's
864
- * fee comes off it, and Ondo prices the remainder.
1566
+ * What both builders take, before the one field whose meaning forks.
865
1567
  *
866
- * Buy-only, so there is no `side`. There is no funding token either - frontline
867
- * resolves both tokens from the offer, because Ninshubur signs a request bound
868
- * to them and a caller that could name them could have CoinList sign for a
869
- * contract of its own.
1568
+ * There is no funding token and no asset here - frontline resolves both from
1569
+ * the offer, because Ninshubur signs a request bound to them and a caller that
1570
+ * could name them could have CoinList sign for a contract of its own. Which
1571
+ * endpoint was called is what says which of the two `amount` counts.
870
1572
  *
871
- * `chain` is required here although the read params refuse it, because this
872
- * one names a real contract on a real chain rather than asking Ondo for a
873
- * price. `walletAddress` must be the wallet that will *send* the transaction:
874
- * the calldata is signed over it, so a transaction built for one wallet and
875
- * broadcast by another reverts.
1573
+ * `chain` is required although the read params refuse it, because these name a
1574
+ * real contract on a real chain rather than asking Ondo for a price.
876
1575
  */
877
- type BuildOndoSwapTransactionParams = {
1576
+ type BuildOndoSwapParamsCore = {
878
1577
  symbol: AssetSymbol;
879
1578
  chain: EthereumChain;
880
- /** The wallet that will broadcast, and that receives the asset. */
1579
+ /**
1580
+ * The wallet that will *send* the transaction, and that receives the other
1581
+ * token. The calldata is signed over it, so a transaction built for one
1582
+ * wallet and broadcast by another reverts.
1583
+ */
881
1584
  walletAddress: EvmWalletAddress;
1585
+ };
1586
+ /**
1587
+ * What it takes to turn an indicative price into signed, fillable calldata for
1588
+ * a purchase: `POST /v1/ondo/swap/buy`.
1589
+ *
1590
+ * Separate from {@link BuildOndoSellParams} although the fields match today,
1591
+ * because `amount` is denominated in a different token on each - which is
1592
+ * frontline's own reason for splitting the endpoint rather than taking a
1593
+ * `side`. Sharing one type would re-merge the distinction the split exists to
1594
+ * make, and the two will diverge the day either side gains a knob.
1595
+ */
1596
+ type BuildOndoBuyParams = BuildOndoSwapParamsCore & {
882
1597
  /**
883
- * The gross amount to spend, in the funding token's base units.
1598
+ * The **gross** deposit, in the base units of the funding token - the coin
1599
+ * the user chose and approved.
884
1600
  *
885
- * Its `decimals` are also what the response's `pay_input_decimals` is
1601
+ * Sized by `amount` alone, with no `notionalValue` alternative: the calldata
1602
+ * authorises a specific ERC-20 pull, so the number that ends up on chain has
1603
+ * to be the number the caller meant rather than one derived from a dollar
1604
+ * figure. CoinList's fee comes off it, and Ondo prices the remainder.
1605
+ *
1606
+ * Its `decimals` are also what the response's `spend_input_decimals` is
886
1607
  * checked against: frontline resolves the funding token from the offer
887
1608
  * rather than from this request, so the two are independent answers to the
888
1609
  * same question and a disagreement means the wrong token was sized.
889
1610
  */
890
1611
  amount: BlockchainAmount;
891
1612
  };
1613
+ /**
1614
+ * What it takes to turn an indicative price into signed, fillable calldata for
1615
+ * a sale: `POST /v1/ondo/swap/sell`.
1616
+ *
1617
+ * See {@link BuildOndoBuyParams} for why this is its own type.
1618
+ */
1619
+ type BuildOndoSellParams = BuildOndoSwapParamsCore & {
1620
+ /**
1621
+ * The quantity of the **asset** to sell, in the asset's own base units - not
1622
+ * in the funding token's, which is the same integer meaning something 1e12
1623
+ * different.
1624
+ *
1625
+ * This is the figure the wallet must have approved: a sale delivers the
1626
+ * asset, so the swap contract pulls it with `transferFrom` exactly as it
1627
+ * pulls the deposit on a purchase.
1628
+ *
1629
+ * Its `decimals` are checked against the response's `spend_input_decimals`,
1630
+ * which frontline reads on-chain from the asset on `chain`. The SDK's own
1631
+ * answer comes from the quote, which resolves the asset on Ethereum mainnet,
1632
+ * so on a testnet these are two independent resolutions of two different
1633
+ * contracts - and the check is what says so.
1634
+ */
1635
+ amount: BlockchainAmount;
1636
+ };
892
1637
 
893
1638
  /**
894
1639
  * Raw JSON models for the Ondo swap endpoints, mirroring
895
- * `OndoSwapTradingStatus`, `OndoSwapQuote` and `OndoSwapTransaction` in
896
- * frontline's OpenAPI schema. The two GETs are free to poll: neither spends an
897
- * attestation, so a client may call them while the user edits an order. The
898
- * POST ({@link OndoSwapTransactionDto}) is not - it spends one and hands back
899
- * signed calldata with a deadline.
1640
+ * `OndoSwapTradingStatus`, `OndoSwapQuote`, `OndoSwapBuy` and `OndoSwapSell`
1641
+ * in frontline's OpenAPI schema. The two GETs are free to poll: neither spends
1642
+ * an attestation, so a client may call them while the user edits an order. The
1643
+ * two POSTs are not - each spends one and hands back signed calldata with a
1644
+ * deadline.
1645
+ *
1646
+ * **One endpoint per side, and therefore one model per side.** The reads take
1647
+ * a `side` and answer the same shape either way, so they stay single. The
1648
+ * writes do not: a buy commits to an exact quantity, a sell to a range with a
1649
+ * floor beneath it, and `amount` is the funding token on one and the asset on
1650
+ * the other. Only the transport fields mean the same thing on both.
1651
+ * `POST /v1/ondo/swap/transaction` survives as a deprecated alias of the buy
1652
+ * under the old `pay_input_*` names; the SDK does not call it.
900
1653
  *
901
1654
  * Neither GET takes a `chain`. Ondo runs no sandbox, so every environment
902
- * prices against Ondo production on Ethereum mainnet. The POST does carry one:
903
- * it targets a real contract, which on every environment but production is the
904
- * Sepolia one with the mocked attestation.
1655
+ * prices against Ondo production on Ethereum mainnet. The POSTs do carry one:
1656
+ * they target a real contract, which on every environment but production is
1657
+ * the Sepolia one with the mocked attestation.
905
1658
  */
906
1659
  /**
907
1660
  * Whether an asset can be traded right now, and the caps if so.
@@ -964,34 +1717,19 @@ type OndoQuoteDto = {
964
1717
  price: string;
965
1718
  };
966
1719
  /**
967
- * Signed, ready-to-broadcast calldata for a buy, and the amounts it commits
968
- * to: `POST /v1/ondo/swap/transaction`.
969
- *
970
- * Unlike {@link OndoQuoteDto} this **spends an attestation**, so it is not
971
- * pollable: one call per order, plus one per user-requested refresh. Frontline
972
- * also reads the wallet's allowance before asking Ninshubur for anything, so
973
- * an unapproved wallet is refused here rather than reverting on chain.
1720
+ * The transport half of a built swap, identical on both sides.
974
1721
  *
975
- * Buy-only. There is no `side`: the funding token goes in and the asset comes
976
- * out, both resolved from the offer, so a caller cannot name either.
1722
+ * `spend_input_amount` is the only key here whose *token* depends on the side,
1723
+ * and `spend_input_decimals` is what says at what scale. Everything below this
1724
+ * point differs, which is why the two responses are two types rather than one
1725
+ * with nullable halves.
977
1726
  *
978
- * **Carries no identity and no price.** No `chain_id`, `symbol`, `ticker`,
979
- * `side` or `price` - the request named the first few and frontline drops
980
- * Ninshubur's `price` deliberately, because `GET /v1/ondo/swap/quote` already
981
- * publishes one under that name at a different scale. Anything the UI needs
982
- * beyond the amounts comes from that GET or from the offer.
983
- *
984
- * Every amount is a uint256 decimal string in one of two scales, and **both
985
- * scales are on the wire**: `receive_output_amount` is in
986
- * `receive_output_decimals`, and the other three are in
987
- * `pay_input_decimals`. Neither is interchangeable with the quote's
988
- * `asset_decimals`, which answers for a different number - see the two fields
989
- * below.
990
- *
991
- * The response has no `object` envelope. `action` says what to do with the
992
- * body, matching `AllowWalletResponseDto`.
1727
+ * There is no `object` envelope and no `side` echo. `action` says what to do
1728
+ * with the body, matching `AllowWalletResponseDto`; which trade it encodes is
1729
+ * settled by the endpoint that was called, so nothing on the wire has to say
1730
+ * it and nothing has to be checked against it.
993
1731
  */
994
- type OndoSwapTransactionDto = {
1732
+ type OndoSwapDtoCore = {
995
1733
  action: 'broadcast_transaction';
996
1734
  /** The swap contract the transaction is sent to. */
997
1735
  to: string;
@@ -1005,36 +1743,74 @@ type OndoSwapTransactionDto = {
1005
1743
  */
1006
1744
  expires_at: string;
1007
1745
  /**
1008
- * Gross amount the wallet pays, echoing the requested `amount`, in the
1009
- * funding token's smallest unit. The approval is compared against this.
1746
+ * Gross amount the wallet spends, echoing the requested `amount`, in the
1747
+ * smallest unit of the token this side spends - the funding token on a buy,
1748
+ * the asset on a sell. The approval is compared against this.
1749
+ *
1750
+ * Named `pay_input_amount` on the deprecated
1751
+ * `POST /v1/ondo/swap/transaction`. `pay` was accurate only while a buy was
1752
+ * the sole thing this could encode: a sell delivers the asset rather than
1753
+ * paying for one.
1010
1754
  */
1011
- pay_input_amount: string;
1755
+ spend_input_amount: string;
1012
1756
  /**
1013
- * Decimals `pay_input_amount`, `fee` and `notional_value` are counted in.
1757
+ * Decimals `spend_input_amount` is counted in.
1758
+ *
1759
+ * Frontline reads it on-chain from the token the side spends, which it
1760
+ * resolves from the offer rather than from anything the caller sent. That
1761
+ * makes it the only published scale for a token the request never names -
1762
+ * and an independent answer to the one the SDK derived when it sized the
1763
+ * order, which is why both builders compare the two.
1014
1764
  *
1015
- * Frontline reads it on-chain from the funding token, which it resolves from
1016
- * the offer rather than from anything the caller sent. That makes it the
1017
- * only published scale for a token the request never names - and an
1018
- * independent answer to the one the SDK derived when it sized the order,
1019
- * which is why `buildOndoSwapTransaction` compares the two.
1765
+ * **The same key, two scales.** It counts whichever token the side spends:
1766
+ * the funding token's 6 on a buy, the asset's 18 on a sell. Worth knowing
1767
+ * before integrating against both.
1020
1768
  */
1021
- pay_input_decimals: number;
1769
+ spend_input_decimals: number;
1770
+ };
1771
+ /**
1772
+ * Signed, ready-to-broadcast calldata for a purchase, and the amounts it
1773
+ * commits to: `POST /v1/ondo/swap/buy`.
1774
+ *
1775
+ * Unlike {@link OndoQuoteDto} this **spends an attestation**, so it is not
1776
+ * pollable: one call per order, plus one per user-requested refresh. Frontline
1777
+ * also reads the wallet's allowance on the funding token before asking
1778
+ * Ninshubur for anything, so an unapproved wallet is refused here rather than
1779
+ * reverting on chain.
1780
+ *
1781
+ * The quantity is attested and exact, with no floor beneath it, which is what
1782
+ * separates a buy from an {@link OndoSellDto}.
1783
+ *
1784
+ * **Carries no identity and no price.** No `chain_id`, `symbol`, `ticker` or
1785
+ * `price` - the request named the first few and frontline drops Ninshubur's
1786
+ * `price` deliberately, because `GET /v1/ondo/swap/quote` already publishes
1787
+ * one under that name at a different scale. Anything the UI needs beyond the
1788
+ * amounts comes from that GET or from the offer.
1789
+ *
1790
+ * Two scales are on the wire: `fee` and `notional_value` are in
1791
+ * `spend_input_decimals`, and `receive_output_amount` is in
1792
+ * `receive_output_decimals`. Neither is interchangeable with the quote's
1793
+ * `asset_decimals`, which answers for a different number.
1794
+ */
1795
+ type OndoBuyDto = OndoSwapDtoCore & {
1022
1796
  /**
1023
- * CoinList's cut of `pay_input_amount`, in the same units. Taken off the
1024
- * deposit rather than added on top, so the approval never has to cover more.
1025
- * `"0"` until ENG-1718 turns a fee on - frontline rejects a non-zero one
1026
- * today.
1797
+ * CoinList's cut of `spend_input_amount`, in the same units. `"0"` until
1798
+ * ENG-1718 turns a fee on - frontline rejects a non-zero one today.
1799
+ *
1800
+ * Taken at execution rather than added on top, so the wallet never approves
1801
+ * more than `spend_input_amount`.
1027
1802
  */
1028
1803
  fee: string;
1029
1804
  /**
1030
- * `pay_input_amount` less `fee`, in the same units. This is the amount Ondo
1031
- * actually priced, and the numerator of the fill price.
1805
+ * `spend_input_amount` less `fee`, in `spend_input_decimals`: the part that
1806
+ * reaches Ondo, what the quantity was priced against, and what the
1807
+ * signature commits to.
1032
1808
  */
1033
1809
  notional_value: string;
1034
1810
  /**
1035
- * Quantity of the asset the wallet receives, in the *asset's* smallest unit,
1036
- * e.g. `"264000000000000000"`. Scale it by `receive_output_decimals`, not by
1037
- * the funding token's and not by {@link OndoQuoteDto}'s `asset_decimals`.
1811
+ * Quantity of the asset the wallet receives, in the asset's smallest unit.
1812
+ * Scale it by `receive_output_decimals`, not by the funding token's and not
1813
+ * by {@link OndoQuoteDto}'s `asset_decimals`.
1038
1814
  */
1039
1815
  receive_output_amount: string;
1040
1816
  /**
@@ -1048,6 +1824,74 @@ type OndoSwapTransactionDto = {
1048
1824
  */
1049
1825
  receive_output_decimals: number;
1050
1826
  };
1827
+ /**
1828
+ * Signed, ready-to-broadcast calldata for a sale, and the range it commits to:
1829
+ * `POST /v1/ondo/swap/sell`.
1830
+ *
1831
+ * Like {@link OndoBuyDto} it **spends an attestation** and expires, so it is
1832
+ * called once on confirmation and never on a timer. Frontline reads the
1833
+ * wallet's allowance on the **asset** before signing - a sell delivers it -
1834
+ * and refuses a short one with a 422 naming that address.
1835
+ *
1836
+ * **A sell commits to a range rather than a quantity.** Ondo settles through
1837
+ * USDon before converting to the settlement token, so the response publishes
1838
+ * what to expect and the floor the calldata enforces, each with the fee
1839
+ * charged at it. There is no `notional_value`: a sell commits on the output
1840
+ * side, so there is no fee-exclusive input to report and frontline declines to
1841
+ * relate numbers Ninshubur did not relate.
1842
+ *
1843
+ * All four amounts below are in `receive_output_decimals`. The one amount that
1844
+ * is not is `spend_input_amount`, in `spend_input_decimals` - see
1845
+ * {@link OndoSwapDtoCore}.
1846
+ *
1847
+ * One thing the response does not say, and the calldata does: the floor signed
1848
+ * into `data` is **gross** of CoinList's fee, so a caller decoding it finds a
1849
+ * larger number than `minimum_quantity`. Both are correct; `minimum_quantity`
1850
+ * is what the wallet actually receives.
1851
+ */
1852
+ type OndoSellDto = OndoSwapDtoCore & {
1853
+ /**
1854
+ * CoinList's cut at the expected outcome, in the settlement token. Already
1855
+ * deducted from `expected_quantity` rather than charged on top of it.
1856
+ *
1857
+ * `"0"` until ENG-1718 turns a fee on - frontline rejects a non-zero one on
1858
+ * either side today.
1859
+ */
1860
+ expected_fee: string;
1861
+ /**
1862
+ * What the sale is expected to return, **net of `expected_fee`**, in the
1863
+ * settlement token's smallest unit.
1864
+ *
1865
+ * An expectation rather than a guarantee. What the contract enforces is
1866
+ * {@link OndoSellDto.minimum_quantity}.
1867
+ */
1868
+ expected_quantity: string;
1869
+ /**
1870
+ * CoinList's cut at the floor, in the same units.
1871
+ *
1872
+ * A different number from `expected_fee` because the two are charged on
1873
+ * different amounts - which is why frontline publishes both rather than one.
1874
+ * This is the one to disclose worst-case cost with.
1875
+ */
1876
+ minimum_fee: string;
1877
+ /**
1878
+ * The least the wallet can receive, **net of `minimum_fee`**, in the same
1879
+ * units. Below it the transaction reverts on chain.
1880
+ *
1881
+ * Frontline guarantees it is at most `expected_quantity` and greater than
1882
+ * zero. The settlement lands somewhere between the two.
1883
+ */
1884
+ minimum_quantity: string;
1885
+ /**
1886
+ * Decimals both quantities and both fees are counted in, reported by
1887
+ * whatever priced them rather than looked up from the token.
1888
+ *
1889
+ * Not the same number as {@link OndoQuoteDto}'s `asset_decimals`, which
1890
+ * answers for the asset being sold rather than for the coin the proceeds
1891
+ * arrive in.
1892
+ */
1893
+ receive_output_decimals: number;
1894
+ };
1051
1895
 
1052
1896
  /**
1053
1897
  * Whether an Ondo asset can be traded right now.
@@ -1092,22 +1936,30 @@ declare const OndoTradingStatus: {
1092
1936
  * contract's `preview`, and no such contract exists for Ondo yet.
1093
1937
  *
1094
1938
  * The quote carries no transaction to broadcast and no expiry. Building one is
1095
- * a separate endpoint that spends an attestation - see
1096
- * {@link OndoSwapTransaction}.
1939
+ * a separate endpoint per side that spends an attestation - see
1940
+ * {@link OndoBuyTransaction} and {@link OndoSellTransaction}.
1097
1941
  */
1098
1942
  type OndoQuote = {
1099
1943
  /** Always `ethereum_mainnet`: Ondo runs no sandbox in any environment. */
1100
1944
  chain: EthereumChain;
1101
1945
  ticker: Ticker;
1102
- /** Needed to approve or transfer the asset; the quote is the only source. */
1946
+ /**
1947
+ * Needed to approve or transfer the asset; the quote is the only source.
1948
+ *
1949
+ * **Resolved on Ethereum mainnet**, like everything else on this quote, and
1950
+ * therefore not necessarily the contract the swap pulls from on the chain
1951
+ * the order executes on. Frontline resolves that one per chain and publishes
1952
+ * it nowhere, so on a testnet these are two different tokens. Tracked
1953
+ * against the frontline stack that follows ENG-1756.
1954
+ */
1103
1955
  assetAddress: EvmContractAddress;
1104
1956
  /**
1105
1957
  * The asset as the quote resolves it, from frontline's own catalogue.
1106
1958
  *
1107
1959
  * Its `decimals` scale {@link tokenBaseUnits} and nothing else. They are
1108
- * **not** the scale of an {@link OndoSwapTransaction}'s output: that one is
1109
- * reported by whatever priced the quantity, the two sources are allowed to
1110
- * disagree, and only the one that produced a number answers for it.
1960
+ * **not** the scale of a built transaction's spend or output: those are
1961
+ * reported by whatever priced them, the sources are allowed to disagree, and
1962
+ * only the one that produced a number answers for it.
1111
1963
  */
1112
1964
  asset: Erc20Asset;
1113
1965
  side: OrderBookSide;
@@ -1123,26 +1975,22 @@ declare const OndoQuote: {
1123
1975
  fromDto: (dto: OndoQuoteDto) => OndoQuote;
1124
1976
  };
1125
1977
  /**
1126
- * A signed, expiring buy: the calldata that fills it and the amounts it
1127
- * commits to, from `buildSwapTransaction`.
1978
+ * The half of a built swap that both sides share: the calldata, its deadline,
1979
+ * and what the wallet parts with.
1128
1980
  *
1129
- * Distinct from {@link OndoQuote} in three ways that matter: it costs an
1130
- * attestation to obtain, it expires, and it carries a {@link Tx} the wallet
1131
- * broadcasts verbatim. Treat it as single-use - once broadcast (or once
1132
- * `expiresAt` passes) it is spent, and a new one must be built.
1133
- *
1134
- * **It carries no identity.** No chain, ticker, asset or side: the endpoint
1135
- * publishes none of them, and inventing them from the request would assert
1136
- * what the server resolved rather than report it. What it does publish is the
1137
- * scale of every amount on it, so a caller needs nothing alongside it to read
1138
- * the numbers - only to name the asset, which the offer already does.
1981
+ * There is deliberately **no union over the two sides**. Each is built by its
1982
+ * own endpoint and its own namespace method, so a caller never holds one
1983
+ * without knowing which it is, and a union would only re-pose a question the
1984
+ * call site had already answered. What is genuinely common lives here, and
1985
+ * {@link executeOndoSwap} takes this rather than either arm - broadcasting
1986
+ * knows nothing about the direction of the trade.
1139
1987
  *
1140
- * It carries no price either. Divide {@link notionalValue} by
1141
- * {@link receiveOutputAmount} - see `computeOndoPrice` - which is the price
1142
- * this transaction actually fills at rather than an indicative one that has
1143
- * since moved.
1988
+ * `side` is not on this type but on each arm, as a literal the SDK authors
1989
+ * from the method that was called. The wire stopped echoing one when the
1990
+ * endpoint split, and it is still worth carrying: `OndoOrderPlaced` is a union
1991
+ * the SDK builds from both, and that union needs a tag.
1144
1992
  */
1145
- type OndoSwapTransaction = {
1993
+ type OndoSwapTransactionCore = {
1146
1994
  /**
1147
1995
  * Broadcast as-is. The `to` is the swap contract, which is also the ERC-20
1148
1996
  * spender the user must have approved.
@@ -1153,16 +2001,51 @@ type OndoSwapTransaction = {
1153
2001
  * reverts, so callers must compare against it before signing.
1154
2002
  */
1155
2003
  expiresAt: Date;
1156
- /** Gross amount the wallet pays, in the funding token's decimals. */
1157
- payInputAmount: BlockchainAmount;
1158
2004
  /**
1159
- * CoinList's cut of {@link payInputAmount}, in the same decimals. Taken off
1160
- * the deposit rather than added on top, so the approval never has to cover
1161
- * more than `payInputAmount`. Zero until ENG-1718 lands.
2005
+ * Gross amount the wallet spends, in the decimals of the token this side
2006
+ * spends: the funding token on a buy, the asset on a sell.
2007
+ *
2008
+ * The same field at two scales, which is the whole reason the two sides are
2009
+ * two types. It is also what the approval has to cover.
2010
+ */
2011
+ spendInputAmount: BlockchainAmount;
2012
+ };
2013
+ /**
2014
+ * A signed, expiring purchase: a funding token in, an exact quantity of the
2015
+ * asset out.
2016
+ *
2017
+ * Distinct from {@link OndoQuote} in three ways that matter: it costs an
2018
+ * attestation to obtain, it expires, and it carries a {@link Tx} the wallet
2019
+ * broadcasts verbatim. Treat it as single-use - once broadcast (or once
2020
+ * `expiresAt` passes) it is spent, and a new one must be built.
2021
+ *
2022
+ * **The quantity is attested and exact, with no floor beneath it.** That is
2023
+ * what separates it from an {@link OndoSellTransaction}, which commits to a
2024
+ * range: nothing here is an estimate.
2025
+ *
2026
+ * **It carries no other identity.** No chain, ticker or asset: the endpoint
2027
+ * publishes none of them, and inventing them from the request would assert
2028
+ * what the server resolved rather than report it. What it does publish is the
2029
+ * scale of every amount on it, so a caller needs nothing alongside it to read
2030
+ * the numbers - only to name the assets, which the offer already does.
2031
+ *
2032
+ * It carries no price either. See `computeOndoBuyPrice`, which derives it from
2033
+ * the amounts the response does carry - the price this transaction fills at,
2034
+ * rather than an indicative one that has since moved.
2035
+ */
2036
+ type OndoBuyTransaction = OndoSwapTransactionCore & {
2037
+ side: 'buy';
2038
+ /**
2039
+ * CoinList's cut, in `spendInputAmount`'s decimals. Zero until ENG-1718
2040
+ * lands - frontline rejects a non-zero one on either side today.
2041
+ *
2042
+ * Taken at execution rather than added on top, so the approval never has to
2043
+ * cover more than `spendInputAmount`.
1162
2044
  */
1163
2045
  fee: BlockchainAmount;
1164
2046
  /**
1165
- * `payInputAmount` less `fee`, in the same decimals: what Ondo priced.
2047
+ * `spendInputAmount` less `fee`, in the same decimals: what Ondo priced, and
2048
+ * the numerator of the fill price.
1166
2049
  *
1167
2050
  * Read from the response rather than subtracted here. Whether the fee comes
1168
2051
  * off the deposit or goes on top of it is the server's definition to change,
@@ -1170,22 +2053,86 @@ type OndoSwapTransaction = {
1170
2053
  */
1171
2054
  notionalValue: BlockchainAmount;
1172
2055
  /**
1173
- * What the buyer receives, at the scale whatever priced the quantity
1174
- * reported - not at the {@link OndoQuote}'s.
2056
+ * Quantity of the asset the wallet receives, at the scale whatever priced it
2057
+ * reported - not at the {@link OndoQuote}'s `asset.decimals`.
1175
2058
  */
1176
2059
  receiveOutputAmount: BlockchainAmount;
1177
2060
  };
1178
- declare const OndoSwapTransaction: {
1179
- fromDto: (dto: OndoSwapTransactionDto) => OndoSwapTransaction;
2061
+ declare const OndoBuyTransaction: {
2062
+ fromDto: (dto: OndoBuyDto) => OndoBuyTransaction;
2063
+ };
2064
+ /**
2065
+ * One end of the range a sale commits to: what arrives, and what CoinList took
2066
+ * to get it there.
2067
+ *
2068
+ * The two travel together because they are charged against each other -
2069
+ * `quantity` is already **net** of `fee` - and because the pair a caller wants
2070
+ * is always both halves of the same outcome. Grouping them is what makes
2071
+ * "the expected quantity, less the fee at the floor" unrepresentable rather
2072
+ * than merely wrong.
2073
+ */
2074
+ type OndoSellOutcome = {
2075
+ /** What the wallet receives at this outcome, net of {@link fee}. */
2076
+ quantity: BlockchainAmount;
2077
+ /**
2078
+ * CoinList's cut at this outcome, in the same decimals. Zero until ENG-1718
2079
+ * lands - frontline rejects a non-zero one on either side today.
2080
+ *
2081
+ * Already deducted from {@link quantity} rather than charged on top of it.
2082
+ */
2083
+ fee: BlockchainAmount;
2084
+ };
2085
+ /**
2086
+ * A signed, expiring sale: the asset in, a settlement coin out, somewhere
2087
+ * between two published outcomes.
2088
+ *
2089
+ * Single-use and expiring for the same reasons as an {@link OndoBuyTransaction},
2090
+ * and obtained the same way - one endpoint, one attestation.
2091
+ *
2092
+ * **A sale commits to a range, not a quantity.** Ondo settles through USDon
2093
+ * before converting to the settlement token, so {@link expected} is what to
2094
+ * expect and {@link minimum} is what the calldata enforces. A screen that
2095
+ * shows only the first presents a firm-looking number the contract may
2096
+ * legitimately fill below.
2097
+ *
2098
+ * There is deliberately no counterpart to a buy's `notionalValue`. A sale
2099
+ * commits on the output side, so frontline reports no fee-exclusive input and
2100
+ * refuses to relate numbers Ninshubur did not relate. What it does relate is
2101
+ * each quantity to the fee beside it, which is why {@link OndoSellOutcome}
2102
+ * pairs them.
2103
+ *
2104
+ * One thing this type cannot see: the floor signed into `tx.data` is gross of
2105
+ * the fee, so a caller decoding the calldata finds a larger number than
2106
+ * `minimum.quantity`. Both are correct; `minimum.quantity` is what the wallet
2107
+ * actually receives.
2108
+ */
2109
+ type OndoSellTransaction = OndoSwapTransactionCore & {
2110
+ side: 'sell';
2111
+ /** What the sale is expected to return. An expectation, not a guarantee. */
2112
+ expected: OndoSellOutcome;
2113
+ /**
2114
+ * The floor the calldata enforces. A fill below it reverts on chain, so this
2115
+ * - not {@link expected} - is what a seller is actually guaranteed.
2116
+ */
2117
+ minimum: OndoSellOutcome;
2118
+ };
2119
+ declare const OndoSellTransaction: {
2120
+ fromDto: (dto: OndoSellDto) => OndoSellTransaction;
1180
2121
  };
1181
2122
 
1182
2123
  /**
1183
2124
  * Ondo swap reads, plus the write that turns one into a fillable transaction.
1184
2125
  *
1185
2126
  * The two reads are free to poll - neither spends an attestation, so a client
1186
- * may call them while the user edits an order. {@link buildSwapTransaction} is
1187
- * not: budget one call per order placed, plus one per refresh the user asks
1188
- * for.
2127
+ * may call them while the user edits an order. The two builders are not:
2128
+ * budget one call per order placed, plus one per refresh the user asks for.
2129
+ *
2130
+ * **One builder per side, mirroring the endpoints.** A purchase and a sale
2131
+ * agree on how to broadcast and on nothing else: a purchase commits to an
2132
+ * exact quantity, a sale to a range with a floor beneath it, and `amount` is
2133
+ * the funding token on one and the asset on the other. A single method taking
2134
+ * a `side` would have to return a union the caller then re-narrows, having
2135
+ * already decided which trade it was placing.
1189
2136
  *
1190
2137
  * **No CoinList fee is applied to a read quote, and no read discloses one.**
1191
2138
  * Ondo prices exactly the amount passed. A CoinList approval is fee-inclusive,
@@ -1200,26 +2147,44 @@ interface OndoNamespace {
1200
2147
  getTradingStatus(params: GetOndoTradingStatusParams): Promise<OndoTradingStatus>;
1201
2148
  getQuote(params: GetOndoQuoteParams): Promise<OndoQuote>;
1202
2149
  /**
1203
- * Builds the buy for a specific wallet and amount: spends an attestation and
1204
- * returns calldata to broadcast, valid until
1205
- * {@link OndoSwapTransaction.expiresAt}.
2150
+ * Builds a purchase for a specific wallet and deposit: spends an attestation
2151
+ * and returns calldata to broadcast, valid until
2152
+ * {@link OndoBuyTransaction.expiresAt}.
1206
2153
  *
1207
2154
  * The wallet must have approved the swap contract to spend `amount` of the
1208
- * offer's funding token first - frontline reads the allowance before asking
1209
- * Ninshubur for anything, and rejects a short one with a 422 carrying
1210
- * `code: "insufficient_allowance"`. See `prepareSwap` on the client
2155
+ * offer's **funding token** first. Frontline reads that allowance before
2156
+ * asking Ninshubur for anything, and rejects a short one with a 422 carrying
2157
+ * `code: "insufficient_allowance"`. See `prepareBuy` on the client
1211
2158
  * namespace, which approves and then builds, in that order.
1212
2159
  *
1213
- * Buy-only, and the tokens are the offer's rather than the caller's to name.
2160
+ * The tokens themselves are the offer's rather than the caller's to name.
1214
2161
  */
1215
- buildSwapTransaction(params: BuildOndoSwapTransactionParams): Promise<OndoSwapTransaction>;
2162
+ buildBuyTransaction(params: BuildOndoBuyParams): Promise<OndoBuyTransaction>;
2163
+ /**
2164
+ * Builds a sale for a specific wallet and quantity: spends an attestation
2165
+ * and returns calldata to broadcast, valid until
2166
+ * {@link OndoSellTransaction.expiresAt}.
2167
+ *
2168
+ * The approval this one needs is on the **asset**, not on a stablecoin - a
2169
+ * sale delivers the asset, so the swap contract pulls it with `transferFrom`
2170
+ * exactly as it pulls the deposit on a purchase. Same 422 when it is short,
2171
+ * with a message naming that address. See `prepareSell` on the client
2172
+ * namespace.
2173
+ *
2174
+ * Returns a range rather than a quantity: Ondo settles through USDon before
2175
+ * converting to the settlement token, so disclose
2176
+ * {@link OndoSellTransaction.minimum} and not only `expected`.
2177
+ */
2178
+ buildSellTransaction(params: BuildOndoSellParams): Promise<OndoSellTransaction>;
1216
2179
  }
1217
2180
  declare class OndoNamespaceImpl implements OndoNamespace {
1218
2181
  private readonly ctx;
2182
+ protected readonly log: InternalLogger;
1219
2183
  constructor(ctx: SharedNamespaceContext);
1220
2184
  getTradingStatus(params: GetOndoTradingStatusParams): Promise<OndoTradingStatus>;
1221
2185
  getQuote(params: GetOndoQuoteParams): Promise<OndoQuote>;
1222
- buildSwapTransaction(params: BuildOndoSwapTransactionParams): Promise<OndoSwapTransaction>;
2186
+ buildBuyTransaction(params: BuildOndoBuyParams): Promise<OndoBuyTransaction>;
2187
+ buildSellTransaction(params: BuildOndoSellParams): Promise<OndoSellTransaction>;
1223
2188
  }
1224
2189
 
1225
2190
  /** Parameters shared by contract reads scoped to a chain. */
@@ -1272,6 +2237,7 @@ interface SuperstateSwapNamespace {
1272
2237
  }
1273
2238
  declare class SuperstateSwapNamespaceImpl implements SuperstateSwapNamespace {
1274
2239
  private readonly ctx;
2240
+ protected readonly log: InternalLogger;
1275
2241
  constructor(ctx: SharedNamespaceContext);
1276
2242
  getAuthorization(params: GetSwapAuthorizationParams): Promise<SwapAuthorization>;
1277
2243
  getPreview(params: GetSwapPreviewParams): Promise<SwapPreview>;
@@ -1465,6 +2431,7 @@ interface WalletsNamespace {
1465
2431
  }
1466
2432
  declare class WalletsNamespaceImpl implements WalletsNamespace {
1467
2433
  private readonly ctx;
2434
+ private readonly log;
1468
2435
  constructor(ctx: SharedNamespaceContext);
1469
2436
  createOwnershipChallenge(params: CreateWalletOwnershipChallengeParams): Promise<WalletOwnershipChallenge>;
1470
2437
  connectExternal(params: ConnectExternalWalletParams): Promise<OfferOptionAddress>;
@@ -1577,7 +2544,7 @@ declare const Pii: {
1577
2544
  fromDto: (dto: PiiDto) => Pii;
1578
2545
  };
1579
2546
 
1580
- type RequirementTypeDto = 'kyc_approved' | 'identity_verified' | 'proof_of_address' | 'source_of_funds' | 'external_wallet' | 'whitelisted_wallet' | 'jurisdiction' | 'accreditation' | 'document';
2547
+ type RequirementTypeDto = 'kyc_approved' | 'external_wallet' | 'whitelisted_wallet' | 'jurisdiction' | 'accreditation' | 'document';
1581
2548
  type RequirementDto = {
1582
2549
  object: 'requirement';
1583
2550
  id: string;
@@ -1701,6 +2668,7 @@ interface RequirementsNamespace {
1701
2668
  }
1702
2669
  declare class RequirementsNamespaceImpl implements RequirementsNamespace {
1703
2670
  protected readonly ctx: SharedNamespaceContext;
2671
+ protected readonly log: InternalLogger;
1704
2672
  constructor(ctx: SharedNamespaceContext);
1705
2673
  forOffer(offerId: OfferId): Promise<Record<OfferOptionId, Requirement[]>>;
1706
2674
  statuses(offerId: OfferId): Promise<RequirementStatusInfo[]>;
@@ -1738,6 +2706,7 @@ interface OffersNamespace {
1738
2706
  }
1739
2707
  declare class OffersNamespaceImpl implements OffersNamespace {
1740
2708
  private readonly ctx;
2709
+ private readonly log;
1741
2710
  constructor(ctx: SharedNamespaceContext);
1742
2711
  list(): Promise<Offer[]>;
1743
2712
  listPage(params: PaginationParams): Promise<PaginatedResponse<Offer>>;
@@ -1806,6 +2775,16 @@ type NabuChainAssetsDto = {
1806
2775
  protocol: string;
1807
2776
  assets: NabuChainAssetDto[];
1808
2777
  };
2778
+ /** The registry's `/assets.json` route: the complete snapshot, every chain. */
2779
+ type NabuRegistryDto = {
2780
+ data_version: string;
2781
+ schema_version: number;
2782
+ chains: Array<{
2783
+ chain: string;
2784
+ protocol: string;
2785
+ assets: NabuChainAssetDto[];
2786
+ }>;
2787
+ };
1809
2788
 
1810
2789
  /**
1811
2790
  * An absolute URL to a logo image in the token registry. Registry image URLs
@@ -1855,6 +2834,13 @@ type TokenMetadata = {
1855
2834
  declare const TokenMetadata: {
1856
2835
  /** `baseUrl` is the registry origin; registry logo URLs are root-relative. */
1857
2836
  fromDto: (dto: NabuTokenDto, baseUrl: string) => TokenMetadata;
2837
+ /**
2838
+ * Maps the complete registry snapshot to every token it lists across the
2839
+ * chains this SDK models, skipping native coins and chains outside
2840
+ * {@link EthereumChain} (e.g. Solana) rather than failing on them — the
2841
+ * registry may serve chains ahead of the SDK's type surface.
2842
+ */
2843
+ fromRegistryDto: (dto: NabuRegistryDto, baseUrl: string) => TokenMetadata[];
1858
2844
  /**
1859
2845
  * Maps a chain snapshot to the tokens it lists, skipping the chain's native
1860
2846
  * coin (`kind: 'COIN'`, no contract address).
@@ -1865,7 +2851,7 @@ declare const TokenMetadata: {
1865
2851
  /**
1866
2852
  * Token display metadata — name, symbol, decimals, and logos — from
1867
2853
  * CoinList's public token registry, keyed by {@link TokenIdentifier} (the
1868
- * same chain + address pairs `OfferDetail.tokens` carries).
2854
+ * same chain + address pairs `Offer.tokens` carries).
1869
2855
  *
1870
2856
  * Unlike the other namespaces, this one is public: no method requires an
1871
2857
  * authenticated user, and nothing here touches the CoinList API — reads go to
@@ -1879,26 +2865,31 @@ interface TokensNamespace {
1879
2865
  */
1880
2866
  get(token: TokenIdentifier): Promise<TokenMetadata | null>;
1881
2867
  /**
1882
- * Fetches all available tokens the registry lists for `chain`, in one
1883
- * request. Prefer this over calling {@link get} in a loop when displaying a
1884
- * catalogue: two hundred tokens is still a single snapshot download.
2868
+ * Fetches every token the registry lists, in one request: the complete
2869
+ * snapshot with no `chain`, or one chain's snapshot with it. Prefer this
2870
+ * over calling {@link get} in a loop when displaying a catalogue — two
2871
+ * hundred tokens across three chains is still a single download.
2872
+ *
2873
+ * The complete snapshot spans every chain the registry knows; tokens on
2874
+ * chains this SDK does not model (e.g. Solana) are left out of the result.
1885
2875
  *
1886
- * Unlike {@link get}, a missing chain snapshot throws rather than returning
1887
- * `[]`: the registry publishes one for every chain it knows, so its absence
1888
- * is a deployment problem, not an empty catalogue.
2876
+ * Unlike {@link get}, a missing snapshot throws rather than returning `[]`:
2877
+ * the registry always publishes the complete snapshot and one per chain it
2878
+ * knows, so an absence is a deployment problem, not an empty catalogue.
1889
2879
  */
1890
- list(chain: EthereumChain): Promise<TokenMetadata[]>;
2880
+ list(chain?: EthereumChain): Promise<TokenMetadata[]>;
1891
2881
  }
1892
2882
  declare class TokensNamespaceImpl implements TokensNamespace {
1893
2883
  private readonly api;
2884
+ private readonly log;
1894
2885
  /**
1895
2886
  * Takes the registry origin rather than a `SharedNamespaceContext`: the
1896
2887
  * registry is unauthenticated and on its own host, so the frontline sender
1897
2888
  * and the auth check would both be dead weight here.
1898
2889
  */
1899
- constructor(baseUrl: string);
2890
+ constructor(baseUrl: string, logger?: Logger | null);
1900
2891
  get(token: TokenIdentifier): Promise<TokenMetadata | null>;
1901
- list(chain: EthereumChain): Promise<TokenMetadata[]>;
2892
+ list(chain?: EthereumChain): Promise<TokenMetadata[]>;
1902
2893
  }
1903
2894
 
1904
2895
  interface Config {
@@ -1921,6 +2912,48 @@ interface Config {
1921
2912
  * registry.
1922
2913
  */
1923
2914
  readonly tokensBaseUrl?: string;
2915
+ /**
2916
+ * Where the SDK reports what it is doing. **Omit it and the SDK logs nothing
2917
+ * at all** - no `console` fallback, at any level, on any codepath. Supply
2918
+ * one and every request, every classified failure and every hook state
2919
+ * transition is reported at the level your logger asks for.
2920
+ *
2921
+ * Absent, the SDK says nothing at all - there is no fallback to `console`,
2922
+ * at any level, on any codepath.
2923
+ *
2924
+ * **Running the SDK's logging in production is not advised.** The safest
2925
+ * posture is to leave this undefined outside development, staging and
2926
+ * incident reproduction: a seam that emits nothing cannot disclose anything,
2927
+ * and that property does not depend on the SDK continuing to get redaction
2928
+ * right.
2929
+ *
2930
+ * If you do run one there, run it at `'info'` or above and know what that
2931
+ * does and does not buy you. Those levels are **redacted by construction**:
2932
+ * they carry only SDK-authored classification and server-authored
2933
+ * identifiers, and the type system holds that line rather than a convention -
2934
+ * an event at those levels accepts scalar fields only, so a body, a DTO or
2935
+ * an operation's parameters cannot be put on one. What the SDK does **not**
2936
+ * give you is a warranty that the result is safe for your environment. The
2937
+ * mechanism is checkable and stated; the conclusion depends on your sink,
2938
+ * your retention and your threat model, and it is yours to draw.
2939
+ *
2940
+ * **`'debug'` is not.** It reports request and response bodies, full URLs,
2941
+ * headers and operation parameters verbatim - bearer tokens, KYC answers,
2942
+ * tax-document fields, wallet signatures - and **the SDK does not redact**.
2943
+ * Run it on a developer's machine, in tests, and in beta or staging
2944
+ * environments where the data flowing through is not real customer data.
2945
+ * The shipped implementations make that structural: built with
2946
+ * `isDev: false`, `'debug'` does not typecheck. Either way, filtering,
2947
+ * redaction and retention at your sink are yours, not the SDK's.
2948
+ *
2949
+ * Every method you implement here **must be total**: the SDK calls them on
2950
+ * the codepath of the work they report and does not catch them, so a logger
2951
+ * that throws fails the operation it was describing.
2952
+ *
2953
+ * See {@link Logger} for the full contract, and {@link pinoClientLogger} or
2954
+ * {@link pinoServerLogger} for a ready-made implementation over pino.
2955
+ */
2956
+ readonly logger?: Logger;
1924
2957
  }
1925
2958
 
1926
- export { type HttpResponse as $, AuthorizationCode as A, BlockchainAmount as B, CodeVerifier as C, type RequirementType as D, EvmWalletAddress as E, type RequirementStatusValue as F, OfferOptionAddress as G, RequirementId as H, type WalletChallengeType as I, DocumentSubmission as J, type KycLevelName as K, OndoTradingStatus as L, AssetDecimals as M, OndoQuote as N, OfferId as O, Participation as P, type OrderBookSide as Q, type RequirementsNamespace as R, type SharedNamespaceContext as S, type Tx as T, type OndoQuoteSize as U, type BuildOndoSwapTransactionParams as V, type WalletsNamespace as W, OfferOptionAddressId as X, TokenMetadata as Y, type TokenIdentifier as Z, HttpError as _, EthereumChain as a, type RemoveOptionAddressParams as a$, KycToken as a0, OAuthSession as a1, ClientCredentialsOAuth as a2, ClientSecret as a3, type Sender as a4, PaginationParams as a5, PaginatedResponse as a6, type Uint256 as a7, KnownAssetSymbol as a8, DecimalString as a9, type GetTokenBalanceParams as aA, HexEncodedTransactionData as aB, Iso2CountryCode as aC, Link as aD, type ListOptionAddressesParams as aE, MAX_ASSET_DECIMALS as aF, MAX_UINT_256 as aG, Milestone as aH, OAuthRefreshToken as aI, OfferOption as aJ, OfferOptionSlug as aK, OfferSlug as aL, OfferToken as aM, OffersNamespaceImpl as aN, type OndoQuoteDuration as aO, PKCEState as aP, type PaginatedResponseDto as aQ, ParticipationId as aR, type ParticipationStatus as aS, ParticipationsPaginationParams as aT, Pii as aU, PiiAddress as aV, PiiJurisdiction as aW, type PiiKind as aX, type QueryParamValue as aY, type QueryParamValues as aZ, RedirectUri as a_, SwapStatus as aa, type Newtype as ab, type AllowWalletParams as ac, AllowWalletResponse as ad, Asset as ae, AssetCode as af, Blockchain as ag, Chain as ah, ClientId as ai, CodeChallenge as aj, ConnectExternalWalletParams as ak, type CreateKycTokenParams as al, CreateParticipationParams as am, CreateWalletOwnershipChallengeParams as an, Cursor as ao, type DocumentFormType as ap, type DocumentSubmissionStatus as aq, type DocumentType as ar, ETHEREUM_CHAINS as as, Erc20NamespaceImpl as at, FaqItem as au, type GetOndoQuoteParams as av, type GetOndoTradingStatusParams as aw, type GetSwapAuthorizationParams as ax, type GetSwapPreviewParams as ay, type GetTokenAllowanceParams as az, type Erc20Namespace as b, type RequirementActionNeededReason as b0, SOLANA_CHAINS as b1, STABLE_DECIMALS as b2, SolanaChain as b3, type SubmitDocumentParams as b4, SwapAuthorization as b5, type SwapContractRef as b6, SwapPreview as b7, TermItem as b8, Ticker as b9, TokenAllowance as ba, TokenBalance as bb, TokenLogo as bc, type TokenLogoImage as bd, TokenLogoUrl as be, type TokenRole as bf, TokensNamespaceImpl as bg, WalletAddress as bh, WalletOwnershipChallenge as bi, type WalletProtocol as bj, WalletsNamespaceImpl as bk, apiErrorCode as bl, assertUint256 as bm, EvmContractAddress as c, type CoinListTokenSaleNamespace as d, OfferOptionId as e, AssetId as f, CoinListTokenSaleNamespaceImpl as g, type OndoNamespace as h, AssetSymbol as i, OndoSwapTransaction as j, OndoNamespaceImpl as k, type SuperstateSwapNamespace as l, Bps as m, SuperstateSwapNamespaceImpl as n, Requirement as o, RequirementsNamespaceImpl as p, type Config as q, type OAuthAccessToken as r, type OffersNamespace as s, type TokensNamespace as t, type Erc20Asset as u, OfferDetail as v, StablecoinSymbol as w, type OfferType as x, Offer as y, RequirementStatusInfo as z };
2959
+ export { type OndoQuoteSize as $, AuthorizationCode as A, BlockchainAmount as B, CodeVerifier as C, type OfferType as D, type Erc20Namespace as E, Offer as F, RequirementStatusInfo as G, type RequirementType as H, type RequirementStatusValue as I, OfferOptionAddress as J, RequirementId as K, type Logger as L, type KycLevelName as M, DocumentSubmission as N, OfferId as O, Participation as P, type WalletChallengeType as Q, type RequirementsNamespace as R, type SharedNamespaceContext as S, type TokensNamespace as T, type PinoLoggerOptions as U, OndoTradingStatus as V, type WalletError as W, AssetDecimals as X, type BuildOndoBuyParams as Y, type BuildOndoSellParams as Z, OndoQuote as _, EthereumChain as a, Milestone as a$, OfferOptionAddressId as a0, TokenMetadata as a1, type TokenIdentifier as a2, type DebugEvent as a3, type FrontlineEventId as a4, HttpError as a5, type HttpResponse as a6, KycToken as a7, type LogBinding as a8, type LogBindings as a9, type BuildOndoSwapParamsCore as aA, Chain as aB, ClientId as aC, CodeChallenge as aD, ConnectExternalWalletParams as aE, type CreateKycTokenParams as aF, CreateParticipationParams as aG, CreateWalletOwnershipChallengeParams as aH, Cursor as aI, type DocumentFormType as aJ, type DocumentSubmissionStatus as aK, type DocumentType as aL, ETHEREUM_CHAINS as aM, Erc20NamespaceImpl as aN, FaqItem as aO, type GetOndoQuoteParams as aP, type GetOndoTradingStatusParams as aQ, type GetSwapAuthorizationParams as aR, type GetSwapPreviewParams as aS, type GetTokenAllowanceParams as aT, type GetTokenBalanceParams as aU, HexEncodedTransactionData as aV, Iso2CountryCode as aW, Link as aX, type ListOptionAddressesParams as aY, MAX_ASSET_DECIMALS as aZ, MAX_UINT_256 as a_, type LogCause as aa, type LogLevel as ab, type LogScope as ac, type LogValue as ad, type ProductionLogLevel as ae, RedactedWalletError as af, type RequestId as ag, type SafeEvent as ah, type SafeFields as ai, type UnredactedFields as aj, OAuthSession as ak, ClientCredentialsOAuth as al, ClientSecret as am, type Sender as an, PaginationParams as ao, PaginatedResponse as ap, type Uint256 as aq, KnownAssetSymbol as ar, DecimalString as as, SwapStatus as at, type Newtype as au, type AllowWalletParams as av, AllowWalletResponse as aw, Asset as ax, AssetCode as ay, Blockchain as az, EvmContractAddress as b, OAuthRefreshToken as b0, OfferOption as b1, OfferOptionSlug as b2, OfferSlug as b3, OfferToken as b4, OffersNamespaceImpl as b5, type OndoQuoteDuration as b6, type OndoSellOutcome as b7, PKCEState as b8, type PaginatedResponseDto as b9, type TokenRole as bA, TokensNamespaceImpl as bB, type Tx as bC, WalletAddress as bD, WalletOwnershipChallenge as bE, type WalletProtocol as bF, WalletsNamespaceImpl as bG, apiErrorCode as bH, assertUint256 as bI, parseUint256 as bJ, ParticipationId as ba, type ParticipationStatus as bb, ParticipationsPaginationParams as bc, Pii as bd, PiiAddress as be, PiiJurisdiction as bf, type PiiKind as bg, type QueryParamValue as bh, type QueryParamValues as bi, RedirectUri as bj, type RemoveOptionAddressParams as bk, type RequirementActionNeededReason as bl, SOLANA_CHAINS as bm, STABLE_DECIMALS as bn, SolanaChain as bo, type SubmitDocumentParams as bp, SwapAuthorization as bq, type SwapContractRef as br, SwapPreview as bs, TermItem as bt, Ticker as bu, TokenAllowance as bv, TokenBalance as bw, TokenLogo as bx, type TokenLogoImage as by, TokenLogoUrl as bz, type CoinListTokenSaleNamespace as c, OfferOptionId as d, AssetId as e, CoinListTokenSaleNamespaceImpl as f, type OndoNamespace as g, AssetSymbol as h, OndoBuyTransaction as i, OndoSellTransaction as j, type OndoSwapTransactionCore as k, type OrderBookSide as l, OndoNamespaceImpl as m, type SuperstateSwapNamespace as n, type WalletsNamespace as o, Bps as p, EvmWalletAddress as q, SuperstateSwapNamespaceImpl as r, Requirement as s, RequirementsNamespaceImpl as t, type Config as u, type OAuthAccessToken as v, type OffersNamespace as w, StablecoinSymbol as x, type Erc20Asset as y, OfferDetail as z };