@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
@@ -24,7 +24,9 @@ __export(server_exports, {
24
24
  ServerOffersNamespaceImpl: () => ServerOffersNamespaceImpl,
25
25
  ServerRequirementsNamespaceImpl: () => ServerRequirementsNamespaceImpl,
26
26
  WritableSessionStoreRequiredError: () => WritableSessionStoreRequiredError,
27
- createCoinListServer: () => createCoinListServer
27
+ createCoinListServer: () => createCoinListServer,
28
+ emptySessionStore: () => emptySessionStore,
29
+ pinoServerLogger: () => pinoServerLogger
28
30
  });
29
31
  module.exports = __toCommonJS(server_exports);
30
32
 
@@ -55,6 +57,8 @@ var retryAttempt = (attempt) => ({
55
57
  var renewAttempted = (value) => ({
56
58
  renewAttempted: value
57
59
  });
60
+ var requestId = (id) => ({ requestId: id });
61
+ var getRequestId = (attrs) => attrs?.requestId ?? null;
58
62
  var isProtected = (attrs) => attrs?.protected === true;
59
63
  var needUserAgent = (attrs) => attrs?.userAgent === true;
60
64
  var isIdempotent = (attrs) => attrs?.idempotencyKey === true;
@@ -76,7 +80,9 @@ var Attributes = {
76
80
  renewAttempted,
77
81
  wasRenewAttempted,
78
82
  clientCredentials,
79
- getClientCredentials
83
+ getClientCredentials,
84
+ requestId,
85
+ getRequestId
80
86
  };
81
87
 
82
88
  // src/shared/api/http.ts
@@ -86,7 +92,24 @@ var HttpError = class extends Error {
86
92
  this.name = "HttpError";
87
93
  this.response = response;
88
94
  }
95
+ /**
96
+ * Correlates this failure with the `[HTTP]` log lines for the same request,
97
+ * which carry the method, the URL, the duration and every retry. `null` when
98
+ * the response did not come from an {@link HttpClient}.
99
+ *
100
+ * Worth quoting in a bug report: it is what makes a log excerpt readable.
101
+ */
102
+ get requestId() {
103
+ return this.response.requestId ?? null;
104
+ }
89
105
  };
106
+ function apiErrorCode(error) {
107
+ if (!(error instanceof HttpError)) return null;
108
+ const body = error.response.body;
109
+ if (typeof body !== "object" || body === null) return null;
110
+ const code = body.code;
111
+ return typeof code === "string" ? code : null;
112
+ }
90
113
  async function makeRequest(request) {
91
114
  const headers = {
92
115
  Accept: "application/json",
@@ -192,11 +215,194 @@ var HEADER_API_VERSION = "X-API-Version";
192
215
  var HEADER_IDEMPOTENCY_KEY = "Idempotency-Key";
193
216
  var API_VERSION = "2025-10-17";
194
217
 
218
+ // src/shared/types/errors.ts
219
+ var NotImplementedError = class extends Error {
220
+ constructor(message = "Not implemented yet") {
221
+ super(message);
222
+ this.name = "NotImplementedError";
223
+ }
224
+ };
225
+ var NotAuthenticatedError = class extends Error {
226
+ constructor(message = "The user is not authenticated. Go through the OAuth flow first!") {
227
+ super(message);
228
+ this.name = "NotAuthenticatedError";
229
+ }
230
+ };
231
+ var ValidationError = class extends Error {
232
+ constructor(message) {
233
+ super(message);
234
+ this.name = "ValidationError";
235
+ }
236
+ };
237
+ var InvariantError = class extends Error {
238
+ constructor(message) {
239
+ super(message);
240
+ this.name = "InvariantError";
241
+ }
242
+ };
243
+ var MathError = class extends Error {
244
+ constructor(message) {
245
+ super(message);
246
+ this.name = "MathError";
247
+ }
248
+ };
249
+
250
+ // src/shared/core/observability/log-cause.ts
251
+ function classifyLogCause(error) {
252
+ if (error instanceof HttpError) {
253
+ return httpCause(error);
254
+ }
255
+ if (error instanceof ValidationError) {
256
+ return { type: "validation", message: error.message };
257
+ }
258
+ if (error instanceof InvariantError) {
259
+ return { type: "invariant", message: error.message };
260
+ }
261
+ if (error instanceof MathError) {
262
+ return { type: "math", message: error.message };
263
+ }
264
+ if (error instanceof NotAuthenticatedError) {
265
+ return { type: "not-authenticated" };
266
+ }
267
+ if (error instanceof NotImplementedError) {
268
+ return { type: "not-implemented" };
269
+ }
270
+ return { type: "generic-error", name: errorName(error) };
271
+ }
272
+ function describeErrorUnredacted(error) {
273
+ if (error instanceof HttpError) {
274
+ return describeHttpErrorRedacted(error);
275
+ }
276
+ if (error instanceof Error) {
277
+ return `${error.name}: ${error.message}`;
278
+ }
279
+ return `thrown non-error: ${stringifyUnredacted(error)}`;
280
+ }
281
+ function stringifyUnredacted(value) {
282
+ if (value === void 0) return "";
283
+ try {
284
+ return JSON.stringify(
285
+ value,
286
+ (_key, item) => typeof item === "bigint" ? `${item}` : item
287
+ ) ?? String(value);
288
+ } catch (_) {
289
+ return "<unserializable>";
290
+ }
291
+ }
292
+ function describeHttpErrorRedacted(error) {
293
+ const code = apiErrorCode(error);
294
+ const status = error.response.status;
295
+ return code === null ? `HttpError ${status}` : `HttpError ${status} (${code})`;
296
+ }
297
+ function errorName(error) {
298
+ return error instanceof Error ? error.name : `non-error ${typeof error}`;
299
+ }
300
+ function httpCause(error) {
301
+ return {
302
+ type: "http",
303
+ requestId: error.requestId,
304
+ status: error.response.status,
305
+ code: apiErrorCode(error),
306
+ eventId: apiErrorEventId(error)
307
+ };
308
+ }
309
+ function apiErrorEventId(error) {
310
+ const body = error.response.body;
311
+ if (typeof body !== "object" || body === null) return null;
312
+ const eventId = body.event_id;
313
+ return typeof eventId === "string" ? eventId : null;
314
+ }
315
+
316
+ // src/shared/core/observability/internal-logger.ts
317
+ function internalLogger(logger, scope) {
318
+ return logger ? scopedLogger(logger, scope, {}) : noopInternalLogger;
319
+ }
320
+ var LEVEL_RANK = {
321
+ none: 0,
322
+ error: 1,
323
+ warn: 2,
324
+ info: 3,
325
+ debug: 4
326
+ };
327
+ function scopedLogger(logger, scope, bindings) {
328
+ const admits = (level) => LEVEL_RANK[logger.level()] >= LEVEL_RANK[level];
329
+ const safe = (event) => ({
330
+ msg: event.msg,
331
+ scope,
332
+ bindings,
333
+ fields: event.fields ?? {},
334
+ ...event.cause === void 0 ? {} : { cause: event.cause }
335
+ });
336
+ const unredacted = (event) => ({
337
+ msg: event.msg,
338
+ scope,
339
+ bindings,
340
+ fields: event.fields ?? {}
341
+ });
342
+ const self = {
343
+ child: (binding) => scopedLogger(logger, scope, { ...bindings, ...binding }),
344
+ debug: (event) => {
345
+ if (admits("debug")) logger.debug(() => unredacted(event()));
346
+ },
347
+ info: (event) => {
348
+ if (admits("info")) logger.info(() => safe(event()));
349
+ },
350
+ warn: (event) => {
351
+ if (admits("warn")) logger.warn(() => safe(event()));
352
+ },
353
+ error: (event) => {
354
+ if (admits("error")) logger.error(() => safe(event()));
355
+ },
356
+ failure: (event, error) => {
357
+ if (admits("debug"))
358
+ logger.debug(() => unredacted(verbatim(event(), error)));
359
+ if (admits("error")) logger.error(() => safe(classified(event(), error)));
360
+ },
361
+ warning: (event, error) => {
362
+ if (admits("debug"))
363
+ logger.debug(() => unredacted(verbatim(event(), error)));
364
+ if (admits("warn")) logger.warn(() => safe(classified(event(), error)));
365
+ },
366
+ wrap: async (op, params, run) => {
367
+ const opLog = self.child({ op });
368
+ opLog.debug(() => ({ msg: "call", fields: { params } }));
369
+ try {
370
+ return await run();
371
+ } catch (error) {
372
+ opLog.failure(() => ({ msg: "call failed" }), error);
373
+ throw error;
374
+ }
375
+ }
376
+ };
377
+ return self;
378
+ }
379
+ function verbatim(event, error) {
380
+ return {
381
+ msg: event.msg,
382
+ fields: { ...event.fields, error: describeErrorUnredacted(error) }
383
+ };
384
+ }
385
+ function classified(event, error) {
386
+ return { ...event, cause: event.cause ?? classifyLogCause(error) };
387
+ }
388
+ var noopInternalLogger = {
389
+ child: () => noopInternalLogger,
390
+ debug: () => void 0,
391
+ info: () => void 0,
392
+ warn: () => void 0,
393
+ error: () => void 0,
394
+ failure: () => void 0,
395
+ warning: () => void 0,
396
+ wrap: (_op, _params, run) => run()
397
+ };
398
+
195
399
  // src/shared/api/http-client.ts
196
400
  var HttpClient = class {
197
- constructor(config, middleware = {}) {
401
+ constructor(config, middleware = {}, logger = null, options = {}) {
198
402
  this.config = config;
199
403
  this.middleware = middleware;
404
+ this.log = internalLogger(logger, "HTTP");
405
+ this.makeRequestId = options.makeRequestId ?? defaultMakeRequestId;
200
406
  }
201
407
  async send(request) {
202
408
  return this.runRequestWithAfterMiddleware(request);
@@ -232,7 +438,13 @@ var HttpClient = class {
232
438
  return {
233
439
  ...request,
234
440
  url,
235
- headers
441
+ headers,
442
+ // Only when absent: a retry or a post-renewal re-send arrives with the
443
+ // first attempt's id already on it, and keeping it is the whole point.
444
+ attributes: Attributes.getRequestId(request.attributes) === null ? Attributes.concat(
445
+ request.attributes ?? Attributes.empty,
446
+ Attributes.requestId(this.makeRequestId())
447
+ ) : request.attributes
236
448
  };
237
449
  }
238
450
  /**
@@ -256,10 +468,89 @@ var HttpClient = class {
256
468
  }
257
469
  return request;
258
470
  }
259
- executeRequest(request) {
260
- return makeRequest(request);
471
+ /**
472
+ * One physical attempt, logged as one line.
473
+ *
474
+ * A non-2xx is a `warn` rather than an `error` because the wire does not
475
+ * know whether it is a failure: the retry middleware may turn a 503 into a
476
+ * success, and the token registry reads a 404 as "not listed". The namespace
477
+ * above decides, and logs the `error` when it does.
478
+ */
479
+ async executeRequest(request) {
480
+ const requestId2 = Attributes.getRequestId(request.attributes);
481
+ const log = requestId2 === null ? this.log : this.log.child({ requestId: requestId2 });
482
+ const attempt = Attributes.getRetryAttempt(request.attributes) + 1;
483
+ const startedAt = Date.now();
484
+ log.debug(() => ({
485
+ msg: "request sent",
486
+ fields: describeRequestUnredacted(request)
487
+ }));
488
+ try {
489
+ const response = await makeRequest(request);
490
+ const elapsed = Date.now() - startedAt;
491
+ const outcome = () => ({
492
+ msg: "request completed",
493
+ fields: {
494
+ ...identifyRequest(request),
495
+ "http.response.status_code": response.status,
496
+ duration_ms: elapsed,
497
+ attempt
498
+ }
499
+ });
500
+ if (response.status >= 200 && response.status < 300) {
501
+ log.info(outcome);
502
+ } else {
503
+ log.warn(outcome);
504
+ }
505
+ log.debug(() => ({
506
+ msg: "response received",
507
+ fields: { body: response.body }
508
+ }));
509
+ return requestId2 === null ? response : { ...response, requestId: requestId2 };
510
+ } catch (error) {
511
+ const elapsed = Date.now() - startedAt;
512
+ log.warning(
513
+ () => ({
514
+ msg: "request threw",
515
+ fields: {
516
+ ...identifyRequest(request),
517
+ duration_ms: elapsed,
518
+ attempt
519
+ }
520
+ }),
521
+ error
522
+ );
523
+ throw error;
524
+ }
261
525
  }
262
526
  };
527
+ function identifyRequest(request) {
528
+ const { host, path } = splitUrl(request.url);
529
+ return {
530
+ "http.request.method": request.method,
531
+ "server.address": host,
532
+ "url.path": path
533
+ };
534
+ }
535
+ function splitUrl(url) {
536
+ try {
537
+ const parsed = new URL(url);
538
+ return { host: parsed.host, path: parsed.pathname };
539
+ } catch (_) {
540
+ return { host: null, path: url };
541
+ }
542
+ }
543
+ function defaultMakeRequestId() {
544
+ return Math.random().toString(36).slice(2, 10).padEnd(8, "0");
545
+ }
546
+ function describeRequestUnredacted(request) {
547
+ return {
548
+ "http.request.method": request.method,
549
+ "url.full": buildUrlWithQueryParams(request.url, request.queryParams),
550
+ headers: request.headers,
551
+ ...request.method === "POST" ? { body: request.body } : {}
552
+ };
553
+ }
263
554
 
264
555
  // src/shared/api/middleware/attach-session-middleware.ts
265
556
  function attachSessionMiddleware(fetchAccessToken) {
@@ -390,18 +681,22 @@ function renewSessionMiddleware(fetchAccessToken) {
390
681
 
391
682
  // src/shared/api/authenticated-api-client.ts
392
683
  var AuthenticatedApiClient = class {
393
- constructor(config, fetchAccessToken, additionalBeforeRequest = []) {
394
- this.httpClient = new HttpClient(config, {
395
- beforeRequest: [
396
- attachSessionMiddleware(fetchAccessToken),
397
- ...additionalBeforeRequest,
398
- idempotencyKeyMiddleware
399
- ],
400
- afterRequest: [
401
- renewSessionMiddleware(fetchAccessToken),
402
- requestRetryMiddleware
403
- ]
404
- });
684
+ constructor(config, fetchAccessToken, additionalBeforeRequest = [], logger = null) {
685
+ this.httpClient = new HttpClient(
686
+ config,
687
+ {
688
+ beforeRequest: [
689
+ attachSessionMiddleware(fetchAccessToken),
690
+ ...additionalBeforeRequest,
691
+ idempotencyKeyMiddleware
692
+ ],
693
+ afterRequest: [
694
+ renewSessionMiddleware(fetchAccessToken),
695
+ requestRetryMiddleware
696
+ ]
697
+ },
698
+ logger
699
+ );
405
700
  }
406
701
  async send(request) {
407
702
  const response = await this.httpClient.send(request);
@@ -415,8 +710,13 @@ var AuthenticatedApiClient = class {
415
710
 
416
711
  // src/server/core/api/api-client.ts
417
712
  var ApiClient = class {
418
- constructor(config, fetchAccessToken) {
419
- this.client = new AuthenticatedApiClient(config, fetchAccessToken);
713
+ constructor(config, fetchAccessToken, logger = null) {
714
+ this.client = new AuthenticatedApiClient(
715
+ config,
716
+ fetchAccessToken,
717
+ [],
718
+ logger
719
+ );
420
720
  }
421
721
  async send(request) {
422
722
  return this.client.send(request);
@@ -448,33 +748,43 @@ var OAuthSession = {
448
748
  };
449
749
 
450
750
  // src/server/core/server-auth-namespace.ts
751
+ function emptySessionStore() {
752
+ return { getSession: async () => null };
753
+ }
451
754
  var ServerAuthNamespaceImpl = class {
452
755
  constructor(api, config) {
453
756
  this.api = api;
454
757
  this.config = config;
758
+ this.log = internalLogger(config.logger ?? null, "AUTH");
455
759
  }
456
- async completeOAuth({
457
- code,
458
- codeVerifier
459
- }) {
460
- const setSession = this.writableSessionStore();
461
- const sessionDto = await this.api.send({
462
- method: "POST",
463
- url: `/oauth/token`,
464
- body: {
465
- grant_type: "authorization_code",
466
- code,
467
- redirect_uri: this.config.redirectUri,
468
- client_id: this.config.clientId,
469
- client_secret: this.config.clientSecret,
470
- code_verifier: codeVerifier
471
- }
760
+ async completeOAuth(params) {
761
+ return this.log.wrap("completeOAuth", void 0, async () => {
762
+ const setSession = this.writableSessionStore();
763
+ const sessionDto = await this.api.send({
764
+ method: "POST",
765
+ url: `/oauth/token`,
766
+ body: {
767
+ grant_type: "authorization_code",
768
+ code: params.code,
769
+ redirect_uri: this.config.redirectUri,
770
+ client_id: this.config.clientId,
771
+ client_secret: this.config.clientSecret,
772
+ code_verifier: params.codeVerifier
773
+ }
774
+ });
775
+ const session = OAuthSession.fromDto(sessionDto);
776
+ await setSession(session);
777
+ return session;
472
778
  });
473
- const session = OAuthSession.fromDto(sessionDto);
474
- await setSession(session);
475
- return session;
476
779
  }
477
780
  async getAccessToken() {
781
+ return this.log.wrap(
782
+ "getAccessToken",
783
+ void 0,
784
+ () => this.readAccessToken()
785
+ );
786
+ }
787
+ async readAccessToken() {
478
788
  const sessionStore = this.config.sessionStore;
479
789
  const session = await sessionStore.getSession();
480
790
  if (session == null) return null;
@@ -486,12 +796,19 @@ var ServerAuthNamespaceImpl = class {
486
796
  }
487
797
  const setSession = sessionStore.setSession?.bind(sessionStore);
488
798
  if (!setSession) {
799
+ this.log.child({ op: "getAccessToken" }).warn(() => ({
800
+ msg: "serving an expired token: the session store is read-only, so it cannot be refreshed"
801
+ }));
489
802
  return session.accessToken;
490
803
  }
491
804
  return this.refreshSession(session.refreshToken, setSession);
492
805
  }
493
806
  async refreshSession(refreshToken, setSession) {
807
+ const log = this.log.child({ op: "refresh" });
494
808
  if (!refreshToken) {
809
+ log.warn(() => ({
810
+ msg: "clearing the session: it carries no refresh token"
811
+ }));
495
812
  await setSession(null);
496
813
  return null;
497
814
  }
@@ -508,26 +825,36 @@ var ServerAuthNamespaceImpl = class {
508
825
  });
509
826
  const newSession = OAuthSession.fromDto(sessionDto);
510
827
  await setSession(newSession);
828
+ log.info(() => ({ msg: "session renewed" }));
511
829
  return newSession.accessToken;
512
- } catch {
830
+ } catch (error) {
831
+ log.failure(
832
+ () => ({ msg: "refresh failed; clearing the session" }),
833
+ error
834
+ );
513
835
  await setSession(null);
514
836
  return null;
515
837
  }
516
838
  }
517
839
  async clientCredentials() {
518
- const sessionDto = await this.api.send({
519
- method: "POST",
520
- url: `/oauth/token`,
521
- body: {
522
- grant_type: "client_credentials",
523
- client_id: this.config.clientId,
524
- client_secret: this.config.clientSecret
525
- }
840
+ return this.log.wrap("clientCredentials", void 0, async () => {
841
+ const sessionDto = await this.api.send({
842
+ method: "POST",
843
+ url: `/oauth/token`,
844
+ body: {
845
+ grant_type: "client_credentials",
846
+ client_id: this.config.clientId,
847
+ client_secret: this.config.clientSecret
848
+ }
849
+ });
850
+ const session = OAuthSession.fromDto(sessionDto);
851
+ return ClientCredentialsOAuth(session.accessToken);
526
852
  });
527
- const session = OAuthSession.fromDto(sessionDto);
528
- return ClientCredentialsOAuth(session.accessToken);
529
853
  }
530
854
  async logout() {
855
+ return this.log.wrap("logout", void 0, () => this.revokeAndClear());
856
+ }
857
+ async revokeAndClear() {
531
858
  const setSession = this.writableSessionStore();
532
859
  const session = await this.config.sessionStore.getSession();
533
860
  if (session == null) return;
@@ -545,6 +872,9 @@ var ServerAuthNamespaceImpl = class {
545
872
  if (!(err instanceof HttpError) || this.config.strict) {
546
873
  throw err;
547
874
  }
875
+ this.log.child({ op: "logout" }).warn(() => ({
876
+ msg: "the token was not revoked upstream; the local session is cleared regardless"
877
+ }));
548
878
  }
549
879
  await setSession(null);
550
880
  }
@@ -578,52 +908,12 @@ async function fetchAllPages(fetchPage, baseParams) {
578
908
  return items;
579
909
  }
580
910
 
581
- // src/shared/types/offer.ts
582
- var OfferId = (value) => value;
583
- var OfferSlug = (value) => value;
584
- var Offer = {
585
- fromDto: (dto) => ({
586
- id: OfferId(dto.id),
587
- slug: OfferSlug(dto.slug),
588
- type: dto.type,
589
- tagline: dto.tagline,
590
- bannerUrl: dto.banner_url,
591
- logoUrl: dto.logo_url,
592
- startsAt: new Date(dto.starts_at),
593
- endsAt: dto.ends_at ? new Date(dto.ends_at) : null
594
- })
595
- };
596
-
597
- // src/shared/types/asset.ts
598
- var AssetId = (value) => value;
599
- var AssetCode = (value) => value;
600
- var Asset = {
601
- fromDto: (dto) => ({
602
- id: AssetId(dto.id),
603
- code: AssetCode(dto.code),
604
- name: dto.name,
605
- fractionalDigits: dto.fractional_digits
606
- })
607
- };
608
-
609
- // src/shared/types/errors.ts
610
- var NotAuthenticatedError = class extends Error {
611
- constructor(message = "The user is not authenticated. Go through the OAuth flow first!") {
612
- super(message);
613
- this.name = "NotAuthenticatedError";
614
- }
615
- };
616
- var ValidationError = class extends Error {
617
- constructor(message) {
618
- super(message);
619
- this.name = "ValidationError";
620
- }
621
- };
622
-
623
911
  // src/shared/types/blockchain/core.ts
624
912
  var ETHEREUM_CHAINS = {
625
913
  ethereum_mainnet: true,
626
- ethereum_sepolia: true
914
+ ethereum_sepolia: true,
915
+ base_mainnet: true,
916
+ base_sepolia: true
627
917
  };
628
918
  var EthereumChain = (value) => {
629
919
  if (!Object.keys(ETHEREUM_CHAINS).includes(value)) {
@@ -660,57 +950,133 @@ var STABLE_DECIMALS = AssetDecimals(6);
660
950
  var DecimalString = (value) => value;
661
951
  var MAX_UINT_256 = 2n ** 256n - 1n;
662
952
  var assertUint256 = (value) => {
663
- if (value < 0n || value > MAX_UINT_256) {
664
- throw new Error(`Value out of uint256 bounds: ${value}`);
665
- }
666
- return value;
953
+ if (isUint256(value)) return value;
954
+ throw new InvariantError(`Value out of uint256 bounds: ${value}`);
955
+ };
956
+ var parseUint256 = (value, label) => {
957
+ if (isUint256(value)) return value;
958
+ throw new ValidationError(`${label}: out of uint256 bounds (${value})`);
667
959
  };
960
+ var isUint256 = (value) => value >= 0n && value <= MAX_UINT_256;
668
961
  var BlockchainAmount = Object.assign(
669
962
  (value) => value,
670
963
  {
671
964
  add: (a, b) => combineAmounts(a, b, (x, y) => x + y),
672
- sub: (a, b) => combineAmounts(a, b, (x, y) => x - y)
965
+ sub: (a, b) => combineAmounts(a, b, (x, y) => x - y),
966
+ mul: multiplyAmounts,
967
+ div: divideAmounts
673
968
  }
674
969
  );
970
+ function multiplyAmounts(a, b) {
971
+ const product = a.raw * b.raw;
972
+ return BlockchainAmount({
973
+ raw: product / 10n ** BigInt(b.decimals),
974
+ decimals: a.decimals
975
+ });
976
+ }
977
+ function divideAmounts(a, b) {
978
+ if (b.raw === 0n) {
979
+ throw new MathError("Cannot divide a BlockchainAmount by zero");
980
+ }
981
+ const scaled = a.raw * 10n ** BigInt(b.decimals);
982
+ return BlockchainAmount({ raw: scaled / b.raw, decimals: a.decimals });
983
+ }
675
984
  function combineAmounts(a, b, op) {
676
985
  if (a.decimals !== b.decimals) {
677
- throw new Error(
986
+ throw new InvariantError(
678
987
  `Cannot combine BlockchainAmounts with different decimals: ${a.decimals} vs ${b.decimals}`
679
988
  );
680
989
  }
681
990
  const raw = op(a.raw, b.raw);
682
991
  if (raw < 0n || raw > MAX_UINT_256) {
683
- throw new Error(`BlockchainAmount out of uint256 bounds: ${raw}`);
992
+ throw new InvariantError(`BlockchainAmount out of uint256 bounds: ${raw}`);
684
993
  }
685
994
  return BlockchainAmount({ raw, decimals: a.decimals });
686
995
  }
687
996
  var AssetSymbol = (value) => value;
688
997
 
998
+ // src/shared/types/offer.ts
999
+ var OfferId = (value) => value;
1000
+ var OfferSlug = (value) => value;
1001
+ var Offer = {
1002
+ fromDto: (dto) => {
1003
+ if (!Array.isArray(dto.tokens)) {
1004
+ throw new ValidationError(
1005
+ `Offer.tokens: expected an array, got ${typeof dto.tokens}`
1006
+ );
1007
+ }
1008
+ return {
1009
+ id: OfferId(dto.id),
1010
+ slug: OfferSlug(dto.slug),
1011
+ type: dto.type,
1012
+ tagline: dto.tagline,
1013
+ bannerUrl: dto.banner_url,
1014
+ logoUrl: dto.logo_url,
1015
+ startsAt: new Date(dto.starts_at),
1016
+ endsAt: dto.ends_at ? new Date(dto.ends_at) : null,
1017
+ tokens: dto.tokens.map(OfferToken.fromDto)
1018
+ };
1019
+ }
1020
+ };
1021
+ var OfferToken = {
1022
+ fromDto: (dto) => ({
1023
+ role: dto.role,
1024
+ chain: Chain(dto.chain),
1025
+ address: EvmContractAddress(dto.address)
1026
+ })
1027
+ };
1028
+
1029
+ // src/shared/types/asset.ts
1030
+ var AssetId = (value) => value;
1031
+ var AssetCode = (value) => value;
1032
+ var Asset = {
1033
+ fromDto: (dto) => ({
1034
+ id: AssetId(dto.id),
1035
+ code: AssetCode(dto.code),
1036
+ name: dto.name,
1037
+ fractionalDigits: dto.fractional_digits
1038
+ })
1039
+ };
1040
+
689
1041
  // src/shared/types/offer-detail.ts
690
1042
  var OfferOptionId = (value) => value;
691
1043
  var OfferOptionSlug = (value) => value;
692
1044
  var OfferDetail = {
693
1045
  fromDto: (dto) => {
694
1046
  if (!Array.isArray(dto.funding_assets)) {
695
- throw new Error(`funding_assets must be an array`);
1047
+ throw new ValidationError(
1048
+ `OfferDetail.funding_assets: expected an array, got ${typeof dto.funding_assets}`
1049
+ );
696
1050
  }
697
1051
  if (!Array.isArray(dto.options)) {
698
- throw new Error(`options must be an array`);
1052
+ throw new ValidationError(
1053
+ `OfferDetail.options: expected an array, got ${typeof dto.options}`
1054
+ );
699
1055
  }
700
1056
  if (!Array.isArray(dto.terms)) {
701
- throw new Error(`terms must be an array`);
1057
+ throw new ValidationError(
1058
+ `OfferDetail.terms: expected an array, got ${typeof dto.terms}`
1059
+ );
702
1060
  }
703
1061
  if (!Array.isArray(dto.links)) {
704
- throw new Error(`links must be an array`);
1062
+ throw new ValidationError(
1063
+ `OfferDetail.links: expected an array, got ${typeof dto.links}`
1064
+ );
705
1065
  }
706
1066
  if (!Array.isArray(dto.faqs)) {
707
- throw new Error(`faqs must be an array`);
1067
+ throw new ValidationError(
1068
+ `OfferDetail.faqs: expected an array, got ${typeof dto.faqs}`
1069
+ );
708
1070
  }
709
1071
  if (!Array.isArray(dto.milestones)) {
710
- throw new Error(`milestones must be an array`);
1072
+ throw new ValidationError(
1073
+ `OfferDetail.milestones: expected an array, got ${typeof dto.milestones}`
1074
+ );
711
1075
  }
712
1076
  if (!Array.isArray(dto.tokens)) {
713
- throw new Error(`tokens must be an array`);
1077
+ throw new ValidationError(
1078
+ `OfferDetail.tokens: expected an array, got ${typeof dto.tokens}`
1079
+ );
714
1080
  }
715
1081
  return {
716
1082
  id: OfferId(dto.id),
@@ -772,13 +1138,6 @@ var Milestone = {
772
1138
  status: dto.status
773
1139
  })
774
1140
  };
775
- var OfferToken = {
776
- fromDto: (dto) => ({
777
- role: dto.role,
778
- chain: Chain(dto.chain),
779
- address: EvmContractAddress(dto.address)
780
- })
781
- };
782
1141
 
783
1142
  // src/shared/types/pagination.ts
784
1143
  var Cursor = (value) => value;
@@ -838,18 +1197,25 @@ async function fetchOfferDetails(api, id, clientCreds) {
838
1197
  var ServerOffersNamespaceImpl = class {
839
1198
  constructor(ctx) {
840
1199
  this.ctx = ctx;
1200
+ this.log = internalLogger(ctx.logger, "OFFERS");
841
1201
  }
842
1202
  async list(clientCreds) {
843
- await this.ctx.ensureAuthenticated(clientCreds);
844
- return fetchOffers(this.ctx.api, clientCreds);
1203
+ return this.log.wrap("list", clientCreds, async () => {
1204
+ await this.ctx.ensureAuthenticated(clientCreds);
1205
+ return fetchOffers(this.ctx.api, clientCreds);
1206
+ });
845
1207
  }
846
1208
  async listPage(params, clientCreds) {
847
- await this.ctx.ensureAuthenticated(clientCreds);
848
- return fetchOffersPage(this.ctx.api, params, clientCreds);
1209
+ return this.log.wrap("listPage", { params, clientCreds }, async () => {
1210
+ await this.ctx.ensureAuthenticated(clientCreds);
1211
+ return fetchOffersPage(this.ctx.api, params, clientCreds);
1212
+ });
849
1213
  }
850
1214
  async get(id, clientCreds) {
851
- await this.ctx.ensureAuthenticated(clientCreds);
852
- return fetchOfferDetails(this.ctx.api, id, clientCreds);
1215
+ return this.log.wrap("get", { id, clientCreds }, async () => {
1216
+ await this.ctx.ensureAuthenticated(clientCreds);
1217
+ return fetchOfferDetails(this.ctx.api, id, clientCreds);
1218
+ });
853
1219
  }
854
1220
  };
855
1221
 
@@ -982,38 +1348,49 @@ async function fetchPii(api) {
982
1348
  var RequirementsNamespaceImpl = class {
983
1349
  constructor(ctx) {
984
1350
  this.ctx = ctx;
1351
+ this.log = internalLogger(ctx.logger, "REQUIREMENTS");
985
1352
  }
986
1353
  async forOffer(offerId) {
987
- await this.ctx.ensureUserAuthenticated();
988
- return fetchOfferRequirements(
989
- this.ctx.api,
990
- offerId,
991
- void 0
992
- );
1354
+ return this.log.wrap("forOffer", offerId, async () => {
1355
+ await this.ctx.ensureUserAuthenticated();
1356
+ return fetchOfferRequirements(
1357
+ this.ctx.api,
1358
+ offerId,
1359
+ void 0
1360
+ );
1361
+ });
993
1362
  }
994
1363
  async statuses(offerId) {
995
- await this.ctx.ensureUserAuthenticated();
996
- return fetchRequirementStatuses(this.ctx.api, offerId);
1364
+ return this.log.wrap("statuses", offerId, async () => {
1365
+ await this.ctx.ensureUserAuthenticated();
1366
+ return fetchRequirementStatuses(this.ctx.api, offerId);
1367
+ });
997
1368
  }
998
1369
  async createKycToken(params) {
999
- await this.ctx.ensureUserAuthenticated();
1000
- return createKycToken(
1001
- this.ctx.api,
1002
- params?.levelName,
1003
- params?.reset
1004
- );
1370
+ return this.log.wrap("createKycToken", params, async () => {
1371
+ await this.ctx.ensureUserAuthenticated();
1372
+ return createKycToken(
1373
+ this.ctx.api,
1374
+ params?.levelName,
1375
+ params?.reset
1376
+ );
1377
+ });
1005
1378
  }
1006
1379
  async getPii() {
1007
- await this.ctx.ensureUserAuthenticated();
1008
- return fetchPii(this.ctx.api);
1380
+ return this.log.wrap("getPii", void 0, async () => {
1381
+ await this.ctx.ensureUserAuthenticated();
1382
+ return fetchPii(this.ctx.api);
1383
+ });
1009
1384
  }
1010
1385
  async submitDocument(params) {
1011
- await this.ctx.ensureUserAuthenticated();
1012
- return submitDocument(
1013
- this.ctx.api,
1014
- params.documentType,
1015
- params.fields
1016
- );
1386
+ return this.log.wrap("submitDocument", params, async () => {
1387
+ await this.ctx.ensureUserAuthenticated();
1388
+ return submitDocument(
1389
+ this.ctx.api,
1390
+ params.documentType,
1391
+ params.fields
1392
+ );
1393
+ });
1017
1394
  }
1018
1395
  };
1019
1396
 
@@ -1024,12 +1401,14 @@ var ServerRequirementsNamespaceImpl = class extends RequirementsNamespaceImpl {
1024
1401
  this.serverCtx = serverCtx;
1025
1402
  }
1026
1403
  async forOffer(offerId, clientCreds) {
1027
- await this.serverCtx.ensureAuthenticated(clientCreds);
1028
- return fetchOfferRequirements(
1029
- this.serverCtx.api,
1030
- offerId,
1031
- clientCreds
1032
- );
1404
+ return this.log.wrap("forOffer", { offerId, clientCreds }, async () => {
1405
+ await this.serverCtx.ensureAuthenticated(clientCreds);
1406
+ return fetchOfferRequirements(
1407
+ this.serverCtx.api,
1408
+ offerId,
1409
+ clientCreds
1410
+ );
1411
+ });
1033
1412
  }
1034
1413
  };
1035
1414
 
@@ -1044,25 +1423,31 @@ var SwapAuthorization = {
1044
1423
  };
1045
1424
  var SwapPreview = {
1046
1425
  fromDto: (dto) => ({
1047
- inputAmount: assertUint256(BigInt(dto.pay_input_amount)),
1048
- fee: assertUint256(BigInt(dto.fee)),
1049
- outputAmount: assertUint256(BigInt(dto.receive_output_amount))
1426
+ inputAmount: parseUint256(
1427
+ BigInt(dto.pay_input_amount),
1428
+ "SwapPreview.pay_input_amount"
1429
+ ),
1430
+ fee: parseUint256(BigInt(dto.fee), "SwapPreview.fee"),
1431
+ outputAmount: parseUint256(
1432
+ BigInt(dto.receive_output_amount),
1433
+ "SwapPreview.receive_output_amount"
1434
+ )
1050
1435
  })
1051
1436
  };
1052
1437
  var SwapStatus = {
1053
1438
  fromDto: (dto) => ({
1054
- stopped: assertUint256(BigInt(dto.stopped)),
1055
- swapLevel: assertUint256(BigInt(dto.swap_level))
1439
+ stopped: parseUint256(BigInt(dto.stopped), "SwapStatus.stopped"),
1440
+ swapLevel: parseUint256(BigInt(dto.swap_level), "SwapStatus.swap_level")
1056
1441
  })
1057
1442
  };
1058
1443
  var TokenAllowance = {
1059
1444
  fromDto: (dto) => ({
1060
- allowance: assertUint256(BigInt(dto.allowance))
1445
+ allowance: parseUint256(BigInt(dto.allowance), "TokenAllowance.allowance")
1061
1446
  })
1062
1447
  };
1063
1448
  var TokenBalance = {
1064
1449
  fromDto: (dto) => ({
1065
- balance: assertUint256(BigInt(dto.balance))
1450
+ balance: parseUint256(BigInt(dto.balance), "TokenBalance.balance")
1066
1451
  })
1067
1452
  };
1068
1453
  var AllowWalletResponse = {
@@ -1191,14 +1576,19 @@ function toErc20Asset(dto) {
1191
1576
  var Erc20NamespaceImpl = class {
1192
1577
  constructor(ctx) {
1193
1578
  this.ctx = ctx;
1579
+ this.log = internalLogger(ctx.logger, "ERC20");
1194
1580
  }
1195
1581
  async getAllowance(params) {
1196
- await this.ctx.ensureUserAuthenticated();
1197
- return getTokenAllowance(this.ctx.api, params);
1582
+ return this.log.wrap("getAllowance", params, async () => {
1583
+ await this.ctx.ensureUserAuthenticated();
1584
+ return getTokenAllowance(this.ctx.api, params);
1585
+ });
1198
1586
  }
1199
1587
  async getBalance(params) {
1200
- await this.ctx.ensureUserAuthenticated();
1201
- return getTokenBalance(this.ctx.api, params);
1588
+ return this.log.wrap("getBalance", params, async () => {
1589
+ await this.ctx.ensureUserAuthenticated();
1590
+ return getTokenBalance(this.ctx.api, params);
1591
+ });
1202
1592
  }
1203
1593
  };
1204
1594
 
@@ -1287,22 +1677,31 @@ async function createParticipation(api, params) {
1287
1677
  var CoinListTokenSaleNamespaceImpl = class {
1288
1678
  constructor(ctx) {
1289
1679
  this.ctx = ctx;
1680
+ this.log = internalLogger(ctx.logger, "TOKEN_SALE");
1290
1681
  }
1291
1682
  async list(offerId) {
1292
- await this.ctx.ensureUserAuthenticated();
1293
- return fetchParticipations(this.ctx.api, offerId);
1683
+ return this.log.wrap("list", offerId, async () => {
1684
+ await this.ctx.ensureUserAuthenticated();
1685
+ return fetchParticipations(this.ctx.api, offerId);
1686
+ });
1294
1687
  }
1295
1688
  async listPage(params) {
1296
- await this.ctx.ensureUserAuthenticated();
1297
- return fetchParticipationsPage(this.ctx.api, params);
1689
+ return this.log.wrap("listPage", params, async () => {
1690
+ await this.ctx.ensureUserAuthenticated();
1691
+ return fetchParticipationsPage(this.ctx.api, params);
1692
+ });
1298
1693
  }
1299
1694
  async get(id) {
1300
- await this.ctx.ensureUserAuthenticated();
1301
- return fetchParticipation(this.ctx.api, id);
1695
+ return this.log.wrap("get", id, async () => {
1696
+ await this.ctx.ensureUserAuthenticated();
1697
+ return fetchParticipation(this.ctx.api, id);
1698
+ });
1302
1699
  }
1303
1700
  async createParticipation(params) {
1304
- await this.ctx.ensureUserAuthenticated();
1305
- return createParticipation(this.ctx.api, params);
1701
+ return this.log.wrap("createParticipation", params, async () => {
1702
+ await this.ctx.ensureUserAuthenticated();
1703
+ return createParticipation(this.ctx.api, params);
1704
+ });
1306
1705
  }
1307
1706
  };
1308
1707
 
@@ -1322,7 +1721,9 @@ var USD_FRACTION_DIGITS = AssetDecimals(2);
1322
1721
  // src/shared/core/blockchain/chain.ts
1323
1722
  var CHAIN_IDS = {
1324
1723
  ethereum_mainnet: 1,
1325
- ethereum_sepolia: 11155111
1724
+ ethereum_sepolia: 11155111,
1725
+ base_mainnet: 8453,
1726
+ base_sepolia: 84532
1326
1727
  };
1327
1728
  function chainFromId(chainId) {
1328
1729
  const chains = Object.keys(CHAIN_IDS);
@@ -1398,47 +1799,122 @@ var OndoQuote = {
1398
1799
  };
1399
1800
  }
1400
1801
  };
1401
- var OndoSwapTransaction = {
1802
+ var OndoBuyTransaction = {
1402
1803
  fromDto: (dto) => {
1403
- const inputDecimals = AssetDecimals(dto.pay_input_decimals);
1804
+ const spendDecimals = AssetDecimals(dto.spend_input_decimals);
1404
1805
  const outputDecimals = AssetDecimals(dto.receive_output_decimals);
1405
1806
  return {
1406
- tx: {
1407
- to: EvmContractAddress(dto.to),
1408
- data: HexEncodedTransactionData(dto.data)
1409
- },
1410
- expiresAt: parseExpiresAt(dto.expires_at),
1411
- payInputAmount: blockchainAmountFromRawOrThrow({
1412
- label: "pay_input_amount",
1413
- raw: dto.pay_input_amount,
1414
- decimals: inputDecimals
1415
- }),
1807
+ ...parseSwapCore(dto, spendDecimals),
1808
+ side: "buy",
1416
1809
  fee: blockchainAmountFromRawOrThrow({
1417
1810
  label: "fee",
1418
1811
  raw: dto.fee,
1419
- decimals: inputDecimals
1812
+ decimals: spendDecimals
1420
1813
  }),
1421
1814
  notionalValue: blockchainAmountFromRawOrThrow({
1422
1815
  label: "notional_value",
1423
1816
  raw: dto.notional_value,
1424
- decimals: inputDecimals
1817
+ decimals: spendDecimals
1425
1818
  }),
1426
- receiveOutputAmount: parseReceiveOutputAmount(
1427
- dto.receive_output_amount,
1428
- outputDecimals
1429
- )
1819
+ receiveOutputAmount: parsePositiveAmount({
1820
+ label: "receive_output_amount",
1821
+ raw: dto.receive_output_amount,
1822
+ decimals: outputDecimals,
1823
+ // A transaction that yields nothing is not one to sign - the user
1824
+ // would pay the deposit and receive no asset - and frontline refuses
1825
+ // to emit one. A zero here is a changed encoding, not a small order.
1826
+ // Rejecting it at the boundary is also what lets `computeOndoBuyPrice`
1827
+ // divide by it without a fallible result: the failure surfaces as the
1828
+ // data hook's ERROR state rather than as a division during render.
1829
+ reason: "a buy that yields nothing is not fillable"
1830
+ })
1430
1831
  };
1431
1832
  }
1432
1833
  };
1433
- function parseReceiveOutputAmount(raw, decimals) {
1434
- const amount = blockchainAmountFromRawOrThrow({
1435
- label: "receive_output_amount",
1834
+ var OndoSellTransaction = {
1835
+ fromDto: (dto) => {
1836
+ const spendDecimals = AssetDecimals(dto.spend_input_decimals);
1837
+ const outputDecimals = AssetDecimals(dto.receive_output_decimals);
1838
+ const expectedQuantity = parsePositiveAmount({
1839
+ label: "expected_quantity",
1840
+ raw: dto.expected_quantity,
1841
+ decimals: outputDecimals,
1842
+ reason: "a sale that yields nothing is not fillable"
1843
+ });
1844
+ return {
1845
+ ...parseSwapCore(dto, spendDecimals),
1846
+ side: "sell",
1847
+ expected: {
1848
+ quantity: expectedQuantity,
1849
+ fee: blockchainAmountFromRawOrThrow({
1850
+ label: "expected_fee",
1851
+ raw: dto.expected_fee,
1852
+ decimals: outputDecimals
1853
+ })
1854
+ },
1855
+ minimum: {
1856
+ quantity: parseMinimumQuantity(
1857
+ dto.minimum_quantity,
1858
+ outputDecimals,
1859
+ expectedQuantity
1860
+ ),
1861
+ fee: blockchainAmountFromRawOrThrow({
1862
+ label: "minimum_fee",
1863
+ raw: dto.minimum_fee,
1864
+ decimals: outputDecimals
1865
+ })
1866
+ }
1867
+ };
1868
+ }
1869
+ };
1870
+ function parseSwapCore(dto, spendDecimals) {
1871
+ return {
1872
+ tx: {
1873
+ to: EvmContractAddress(dto.to),
1874
+ data: HexEncodedTransactionData(dto.data)
1875
+ },
1876
+ expiresAt: parseExpiresAt(dto.expires_at),
1877
+ spendInputAmount: parsePositiveAmount({
1878
+ label: "spend_input_amount",
1879
+ raw: dto.spend_input_amount,
1880
+ decimals: spendDecimals,
1881
+ // A transaction that takes nothing from the wallet is not one to sign:
1882
+ // it would settle one leg of a trade and skip the other. Frontline
1883
+ // refuses an `amount` of zero whichever way the trade runs, so this is a
1884
+ // changed encoding rather than a small order.
1885
+ //
1886
+ // Guarded on both sides rather than on the sale alone, because which
1887
+ // amount becomes the divisor in the price flips with the direction: a
1888
+ // guard placed by that would be a rule about the arithmetic rather than
1889
+ // about the trade.
1890
+ reason: "a swap that spends nothing is not fillable"
1891
+ })
1892
+ };
1893
+ }
1894
+ function parseMinimumQuantity(raw, decimals, expectedQuantity) {
1895
+ const amount = parsePositiveAmount({
1896
+ label: "minimum_quantity",
1436
1897
  raw,
1437
- decimals
1898
+ decimals,
1899
+ reason: "a floor of zero guarantees nothing"
1438
1900
  });
1901
+ if (amount.raw > expectedQuantity.raw) {
1902
+ throw new ValidationError(
1903
+ `minimum_quantity: must not exceed expected_quantity ("${raw}" > "${expectedQuantity.raw}")`
1904
+ );
1905
+ }
1906
+ return amount;
1907
+ }
1908
+ function parsePositiveAmount({
1909
+ label,
1910
+ raw,
1911
+ decimals,
1912
+ reason
1913
+ }) {
1914
+ const amount = blockchainAmountFromRawOrThrow({ label, raw, decimals });
1439
1915
  if (amount.raw <= 0n) {
1440
1916
  throw new ValidationError(
1441
- `receive_output_amount: must be greater than zero ("${raw}")`
1917
+ `${label}: must be greater than zero ("${raw}") - ${reason}`
1442
1918
  );
1443
1919
  }
1444
1920
  return amount;
@@ -1475,27 +1951,57 @@ async function getOndoQuote(api, params) {
1475
1951
  });
1476
1952
  return OndoQuote.fromDto(dto);
1477
1953
  }
1478
- async function buildOndoSwapTransaction(api, params) {
1954
+ async function buildOndoBuy(api, params) {
1479
1955
  const dto = await api.send({
1480
1956
  method: "POST",
1481
- url: "/v1/ondo/swap/transaction",
1482
- body: {
1483
- symbol: params.symbol,
1484
- chain: params.chain,
1485
- wallet_address: params.walletAddress,
1486
- amount: params.amount.raw.toString()
1487
- },
1957
+ url: "/v1/ondo/swap/buy",
1958
+ body: swapBody(params),
1488
1959
  attributes: Attributes.protected()
1489
1960
  });
1490
- assertFundingScaleAgrees(dto, params);
1491
- return OndoSwapTransaction.fromDto(dto);
1961
+ assertSpendScaleAgrees({
1962
+ published: dto.spend_input_decimals,
1963
+ sized: params.amount,
1964
+ trade: "purchase"
1965
+ });
1966
+ return OndoBuyTransaction.fromDto(dto);
1492
1967
  }
1493
- function assertFundingScaleAgrees(dto, params) {
1494
- if (dto.pay_input_decimals !== params.amount.decimals) {
1495
- throw new ValidationError(
1496
- `pay_input_decimals: the swap was priced in ${dto.pay_input_decimals} decimals but the order was sized in ${params.amount.decimals}`
1497
- );
1498
- }
1968
+ async function buildOndoSell(api, params) {
1969
+ const dto = await api.send({
1970
+ method: "POST",
1971
+ url: "/v1/ondo/swap/sell",
1972
+ body: swapBody(params),
1973
+ attributes: Attributes.protected()
1974
+ });
1975
+ assertSpendScaleAgrees({
1976
+ published: dto.spend_input_decimals,
1977
+ sized: params.amount,
1978
+ trade: "sale",
1979
+ // The two answers come from two chains, so on a testnet they can disagree
1980
+ // for a reason that is neither the caller's nor a corrupt response. Say so,
1981
+ // or a QA run reads as a puzzle rather than a diagnosis.
1982
+ note: "the quote resolves the asset on Ethereum mainnet while the swap executes on the chain requested, so these disagree until frontline serves a chain-scoped quote"
1983
+ });
1984
+ return OndoSellTransaction.fromDto(dto);
1985
+ }
1986
+ function swapBody(params) {
1987
+ return {
1988
+ symbol: params.symbol,
1989
+ chain: params.chain,
1990
+ wallet_address: params.walletAddress,
1991
+ amount: params.amount.raw.toString()
1992
+ };
1993
+ }
1994
+ function assertSpendScaleAgrees({
1995
+ published,
1996
+ sized,
1997
+ trade,
1998
+ note
1999
+ }) {
2000
+ if (published === sized.decimals) return;
2001
+ const because = note === void 0 ? "" : ` - ${note}`;
2002
+ throw new ValidationError(
2003
+ `spend_input_decimals: the ${trade} was priced in ${published} decimals but the order was sized in ${sized.decimals}${because}`
2004
+ );
1499
2005
  }
1500
2006
  function sizeParam(params) {
1501
2007
  const tokenAmount = "tokenAmount" in params ? params.tokenAmount : void 0;
@@ -1520,18 +2026,31 @@ function sizeParam(params) {
1520
2026
  var OndoNamespaceImpl = class {
1521
2027
  constructor(ctx) {
1522
2028
  this.ctx = ctx;
2029
+ this.log = internalLogger(ctx.logger, "ONDO");
1523
2030
  }
1524
2031
  async getTradingStatus(params) {
1525
- await this.ctx.ensureUserAuthenticated();
1526
- return getOndoTradingStatus(this.ctx.api, params);
2032
+ return this.log.wrap("getTradingStatus", params, async () => {
2033
+ await this.ctx.ensureUserAuthenticated();
2034
+ return getOndoTradingStatus(this.ctx.api, params);
2035
+ });
1527
2036
  }
1528
2037
  async getQuote(params) {
1529
- await this.ctx.ensureUserAuthenticated();
1530
- return getOndoQuote(this.ctx.api, params);
2038
+ return this.log.wrap("getQuote", params, async () => {
2039
+ await this.ctx.ensureUserAuthenticated();
2040
+ return getOndoQuote(this.ctx.api, params);
2041
+ });
2042
+ }
2043
+ async buildBuyTransaction(params) {
2044
+ return this.log.wrap("buildBuyTransaction", params, async () => {
2045
+ await this.ctx.ensureUserAuthenticated();
2046
+ return buildOndoBuy(this.ctx.api, params);
2047
+ });
1531
2048
  }
1532
- async buildSwapTransaction(params) {
1533
- await this.ctx.ensureUserAuthenticated();
1534
- return buildOndoSwapTransaction(this.ctx.api, params);
2049
+ async buildSellTransaction(params) {
2050
+ return this.log.wrap("buildSellTransaction", params, async () => {
2051
+ await this.ctx.ensureUserAuthenticated();
2052
+ return buildOndoSell(this.ctx.api, params);
2053
+ });
1535
2054
  }
1536
2055
  };
1537
2056
 
@@ -1539,26 +2058,37 @@ var OndoNamespaceImpl = class {
1539
2058
  var SuperstateSwapNamespaceImpl = class {
1540
2059
  constructor(ctx) {
1541
2060
  this.ctx = ctx;
2061
+ this.log = internalLogger(ctx.logger, "SUPERSTATE");
1542
2062
  }
1543
2063
  async getAuthorization(params) {
1544
- await this.ctx.ensureUserAuthenticated();
1545
- return getSwapAuthorization(this.ctx.api, params);
2064
+ return this.log.wrap("getAuthorization", params, async () => {
2065
+ await this.ctx.ensureUserAuthenticated();
2066
+ return getSwapAuthorization(this.ctx.api, params);
2067
+ });
1546
2068
  }
1547
2069
  async getPreview(params) {
1548
- await this.ctx.ensureUserAuthenticated();
1549
- return getSwapPreview(this.ctx.api, params);
2070
+ return this.log.wrap("getPreview", params, async () => {
2071
+ await this.ctx.ensureUserAuthenticated();
2072
+ return getSwapPreview(this.ctx.api, params);
2073
+ });
1550
2074
  }
1551
2075
  async getStatus(params) {
1552
- await this.ctx.ensureUserAuthenticated();
1553
- return getSwapStatus(this.ctx.api, params);
2076
+ return this.log.wrap("getStatus", params, async () => {
2077
+ await this.ctx.ensureUserAuthenticated();
2078
+ return getSwapStatus(this.ctx.api, params);
2079
+ });
1554
2080
  }
1555
2081
  async getOutputToken(params) {
1556
- await this.ctx.ensureUserAuthenticated();
1557
- return getSwapOutputToken(this.ctx.api, params);
2082
+ return this.log.wrap("getOutputToken", params, async () => {
2083
+ await this.ctx.ensureUserAuthenticated();
2084
+ return getSwapOutputToken(this.ctx.api, params);
2085
+ });
1558
2086
  }
1559
2087
  async allowWallet(params) {
1560
- await this.ctx.ensureUserAuthenticated();
1561
- return allowWallet(this.ctx.api, params);
2088
+ return this.log.wrap("allowWallet", params, async () => {
2089
+ await this.ctx.ensureUserAuthenticated();
2090
+ return allowWallet(this.ctx.api, params);
2091
+ });
1562
2092
  }
1563
2093
  };
1564
2094
 
@@ -1591,27 +2121,47 @@ var TokenMetadata = {
1591
2121
  logoDark: dto.logo_dark === void 0 ? null : TokenLogo.fromDto(dto.logo_dark, baseUrl)
1592
2122
  };
1593
2123
  },
2124
+ /**
2125
+ * Maps the complete registry snapshot to every token it lists across the
2126
+ * chains this SDK models, skipping native coins and chains outside
2127
+ * {@link EthereumChain} (e.g. Solana) rather than failing on them — the
2128
+ * registry may serve chains ahead of the SDK's type surface.
2129
+ */
2130
+ fromRegistryDto: (dto, baseUrl) => {
2131
+ assertSupportedSchemaVersion(dto.schema_version);
2132
+ return dto.chains.flatMap((chainDto) => {
2133
+ let chain;
2134
+ try {
2135
+ chain = EthereumChain(chainDto.chain);
2136
+ } catch {
2137
+ return [];
2138
+ }
2139
+ return tokensOfChain(chain, chainDto.assets, baseUrl);
2140
+ });
2141
+ },
1594
2142
  /**
1595
2143
  * Maps a chain snapshot to the tokens it lists, skipping the chain's native
1596
2144
  * coin (`kind: 'COIN'`, no contract address).
1597
2145
  */
1598
2146
  fromChainAssetsDto: (dto, baseUrl) => {
1599
2147
  assertSupportedSchemaVersion(dto.schema_version);
1600
- const chain = EthereumChain(dto.chain);
1601
- return dto.assets.filter((asset) => asset.kind === "TOKEN" && asset.address !== void 0).map((asset) => ({
1602
- identifier: {
1603
- chain,
1604
- // The filter above cannot narrow `address` for the type checker.
1605
- address: EvmContractAddress(asset.address)
1606
- },
1607
- name: asset.name,
1608
- symbol: AssetSymbol(asset.symbol),
1609
- decimals: AssetDecimals(asset.decimals),
1610
- logo: TokenLogo.fromDto(asset.logo, baseUrl),
1611
- logoDark: asset.logo_dark === void 0 ? null : TokenLogo.fromDto(asset.logo_dark, baseUrl)
1612
- }));
2148
+ return tokensOfChain(EthereumChain(dto.chain), dto.assets, baseUrl);
1613
2149
  }
1614
2150
  };
2151
+ function tokensOfChain(chain, assets, baseUrl) {
2152
+ return assets.filter((asset) => asset.kind === "TOKEN" && asset.address !== void 0).map((asset) => ({
2153
+ identifier: {
2154
+ chain,
2155
+ // The filter above cannot narrow `address` for the type checker.
2156
+ address: EvmContractAddress(asset.address)
2157
+ },
2158
+ name: asset.name,
2159
+ symbol: AssetSymbol(asset.symbol),
2160
+ decimals: AssetDecimals(asset.decimals),
2161
+ logo: TokenLogo.fromDto(asset.logo, baseUrl),
2162
+ logoDark: asset.logo_dark === void 0 ? null : TokenLogo.fromDto(asset.logo_dark, baseUrl)
2163
+ }));
2164
+ }
1615
2165
  function assertSupportedSchemaVersion(version) {
1616
2166
  if (version !== 1) {
1617
2167
  throw new ValidationError(
@@ -1629,8 +2179,8 @@ var resolveLogoUrl = (url, baseUrl) => TokenLogoUrl(
1629
2179
  );
1630
2180
 
1631
2181
  // src/shared/api/nabu/tokens.ts
1632
- function createNabuApiClient(baseUrl) {
1633
- return new HttpClient({ baseUrl });
2182
+ function createNabuApiClient(baseUrl, logger = null) {
2183
+ return new HttpClient({ baseUrl }, {}, logger);
1634
2184
  }
1635
2185
  async function fetchTokenMetadata(client, token) {
1636
2186
  const address = checksummed(token.address);
@@ -1680,6 +2230,29 @@ async function fetchTokensMetadata(client, chain) {
1680
2230
  }
1681
2231
  return TokenMetadata.fromChainAssetsDto(response.body, client.config.baseUrl);
1682
2232
  }
2233
+ async function fetchAllTokensMetadata(client) {
2234
+ let response;
2235
+ try {
2236
+ response = await client.send({
2237
+ method: "GET",
2238
+ url: `/assets.json`
2239
+ });
2240
+ } catch (error) {
2241
+ if (isRegistryHtmlFallback(error)) {
2242
+ throw new ValidationError(
2243
+ "Token registry returned non-JSON for the complete snapshot"
2244
+ );
2245
+ }
2246
+ throw error;
2247
+ }
2248
+ assertOk(response);
2249
+ if (response.body === null) {
2250
+ throw new ValidationError(
2251
+ "Token registry returned an empty complete snapshot"
2252
+ );
2253
+ }
2254
+ return TokenMetadata.fromRegistryDto(response.body, client.config.baseUrl);
2255
+ }
1683
2256
  function isRegistryHtmlFallback(error) {
1684
2257
  return error instanceof HttpError && error.response.status >= 200 && error.response.status < 300;
1685
2258
  }
@@ -1703,14 +2276,23 @@ var TokensNamespaceImpl = class {
1703
2276
  * registry is unauthenticated and on its own host, so the frontline sender
1704
2277
  * and the auth check would both be dead weight here.
1705
2278
  */
1706
- constructor(baseUrl) {
1707
- this.api = createNabuApiClient(baseUrl);
2279
+ constructor(baseUrl, logger = null) {
2280
+ this.api = createNabuApiClient(baseUrl, logger);
2281
+ this.log = internalLogger(logger, "TOKENS");
1708
2282
  }
1709
2283
  get(token) {
1710
- return fetchTokenMetadata(this.api, token);
2284
+ return this.log.wrap(
2285
+ "get",
2286
+ token,
2287
+ () => fetchTokenMetadata(this.api, token)
2288
+ );
1711
2289
  }
1712
2290
  list(chain) {
1713
- return fetchTokensMetadata(this.api, chain);
2291
+ return this.log.wrap(
2292
+ "list",
2293
+ chain,
2294
+ () => chain === void 0 ? fetchAllTokensMetadata(this.api) : fetchTokensMetadata(this.api, chain)
2295
+ );
1714
2296
  }
1715
2297
  };
1716
2298
 
@@ -1816,33 +2398,42 @@ async function removeOptionAddress(api, offerId, addressId) {
1816
2398
  var WalletsNamespaceImpl = class {
1817
2399
  constructor(ctx) {
1818
2400
  this.ctx = ctx;
2401
+ this.log = internalLogger(ctx.logger, "WALLETS");
1819
2402
  }
1820
2403
  async createOwnershipChallenge(params) {
1821
- await this.ctx.ensureUserAuthenticated();
1822
- return createWalletOwnershipChallenge(
1823
- this.ctx.api,
1824
- params
1825
- );
2404
+ return this.log.wrap("createOwnershipChallenge", params, async () => {
2405
+ await this.ctx.ensureUserAuthenticated();
2406
+ return createWalletOwnershipChallenge(
2407
+ this.ctx.api,
2408
+ params
2409
+ );
2410
+ });
1826
2411
  }
1827
2412
  async connectExternal(params) {
1828
- await this.ctx.ensureUserAuthenticated();
1829
- return connectExternalWallet(this.ctx.api, params);
2413
+ return this.log.wrap("connectExternal", params, async () => {
2414
+ await this.ctx.ensureUserAuthenticated();
2415
+ return connectExternalWallet(this.ctx.api, params);
2416
+ });
1830
2417
  }
1831
2418
  async list(params) {
1832
- await this.ctx.ensureUserAuthenticated();
1833
- return listOptionAddresses(
1834
- this.ctx.api,
1835
- params.offerId,
1836
- params.offerOptionId
1837
- );
2419
+ return this.log.wrap("list", params, async () => {
2420
+ await this.ctx.ensureUserAuthenticated();
2421
+ return listOptionAddresses(
2422
+ this.ctx.api,
2423
+ params.offerId,
2424
+ params.offerOptionId
2425
+ );
2426
+ });
1838
2427
  }
1839
2428
  async remove(params) {
1840
- await this.ctx.ensureUserAuthenticated();
1841
- return removeOptionAddress(
1842
- this.ctx.api,
1843
- params.offerId,
1844
- params.addressId
1845
- );
2429
+ return this.log.wrap("remove", params, async () => {
2430
+ await this.ctx.ensureUserAuthenticated();
2431
+ return removeOptionAddress(
2432
+ this.ctx.api,
2433
+ params.offerId,
2434
+ params.addressId
2435
+ );
2436
+ });
1846
2437
  }
1847
2438
  };
1848
2439
 
@@ -1851,6 +2442,7 @@ var ACCESS_TOKEN_EXPIRY_BUFFER_SECONDS = 60;
1851
2442
  var CoinListServerImpl = class {
1852
2443
  constructor(_config) {
1853
2444
  this._config = _config;
2445
+ const logger = _config.logger ?? null;
1854
2446
  this.api = new ApiClient(
1855
2447
  {
1856
2448
  baseUrl: _config.baseUrl ?? PUBLIC_API_BASE_URL,
@@ -1860,7 +2452,8 @@ var CoinListServerImpl = class {
1860
2452
  // fresh token. A read-only store cannot persist a new session, so return
1861
2453
  // null immediately — this tells the middleware to skip the retry rather
1862
2454
  // than re-sending with the same expired token and wasting a round-trip.
1863
- (refresh) => refresh && !this._config.sessionStore.setSession ? Promise.resolve(null) : this.auth.getAccessToken()
2455
+ (refresh) => refresh && !this._config.sessionStore.setSession ? Promise.resolve(null) : this.auth.getAccessToken(),
2456
+ logger
1864
2457
  );
1865
2458
  this.auth = new ServerAuthNamespaceImpl(this.api, {
1866
2459
  ..._config,
@@ -1869,6 +2462,7 @@ var CoinListServerImpl = class {
1869
2462
  });
1870
2463
  const ctx = {
1871
2464
  api: this.api,
2465
+ logger,
1872
2466
  ensureUserAuthenticated: () => this.ensureAuthenticated(void 0),
1873
2467
  ensureAuthenticated: (clientCreds) => this.ensureAuthenticated(clientCreds)
1874
2468
  };
@@ -1880,7 +2474,8 @@ var CoinListServerImpl = class {
1880
2474
  this.superstate = new SuperstateSwapNamespaceImpl(ctx);
1881
2475
  this.ondo = new OndoNamespaceImpl(ctx);
1882
2476
  this.tokens = new TokensNamespaceImpl(
1883
- _config.tokensBaseUrl ?? NABU_BASE_URL
2477
+ _config.tokensBaseUrl ?? NABU_BASE_URL,
2478
+ logger
1884
2479
  );
1885
2480
  }
1886
2481
  /**
@@ -1898,12 +2493,157 @@ var CoinListServerImpl = class {
1898
2493
  function createCoinListServer(config) {
1899
2494
  return new CoinListServerImpl(config);
1900
2495
  }
2496
+
2497
+ // src/server/core/observability/pino-server-logger.ts
2498
+ var import_pino = require("pino");
2499
+
2500
+ // src/shared/core/observability/pino-logger.ts
2501
+ function toPinoRecord(event) {
2502
+ try {
2503
+ const record = {};
2504
+ for (const key of ownKeys(event.fields)) {
2505
+ record[key] = readProperty(event.fields, key, 0, /* @__PURE__ */ new WeakSet());
2506
+ }
2507
+ const cause = "cause" in event ? event.cause : void 0;
2508
+ if (cause !== void 0) {
2509
+ record.cause = sanitize(cause, 0, /* @__PURE__ */ new WeakSet());
2510
+ }
2511
+ return { ...record, scope: event.scope, ...event.bindings };
2512
+ } catch (_) {
2513
+ return { "log.render": "<unrenderable event>" };
2514
+ }
2515
+ }
2516
+ var PINO_LEVEL = {
2517
+ none: "silent",
2518
+ error: "error",
2519
+ warn: "warn",
2520
+ info: "info",
2521
+ debug: "debug"
2522
+ };
2523
+ var MAX_DEPTH = 8;
2524
+ function sanitize(value, depth, seen) {
2525
+ switch (typeof value) {
2526
+ case "string":
2527
+ case "boolean":
2528
+ return value;
2529
+ case "number":
2530
+ return Number.isFinite(value) ? value : String(value);
2531
+ case "bigint":
2532
+ return value.toString();
2533
+ case "undefined":
2534
+ return "<undefined>";
2535
+ case "function":
2536
+ return "<function>";
2537
+ case "symbol":
2538
+ return value.toString();
2539
+ case "object":
2540
+ return value === null ? null : sanitizeObject(value, depth, seen);
2541
+ default:
2542
+ return "<unrenderable>";
2543
+ }
2544
+ }
2545
+ function sanitizeObject(value, depth, seen) {
2546
+ if (seen.has(value)) return "<circular>";
2547
+ if (depth >= MAX_DEPTH) return "<max depth>";
2548
+ if (value instanceof Error) return describeError(value);
2549
+ if (value instanceof Date) return describeDate(value);
2550
+ seen.add(value);
2551
+ try {
2552
+ if (Array.isArray(value)) {
2553
+ return value.map(
2554
+ (_item, index) => readProperty(value, String(index), depth, seen)
2555
+ );
2556
+ }
2557
+ const out = {};
2558
+ for (const key of ownKeys(value)) {
2559
+ out[key] = readProperty(value, key, depth, seen);
2560
+ }
2561
+ return out;
2562
+ } finally {
2563
+ seen.delete(value);
2564
+ }
2565
+ }
2566
+ function readProperty(owner, key, depth, seen) {
2567
+ try {
2568
+ return sanitize(owner[key], depth + 1, seen);
2569
+ } catch (_) {
2570
+ return "<unreadable>";
2571
+ }
2572
+ }
2573
+ function ownKeys(value) {
2574
+ try {
2575
+ return Object.keys(value);
2576
+ } catch (_) {
2577
+ return [];
2578
+ }
2579
+ }
2580
+ function describeError(error) {
2581
+ return {
2582
+ name: safeRead(() => error.name),
2583
+ message: safeRead(() => error.message),
2584
+ stack: safeRead(() => error.stack)
2585
+ };
2586
+ }
2587
+ function describeDate(date) {
2588
+ return safeRead(() => date.toISOString());
2589
+ }
2590
+ function safeRead(read) {
2591
+ try {
2592
+ const value = read();
2593
+ return typeof value === "string" ? value : "<unreadable>";
2594
+ } catch (_) {
2595
+ return "<unreadable>";
2596
+ }
2597
+ }
2598
+ var SDK_LOGGER_NAME = "@coinlist-co/react";
2599
+ function loggerOverPino(sink, level) {
2600
+ return {
2601
+ level: () => level,
2602
+ debug: (event) => emit(sink, "debug", event),
2603
+ info: (event) => emit(sink, "info", event),
2604
+ warn: (event) => emit(sink, "warn", event),
2605
+ error: (event) => emit(sink, "error", event)
2606
+ };
2607
+ }
2608
+ function emit(sink, method, event) {
2609
+ try {
2610
+ const value = event();
2611
+ sink[method](toPinoRecord(value), value.msg);
2612
+ } catch (error) {
2613
+ reportRenderFailure(sink, error);
2614
+ }
2615
+ }
2616
+ function reportRenderFailure(sink, error) {
2617
+ try {
2618
+ sink.error(
2619
+ { "error.type": error instanceof Error ? error.name : typeof error },
2620
+ "log event failed to render"
2621
+ );
2622
+ } catch (_) {
2623
+ }
2624
+ }
2625
+
2626
+ // src/server/core/observability/pino-server-logger.ts
2627
+ function pinoServerLogger(options) {
2628
+ return loggerOverPino(
2629
+ // `name` as a child binding rather than pino's `name` option: the option
2630
+ // is honoured by pino's node build and silently dropped by its browser
2631
+ // build, so a binding is the only spelling that identifies the SDK in
2632
+ // both environments.
2633
+ (0, import_pino.pino)({
2634
+ level: PINO_LEVEL[options.level]
2635
+ }).child({ name: SDK_LOGGER_NAME }),
2636
+ options.level
2637
+ );
2638
+ }
1901
2639
  // Annotate the CommonJS export names for ESM import in node:
1902
2640
  0 && (module.exports = {
1903
2641
  ServerAuthNamespaceImpl,
1904
2642
  ServerOffersNamespaceImpl,
1905
2643
  ServerRequirementsNamespaceImpl,
1906
2644
  WritableSessionStoreRequiredError,
1907
- createCoinListServer
2645
+ createCoinListServer,
2646
+ emptySessionStore,
2647
+ pinoServerLogger
1908
2648
  });
1909
2649
  //# sourceMappingURL=index.cjs.map