@minswap/sdk-v2 1.0.0

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.
package/dist/index.js ADDED
@@ -0,0 +1,4558 @@
1
+ import { z } from 'zod';
2
+ import JSONBig from 'json-bigint';
3
+
4
+ // src/client/errors.ts
5
+ var MinswapErrorCode = /* @__PURE__ */ ((MinswapErrorCode2) => {
6
+ MinswapErrorCode2["NETWORK"] = "NETWORK";
7
+ MinswapErrorCode2["TIMEOUT"] = "TIMEOUT";
8
+ MinswapErrorCode2["RATE_LIMITED"] = "RATE_LIMITED";
9
+ MinswapErrorCode2["NOT_FOUND"] = "NOT_FOUND";
10
+ MinswapErrorCode2["INVALID_PARAMS"] = "INVALID_PARAMS";
11
+ MinswapErrorCode2["PARSE_ERROR"] = "PARSE_ERROR";
12
+ MinswapErrorCode2["API_ERROR"] = "API_ERROR";
13
+ MinswapErrorCode2["GRAPHQL_ERROR"] = "GRAPHQL_ERROR";
14
+ return MinswapErrorCode2;
15
+ })(MinswapErrorCode || {});
16
+ var MinswapError = class _MinswapError extends Error {
17
+ name = "MinswapError";
18
+ code;
19
+ details;
20
+ constructor(message, code, details) {
21
+ super(message);
22
+ this.code = code;
23
+ this.details = details;
24
+ Object.setPrototypeOf(this, new.target.prototype);
25
+ }
26
+ /**
27
+ * Type guard for narrowing an unknown error to a specific code.
28
+ *
29
+ * @example
30
+ * try { await sdk.token.getById(id); }
31
+ * catch (e) {
32
+ * if (MinswapError.is(e, MinswapErrorCode.RATE_LIMITED)) {
33
+ * // e.details.retryAfterMs is typed here
34
+ * }
35
+ * }
36
+ */
37
+ static is(error, code) {
38
+ return error instanceof _MinswapError && error.code === code;
39
+ }
40
+ };
41
+
42
+ // src/client/url.ts
43
+ function buildUrl(base, path, query = {}) {
44
+ let url;
45
+ try {
46
+ url = new URL(`${base.replace(/\/+$/, "")}${path}`);
47
+ } catch (cause) {
48
+ throw new MinswapError(
49
+ `Invalid endpoint URL "${base}${path}"`,
50
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
51
+ { param: "url", reason: `could not be parsed: ${String(cause)}` }
52
+ );
53
+ }
54
+ for (const [key, value] of Object.entries(query)) {
55
+ if (value === void 0 || value === null) {
56
+ continue;
57
+ }
58
+ if (Array.isArray(value)) {
59
+ if (value.length === 0) {
60
+ continue;
61
+ }
62
+ url.searchParams.set(key, value.join(","));
63
+ } else {
64
+ url.searchParams.set(key, String(value));
65
+ }
66
+ }
67
+ return url.toString();
68
+ }
69
+ function toEpochMs(value) {
70
+ const ms = value instanceof Date ? value.getTime() : value;
71
+ if (!Number.isFinite(ms)) {
72
+ throw new MinswapError(
73
+ `Invalid time value: expected a finite epoch or a valid Date`,
74
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
75
+ { param: "time", reason: "not a finite epoch or valid Date" }
76
+ );
77
+ }
78
+ return ms;
79
+ }
80
+ function toEpochSeconds(value) {
81
+ return Math.floor(toEpochMs(value) / 1e3);
82
+ }
83
+ function toRfc3339(value) {
84
+ return new Date(toEpochMs(value)).toISOString();
85
+ }
86
+
87
+ // src/client/aggregator-api-client.ts
88
+ var AggregatorApiClient = class {
89
+ constructor(http, config) {
90
+ this.http = http;
91
+ this.config = config;
92
+ }
93
+ http;
94
+ config;
95
+ async get(options) {
96
+ return this.http.request({
97
+ url: buildUrl(this.baseUrl, options.path, options.query),
98
+ method: "GET",
99
+ endpoint: `GET ${options.path}`,
100
+ schema: options.schema,
101
+ notFound: options.notFound
102
+ });
103
+ }
104
+ async post(options) {
105
+ return this.http.request({
106
+ url: buildUrl(this.baseUrl, options.path),
107
+ method: "POST",
108
+ body: options.body,
109
+ endpoint: `POST ${options.path}`,
110
+ schema: options.schema,
111
+ notFound: options.notFound
112
+ });
113
+ }
114
+ get baseUrl() {
115
+ return this.config.endpoints.aggregatorApiUrl;
116
+ }
117
+ };
118
+ var paginationSchema = z.object({
119
+ offset: z.number().nullish(),
120
+ limit: z.number().nullish(),
121
+ // Cursors are strings on newer endpoints and numeric ids on older ones.
122
+ last_cursor: z.union([z.string(), z.number()]).nullish()
123
+ });
124
+ function envelope(data) {
125
+ return z.object({
126
+ code: z.number(),
127
+ message: z.string().nullish(),
128
+ data,
129
+ pagination: paginationSchema.nullish()
130
+ });
131
+ }
132
+ var AppApiClient = class {
133
+ constructor(http, config) {
134
+ this.http = http;
135
+ this.config = config;
136
+ }
137
+ http;
138
+ config;
139
+ async get(options) {
140
+ return this.send(
141
+ "GET",
142
+ options.path,
143
+ options,
144
+ buildUrl(this.baseUrl, options.path, options.query)
145
+ );
146
+ }
147
+ async post(options) {
148
+ return this.send(
149
+ "POST",
150
+ options.path,
151
+ options,
152
+ buildUrl(this.baseUrl, options.path),
153
+ options.body
154
+ );
155
+ }
156
+ get baseUrl() {
157
+ return this.config.endpoints.appApiUrl;
158
+ }
159
+ async send(method, path, options, url, body) {
160
+ const response = await this.http.request({
161
+ url,
162
+ method,
163
+ body,
164
+ endpoint: `${method} ${path}`,
165
+ headers: this.headers(options.currency),
166
+ schema: envelope(options.schema),
167
+ notFound: options.notFound
168
+ });
169
+ const pagination = response.pagination ? {
170
+ offset: response.pagination.offset,
171
+ limit: response.pagination.limit,
172
+ lastCursor: response.pagination.last_cursor === null || response.pagination.last_cursor === void 0 ? null : String(response.pagination.last_cursor)
173
+ } : void 0;
174
+ return { data: response.data, pagination };
175
+ }
176
+ headers(currency) {
177
+ const headers = {
178
+ "x-currency": currency ?? this.config.currency
179
+ };
180
+ if (this.config.apiKey) {
181
+ headers["x-api-key"] = this.config.apiKey;
182
+ }
183
+ return headers;
184
+ }
185
+ };
186
+
187
+ // src/client/http.ts
188
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
189
+ function isRetryable(error) {
190
+ if (error.code === "NETWORK" /* NETWORK */) {
191
+ return true;
192
+ }
193
+ if (error.code === "API_ERROR" /* API_ERROR */) {
194
+ const { status } = error.details;
195
+ return status >= 500;
196
+ }
197
+ return false;
198
+ }
199
+ function toParseIssues(error) {
200
+ return error.issues.map((issue) => ({
201
+ path: issue.path.map(String).join(".") || "<root>",
202
+ message: issue.message
203
+ }));
204
+ }
205
+ function parseOrThrow(schema, value, endpoint) {
206
+ const result = schema.safeParse(value);
207
+ if (result.success) {
208
+ return result.data;
209
+ }
210
+ const issues = toParseIssues(result.error);
211
+ throw new MinswapError(
212
+ `${endpoint} returned an unexpected shape (${issues.length} issue(s)). This usually means the backend changed; please report it.`,
213
+ "PARSE_ERROR" /* PARSE_ERROR */,
214
+ { endpoint, issues }
215
+ );
216
+ }
217
+ function apiError(endpoint, status, body) {
218
+ return new MinswapError(`${endpoint} responded ${status}`, "API_ERROR" /* API_ERROR */, {
219
+ endpoint,
220
+ status,
221
+ body
222
+ });
223
+ }
224
+ function parseRetryAfterMs(header) {
225
+ if (!header) {
226
+ return null;
227
+ }
228
+ const seconds = Number(header);
229
+ if (Number.isFinite(seconds)) {
230
+ return seconds * 1e3;
231
+ }
232
+ const at = Date.parse(header);
233
+ return Number.isNaN(at) ? null : Math.max(0, at - Date.now());
234
+ }
235
+ var HttpCore = class {
236
+ constructor(options) {
237
+ this.options = options;
238
+ }
239
+ options;
240
+ async request(request) {
241
+ const { retries, baseDelayMs } = this.options.retry;
242
+ let lastError;
243
+ for (let attempt = 0; ; attempt++) {
244
+ try {
245
+ return await this.attempt(request);
246
+ } catch (error) {
247
+ if (!(error instanceof MinswapError)) {
248
+ throw error;
249
+ }
250
+ lastError = error;
251
+ if (attempt >= retries || !isRetryable(error)) {
252
+ throw lastError;
253
+ }
254
+ await sleep(baseDelayMs * 2 ** attempt);
255
+ }
256
+ }
257
+ }
258
+ async attempt(request) {
259
+ const { timeoutMs } = this.options;
260
+ const controller = new AbortController();
261
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
262
+ try {
263
+ const response = await this.send(request, controller.signal);
264
+ await this.assertOk(response, request);
265
+ return await this.decode(response, request);
266
+ } catch (cause) {
267
+ if (controller.signal.aborted) {
268
+ throw new MinswapError(
269
+ `Request to ${request.endpoint} timed out after ${timeoutMs}ms`,
270
+ "TIMEOUT" /* TIMEOUT */,
271
+ { endpoint: request.endpoint, timeoutMs }
272
+ );
273
+ }
274
+ throw cause;
275
+ } finally {
276
+ clearTimeout(timer);
277
+ }
278
+ }
279
+ async send(request, signal) {
280
+ const { fetch } = this.options;
281
+ const headers = {
282
+ accept: "application/json",
283
+ ...request.headers
284
+ };
285
+ if (request.body !== void 0) {
286
+ headers["content-type"] = "application/json";
287
+ }
288
+ try {
289
+ return await fetch(request.url, {
290
+ method: request.method,
291
+ headers,
292
+ body: request.body === void 0 ? void 0 : JSON.stringify(request.body),
293
+ signal
294
+ });
295
+ } catch (cause) {
296
+ if (signal.aborted) {
297
+ throw cause;
298
+ }
299
+ throw new MinswapError(
300
+ `Network request to ${request.endpoint} failed`,
301
+ "NETWORK" /* NETWORK */,
302
+ { endpoint: request.endpoint, cause }
303
+ );
304
+ }
305
+ }
306
+ async assertOk(response, request) {
307
+ if (response.ok) {
308
+ return;
309
+ }
310
+ if (response.status === 429) {
311
+ throw new MinswapError(
312
+ `Rate limited by ${new URL(request.url).host}. This host blocks breaching clients for an extended period; back off before retrying.`,
313
+ "RATE_LIMITED" /* RATE_LIMITED */,
314
+ {
315
+ host: new URL(request.url).host,
316
+ retryAfterMs: parseRetryAfterMs(response.headers.get("retry-after"))
317
+ }
318
+ );
319
+ }
320
+ if (request.parseErrorBody) {
321
+ return;
322
+ }
323
+ if (response.status === 404 && request.notFound) {
324
+ const { resource, id } = request.notFound;
325
+ throw new MinswapError(`No ${resource} found for "${id}"`, "NOT_FOUND" /* NOT_FOUND */, {
326
+ resource,
327
+ id
328
+ });
329
+ }
330
+ throw apiError(request.endpoint, response.status, await readBody(response));
331
+ }
332
+ async decode(response, request) {
333
+ const raw = await response.text();
334
+ let json;
335
+ try {
336
+ json = raw.length === 0 ? void 0 : (request.parseJson ?? JSON.parse)(raw);
337
+ } catch {
338
+ if (!response.ok) {
339
+ throw apiError(request.endpoint, response.status, truncate(raw));
340
+ }
341
+ throw new MinswapError(
342
+ `${request.endpoint} returned a body that is not valid JSON`,
343
+ "PARSE_ERROR" /* PARSE_ERROR */,
344
+ {
345
+ endpoint: request.endpoint,
346
+ issues: [
347
+ {
348
+ path: "<root>",
349
+ message: `Expected JSON, received: ${truncate(raw)}`
350
+ }
351
+ ]
352
+ }
353
+ );
354
+ }
355
+ if (!response.ok && json === void 0) {
356
+ throw apiError(request.endpoint, response.status, null);
357
+ }
358
+ return parseOrThrow(request.schema, json, request.endpoint);
359
+ }
360
+ };
361
+ async function readBody(response) {
362
+ try {
363
+ const text = await response.text();
364
+ try {
365
+ return JSON.parse(text);
366
+ } catch {
367
+ return truncate(text);
368
+ }
369
+ } catch {
370
+ return void 0;
371
+ }
372
+ }
373
+ function truncate(value, max = 300) {
374
+ return value.length <= max ? value : `${value.slice(0, max)}\u2026`;
375
+ }
376
+
377
+ // src/client/app-graphql-client.ts
378
+ var graphqlEnvelope = z.object({
379
+ data: z.unknown().nullish(),
380
+ errors: z.array(
381
+ z.object({
382
+ message: z.string(),
383
+ path: z.array(z.union([z.string(), z.number()])).nullish()
384
+ })
385
+ ).nullish()
386
+ });
387
+ var AppGraphqlClient = class {
388
+ constructor(http, config) {
389
+ this.http = http;
390
+ this.config = config;
391
+ }
392
+ http;
393
+ config;
394
+ async execute(operation) {
395
+ const endpoint = `GraphQL ${operation.operation}`;
396
+ const response = await this.http.request({
397
+ url: this.config.endpoints.appGraphqlUrl,
398
+ method: "POST",
399
+ endpoint,
400
+ body: {
401
+ // No `operationName`: each document defines exactly one operation, so
402
+ // the server runs it without one. Sending the field name would have to
403
+ // match the document's operation name exactly — a needless coupling.
404
+ query: operation.document,
405
+ variables: operation.variables ?? {}
406
+ },
407
+ schema: graphqlEnvelope,
408
+ // Resolver errors can come back with a 400/500 status; decode the body
409
+ // anyway so the GraphQL `errors` array is not lost as an opaque API error.
410
+ parseErrorBody: true
411
+ });
412
+ if (response.errors && response.errors.length > 0) {
413
+ throw new MinswapError(
414
+ `${operation.operation} failed: ${response.errors.map((e) => e.message).join("; ")}`,
415
+ "GRAPHQL_ERROR" /* GRAPHQL_ERROR */,
416
+ {
417
+ operation: operation.operation,
418
+ errors: response.errors.map((e) => ({
419
+ message: e.message,
420
+ path: e.path ?? void 0
421
+ }))
422
+ }
423
+ );
424
+ }
425
+ const data = response.data;
426
+ if (data === null || data === void 0) {
427
+ throw new MinswapError(
428
+ `${operation.operation} returned no data and no errors`,
429
+ "PARSE_ERROR" /* PARSE_ERROR */,
430
+ {
431
+ endpoint,
432
+ issues: [{ path: "data", message: "Expected an object, received null" }]
433
+ }
434
+ );
435
+ }
436
+ const root = data;
437
+ const keys = Object.keys(root);
438
+ if (keys.length !== 1) {
439
+ throw new MinswapError(
440
+ `${operation.operation} expected a single root field, got [${keys.join(", ")}]`,
441
+ "PARSE_ERROR" /* PARSE_ERROR */,
442
+ {
443
+ endpoint,
444
+ issues: [
445
+ { path: "data", message: `expected exactly one root field, got ${keys.length}` }
446
+ ]
447
+ }
448
+ );
449
+ }
450
+ return parseOrThrow(operation.schema, root[keys[0]], endpoint);
451
+ }
452
+ };
453
+
454
+ // src/client/config.ts
455
+ var DEFAULT_ENDPOINTS = {
456
+ mainnet: {
457
+ appApiUrl: "https://api-internal.minswap.org",
458
+ aggregatorApiUrl: "https://agg-api.minswap.org",
459
+ keyAppApiUrl: "https://k-app-monorepo-mainnet-prod.minswap.org/graphql",
460
+ appGraphqlUrl: "https://app-monorepo-mainnet-prod.minswap.org/graphql"
461
+ }
462
+ };
463
+ var DEFAULT_TIMEOUT_MS = 3e4;
464
+ var DEFAULT_RETRY = {
465
+ retries: 2,
466
+ baseDelayMs: 300
467
+ };
468
+ function invalid(param, reason) {
469
+ throw new MinswapError(`Invalid config: ${param} ${reason}`, "INVALID_PARAMS" /* INVALID_PARAMS */, {
470
+ param,
471
+ reason
472
+ });
473
+ }
474
+ function resolveConfig(config = {}) {
475
+ const network = config.network ?? "mainnet";
476
+ const fetchImpl = config.fetch ?? globalThis.fetch;
477
+ if (typeof fetchImpl !== "function") {
478
+ invalid(
479
+ "fetch",
480
+ "is not available on globalThis; pass an implementation via `new MinswapSdk({ fetch })`"
481
+ );
482
+ }
483
+ const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
484
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
485
+ invalid("timeoutMs", "must be a positive finite number");
486
+ }
487
+ const retries = config.retry?.retries ?? DEFAULT_RETRY.retries;
488
+ if (!Number.isInteger(retries) || retries < 0) {
489
+ invalid("retry.retries", "must be a non-negative integer");
490
+ }
491
+ const baseDelayMs = config.retry?.baseDelayMs ?? DEFAULT_RETRY.baseDelayMs;
492
+ if (!Number.isFinite(baseDelayMs) || baseDelayMs < 0) {
493
+ invalid("retry.baseDelayMs", "must be a non-negative finite number");
494
+ }
495
+ return {
496
+ network,
497
+ currency: config.currency ?? "USD",
498
+ apiKey: config.apiKey,
499
+ endpoints: {
500
+ ...DEFAULT_ENDPOINTS[network],
501
+ ...config.endpoints
502
+ },
503
+ fetch: fetchImpl,
504
+ timeoutMs,
505
+ retry: { retries, baseDelayMs },
506
+ rpcProvider: config.rpcProvider
507
+ };
508
+ }
509
+ var graphqlEnvelope2 = z.object({
510
+ data: z.unknown().nullish(),
511
+ errors: z.array(
512
+ z.object({
513
+ message: z.string(),
514
+ path: z.array(z.union([z.string(), z.number()])).nullish()
515
+ })
516
+ ).nullish()
517
+ });
518
+ var KeyAppApiClient = class {
519
+ constructor(http, config) {
520
+ this.http = http;
521
+ this.config = config;
522
+ }
523
+ http;
524
+ config;
525
+ async execute(operation) {
526
+ const endpoint = `GraphQL ${operation.operation}`;
527
+ const response = await this.http.request({
528
+ url: this.config.endpoints.keyAppApiUrl,
529
+ method: "POST",
530
+ endpoint,
531
+ body: {
532
+ // No `operationName`: each document defines exactly one operation, so
533
+ // GraphQL runs it without one. Sending the field name here would have
534
+ // to match the document's operation name exactly, which is a needless
535
+ // coupling — `operation.operation` is only a label for errors and the
536
+ // single-field unwrap below.
537
+ query: operation.document,
538
+ variables: operation.variables ?? {}
539
+ },
540
+ schema: graphqlEnvelope2,
541
+ // This host returns resolver errors with a 400/500 status; decode the
542
+ // body anyway so the GraphQL `errors` array is not lost.
543
+ parseErrorBody: true
544
+ });
545
+ if (response.errors && response.errors.length > 0) {
546
+ throw new MinswapError(
547
+ `${operation.operation} failed: ${response.errors.map((e) => e.message).join("; ")}`,
548
+ "GRAPHQL_ERROR" /* GRAPHQL_ERROR */,
549
+ {
550
+ operation: operation.operation,
551
+ errors: response.errors.map((e) => ({
552
+ message: e.message,
553
+ path: e.path ?? void 0
554
+ }))
555
+ }
556
+ );
557
+ }
558
+ const data = response.data;
559
+ if (data === null || data === void 0) {
560
+ throw new MinswapError(
561
+ `${operation.operation} returned no data and no errors`,
562
+ "PARSE_ERROR" /* PARSE_ERROR */,
563
+ {
564
+ endpoint,
565
+ issues: [{ path: "data", message: "Expected an object, received null" }]
566
+ }
567
+ );
568
+ }
569
+ const root = data;
570
+ const keys = Object.keys(root);
571
+ if (keys.length !== 1) {
572
+ throw new MinswapError(
573
+ `${operation.operation} expected a single root field, got [${keys.join(", ")}]`,
574
+ "PARSE_ERROR" /* PARSE_ERROR */,
575
+ {
576
+ endpoint,
577
+ issues: [
578
+ { path: "data", message: `expected exactly one root field, got ${keys.length}` }
579
+ ]
580
+ }
581
+ );
582
+ }
583
+ return parseOrThrow(operation.schema, root[keys[0]], endpoint);
584
+ }
585
+ };
586
+
587
+ // src/client/asset-id.ts
588
+ var POLICY_ID_HEX_LENGTH = 56;
589
+ function coinIdToAssetUnit(coinId) {
590
+ if (coinId === "lovelace") {
591
+ return "lovelace";
592
+ }
593
+ return coinId.replace(".", "");
594
+ }
595
+ function assetUnitToCoinId(unit) {
596
+ if (unit === "lovelace" || unit.length <= POLICY_ID_HEX_LENGTH) {
597
+ return unit;
598
+ }
599
+ return `${unit.slice(0, POLICY_ID_HEX_LENGTH)}.${unit.slice(POLICY_ID_HEX_LENGTH)}`;
600
+ }
601
+ function coinIdToInputAsset(coinId) {
602
+ if (coinId === "lovelace") {
603
+ return { currencySymbol: "", tokenName: "" };
604
+ }
605
+ const dot = coinId.indexOf(".");
606
+ if (dot === -1) {
607
+ return { currencySymbol: coinId, tokenName: "" };
608
+ }
609
+ return {
610
+ currencySymbol: coinId.slice(0, dot),
611
+ tokenName: coinId.slice(dot + 1)
612
+ };
613
+ }
614
+ function inputAssetToCoinId(asset2) {
615
+ if (asset2.currencySymbol === "") {
616
+ return "lovelace";
617
+ }
618
+ return asset2.tokenName === "" ? asset2.currencySymbol : `${asset2.currencySymbol}.${asset2.tokenName}`;
619
+ }
620
+ function arrayOf(schema) {
621
+ return z.array(schema).nullish().transform((value) => value ?? []);
622
+ }
623
+ var zeroableNumber = z.number().nullish().transform((value) => value ?? 0);
624
+ var coinBasicInfoSchema = z.object({
625
+ coin_type: z.string().nullish(),
626
+ symbol: z.string(),
627
+ decimals: z.number(),
628
+ icon_url: z.string(),
629
+ verified: z.boolean()
630
+ }).transform((c) => ({
631
+ coinId: c.coin_type ?? null,
632
+ symbol: c.symbol,
633
+ decimals: c.decimals,
634
+ iconUrl: c.icon_url,
635
+ verified: c.verified
636
+ }));
637
+ var coinAmountInfoSchema = z.object({
638
+ coin_type: z.string().nullish(),
639
+ symbol: z.string(),
640
+ decimals: z.number(),
641
+ icon_url: z.string(),
642
+ verified: z.boolean(),
643
+ amount: z.number(),
644
+ usd_value: z.number()
645
+ }).transform((c) => ({
646
+ coinId: c.coin_type ?? null,
647
+ symbol: c.symbol,
648
+ decimals: c.decimals,
649
+ iconUrl: c.icon_url,
650
+ verified: c.verified,
651
+ amount: c.amount,
652
+ usdValue: c.usd_value
653
+ }));
654
+ var poolCoinSchema = z.object({
655
+ coin_type: z.string().nullish(),
656
+ symbol: z.string(),
657
+ decimals: z.number(),
658
+ icon_url: z.string(),
659
+ verified: z.boolean(),
660
+ category: z.string().nullish()
661
+ }).transform((c) => ({
662
+ coinId: c.coin_type ?? null,
663
+ symbol: c.symbol,
664
+ decimals: c.decimals,
665
+ iconUrl: c.icon_url,
666
+ verified: c.verified,
667
+ /** Free-form; not constrained to the `categoryGroup` filter values. */
668
+ category: c.category ?? null
669
+ }));
670
+ var poolRewardSchema = z.object({
671
+ coin_type: z.string().nullish(),
672
+ symbol: z.string(),
673
+ decimals: z.number(),
674
+ icon_url: z.string(),
675
+ verified: z.boolean(),
676
+ daily_amount: z.number(),
677
+ daily_amount_usd: z.number(),
678
+ apr: z.number()
679
+ }).transform((r) => ({
680
+ coinId: r.coin_type ?? null,
681
+ symbol: r.symbol,
682
+ decimals: r.decimals,
683
+ iconUrl: r.icon_url,
684
+ verified: r.verified,
685
+ dailyAmount: r.daily_amount,
686
+ dailyAmountUsd: r.daily_amount_usd,
687
+ apr: r.apr
688
+ }));
689
+ var poolFeeSchema = z.object({ fee_raw: z.number(), fee_usd: z.number() }).transform((f) => ({ feeRaw: f.fee_raw, feeUsd: f.fee_usd }));
690
+ var coinCandleSchema = z.object({
691
+ t: z.string(),
692
+ ohlcv: z.array(z.number()).nullish()
693
+ }).transform(({ t, ohlcv }) => {
694
+ const [open = 0, high = 0, low = 0, close = 0, volume = 0] = ohlcv ?? [];
695
+ return { timestamp: Date.parse(t), open, high, low, close, volume };
696
+ });
697
+ var poolCandleSchema = z.array(z.number()).transform(([seconds = 0, open = 0, high = 0, low = 0, close = 0, volume = 0]) => ({
698
+ timestamp: seconds * 1e3,
699
+ open,
700
+ high,
701
+ low,
702
+ close,
703
+ volume
704
+ }));
705
+ var VALID_BUCKET_MINUTES = [
706
+ 1,
707
+ 5,
708
+ 15,
709
+ 30,
710
+ 60,
711
+ 120,
712
+ 240,
713
+ 360,
714
+ 720,
715
+ 1440,
716
+ 5760,
717
+ 10080,
718
+ 43200
719
+ ];
720
+ function assertBucket(bucketMinutes) {
721
+ if (!VALID_BUCKET_MINUTES.includes(bucketMinutes)) {
722
+ throw new MinswapError(
723
+ `bucketMinutes must be one of ${VALID_BUCKET_MINUTES.join(", ")}; received ${bucketMinutes}`,
724
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
725
+ { param: "bucketMinutes", reason: "unsupported bucket size" }
726
+ );
727
+ }
728
+ }
729
+
730
+ // src/modules/aggregator-module.ts
731
+ var AGGREGATOR_PROTOCOLS = [
732
+ "MinswapV2",
733
+ "Minswap",
734
+ "MinswapStable",
735
+ "Splash",
736
+ "Spectrum",
737
+ "SplashStable",
738
+ "SundaeSwapV3",
739
+ "SundaeSwap",
740
+ "SundaeSwapStable",
741
+ "WingRidersV2",
742
+ "WingRiders",
743
+ "WingRidersStableV1",
744
+ "WingRidersStableV2",
745
+ "OpenDjedV1",
746
+ "ChakraBondingCurve",
747
+ "CswapV1",
748
+ "VyFinance",
749
+ "MuesliSwap",
750
+ "DanogoCLMMV1"
751
+ ];
752
+ var CANCEL_ADAPTER_PROTOCOL = "MINSWAP_ADAPTER";
753
+ var assetSchema = z.object({
754
+ token_id: z.string(),
755
+ logo: z.string().nullish(),
756
+ ticker: z.string().nullish(),
757
+ is_verified: z.boolean().nullish(),
758
+ price_by_ada: z.number().nullish(),
759
+ price_by_usd: z.number().nullish(),
760
+ project_name: z.string().nullish(),
761
+ decimals: z.number().nullish()
762
+ }).transform((a) => ({
763
+ coinId: assetUnitToCoinId(a.token_id),
764
+ logo: a.logo ?? null,
765
+ ticker: a.ticker ?? null,
766
+ isVerified: a.is_verified ?? null,
767
+ priceByAda: a.price_by_ada ?? null,
768
+ priceByUsd: a.price_by_usd ?? null,
769
+ projectName: a.project_name ?? null,
770
+ decimals: a.decimals ?? null
771
+ }));
772
+ var pathAtomicSchema = z.object({
773
+ pool_id: z.string(),
774
+ protocol: z.string(),
775
+ lp_token: z.string(),
776
+ token_in: z.string(),
777
+ token_out: z.string(),
778
+ amount_in: z.string(),
779
+ amount_out: z.string(),
780
+ min_amount_out: z.string(),
781
+ lp_fee: z.string(),
782
+ dex_fee: z.string(),
783
+ deposits: z.string(),
784
+ price_impact: z.number(),
785
+ pool_out_ref: z.string().nullish()
786
+ }).transform((p) => ({
787
+ poolId: p.pool_id,
788
+ protocol: p.protocol,
789
+ lpToken: p.lp_token,
790
+ tokenIn: assetUnitToCoinId(p.token_in),
791
+ tokenOut: assetUnitToCoinId(p.token_out),
792
+ amountIn: p.amount_in,
793
+ amountOut: p.amount_out,
794
+ minAmountOut: p.min_amount_out,
795
+ lpFee: p.lp_fee,
796
+ dexFee: p.dex_fee,
797
+ deposits: p.deposits,
798
+ priceImpact: p.price_impact,
799
+ poolOutRef: p.pool_out_ref ?? null
800
+ }));
801
+ var pathSchema = z.object({
802
+ pool_id: z.string(),
803
+ protocol: z.string(),
804
+ lp_token: z.string(),
805
+ token_in: z.string(),
806
+ token_out: z.string(),
807
+ amount_in: z.string(),
808
+ amount_out: z.string(),
809
+ min_amount_out: z.string(),
810
+ lp_fee: z.string(),
811
+ dex_fee: z.string(),
812
+ deposits: z.string(),
813
+ price_impact: z.number(),
814
+ pool_out_ref: z.string().nullish(),
815
+ callback_paths: z.array(z.array(pathAtomicSchema)).nullish()
816
+ }).transform((p) => ({
817
+ poolId: p.pool_id,
818
+ protocol: p.protocol,
819
+ lpToken: p.lp_token,
820
+ tokenIn: assetUnitToCoinId(p.token_in),
821
+ tokenOut: assetUnitToCoinId(p.token_out),
822
+ amountIn: p.amount_in,
823
+ amountOut: p.amount_out,
824
+ minAmountOut: p.min_amount_out,
825
+ lpFee: p.lp_fee,
826
+ dexFee: p.dex_fee,
827
+ deposits: p.deposits,
828
+ priceImpact: p.price_impact,
829
+ poolOutRef: p.pool_out_ref ?? null,
830
+ callbackPaths: p.callback_paths ?? []
831
+ }));
832
+ var estimateSchema = z.object({
833
+ token_in: z.string(),
834
+ token_out: z.string(),
835
+ amount_in: z.string(),
836
+ amount_out: z.string(),
837
+ min_amount_out: z.string(),
838
+ total_lp_fee: z.string(),
839
+ total_dex_fee: z.string(),
840
+ deposits: z.string(),
841
+ avg_price_impact: z.number(),
842
+ paths: z.array(z.array(pathSchema)),
843
+ aggregator_fee: z.string().nullish(),
844
+ aggregator_fee_percent: z.number().nullish(),
845
+ tokens: z.record(z.string(), assetSchema).nullish(),
846
+ amount_in_decimal: z.boolean().nullish()
847
+ }).transform((e) => ({
848
+ tokenIn: assetUnitToCoinId(e.token_in),
849
+ tokenOut: assetUnitToCoinId(e.token_out),
850
+ amountIn: e.amount_in,
851
+ amountOut: e.amount_out,
852
+ /** Worst acceptable output after slippage — pass to {@link AggregatorModule.buildTx}. */
853
+ minAmountOut: e.min_amount_out,
854
+ totalLpFee: e.total_lp_fee,
855
+ totalDexFee: e.total_dex_fee,
856
+ deposits: e.deposits,
857
+ avgPriceImpact: e.avg_price_impact,
858
+ /**
859
+ * Split routes: the outer array is parallel splits of the input, the inner
860
+ * array is the sequential hops of one split.
861
+ */
862
+ paths: e.paths,
863
+ aggregatorFee: e.aggregator_fee ?? null,
864
+ aggregatorFeePercent: e.aggregator_fee_percent ?? null,
865
+ /** Metadata for every token mentioned, keyed by `coinId`. */
866
+ tokens: e.tokens ? Object.fromEntries(
867
+ Object.entries(e.tokens).map(([unit, asset2]) => [assetUnitToCoinId(unit), asset2])
868
+ ) : {},
869
+ amountInDecimal: e.amount_in_decimal ?? false
870
+ }));
871
+ var buildTxResponseSchema = z.object({ cbor: z.string() }).transform((r) => ({ cbor: r.cbor }));
872
+ var submitResponseSchema = z.object({ tx_id: z.string() }).transform((r) => ({ txId: r.tx_id }));
873
+ var pendingOrderSchema = z.object({
874
+ owner_address: z.string(),
875
+ protocol: z.string(),
876
+ token_in: assetSchema,
877
+ token_out: assetSchema,
878
+ amount_in: z.string(),
879
+ min_amount_out: z.string(),
880
+ created_at: z.number(),
881
+ tx_in: z.string(),
882
+ dex_fee: z.string(),
883
+ deposit: z.string()
884
+ }).transform((o) => ({
885
+ ownerAddress: o.owner_address,
886
+ protocol: o.protocol,
887
+ tokenIn: o.token_in,
888
+ tokenOut: o.token_out,
889
+ amountIn: o.amount_in,
890
+ minAmountOut: o.min_amount_out,
891
+ /** Epoch seconds. */
892
+ createdAt: o.created_at,
893
+ /** The order's UTxO reference, `txHash#index` — pass to {@link AggregatorModule.cancelOrders}. */
894
+ txIn: o.tx_in,
895
+ dexFee: o.dex_fee,
896
+ deposit: o.deposit
897
+ }));
898
+ var pendingOrdersSchema = z.object({
899
+ orders: arrayOf(pendingOrderSchema),
900
+ amount_in_decimal: z.boolean()
901
+ }).transform((r) => r.orders);
902
+ function estimateBody(params) {
903
+ return dropUndefined({
904
+ amount: params.amount,
905
+ token_in: coinIdToAssetUnit(params.tokenIn),
906
+ token_out: coinIdToAssetUnit(params.tokenOut),
907
+ slippage: params.slippage,
908
+ include_protocols: params.includeProtocols,
909
+ exclude_protocols: params.excludeProtocols,
910
+ allow_multi_hops: params.allowMultiHops,
911
+ allow_non_atomic_multi_hops: params.allowNonAtomicMultiHops,
912
+ partner: params.partner,
913
+ amount_in_decimal: params.amountInDecimal
914
+ });
915
+ }
916
+ var AggregatorModule = class {
917
+ constructor(sdk) {
918
+ this.sdk = sdk;
919
+ }
920
+ sdk;
921
+ /**
922
+ * Quote a swap: the best route, expected output, fees, and price impact.
923
+ *
924
+ * @example
925
+ * const quote = await sdk.aggregator.estimate({
926
+ * amount: "1000000",
927
+ * tokenIn: "lovelace",
928
+ * tokenOut: "29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c6.4d494e",
929
+ * slippage: 0.5,
930
+ * });
931
+ */
932
+ async estimate(params) {
933
+ return this.sdk.clients.aggregatorApi.post({
934
+ path: "/aggregator/estimate",
935
+ body: estimateBody(params),
936
+ schema: estimateSchema
937
+ });
938
+ }
939
+ /**
940
+ * Build the unsigned swap transaction for a quote.
941
+ *
942
+ * @example
943
+ * const quote = await sdk.aggregator.estimate(params);
944
+ * const { cbor } = await sdk.aggregator.buildTx({
945
+ * sender,
946
+ * estimate: params,
947
+ * minAmountOut: quote.minAmountOut,
948
+ * });
949
+ */
950
+ async buildTx(params) {
951
+ return this.sdk.clients.aggregatorApi.post({
952
+ path: "/aggregator/build-tx",
953
+ body: dropUndefined({
954
+ sender: params.sender,
955
+ min_amount_out: params.minAmountOut,
956
+ estimate: estimateBody(params.estimate),
957
+ amount_in_decimal: params.estimate.amountInDecimal,
958
+ extra_output: params.extraOutput,
959
+ inputs_to_choose: params.inputsToChoose
960
+ }),
961
+ schema: buildTxResponseSchema
962
+ });
963
+ }
964
+ /**
965
+ * Assemble a signed witness onto a built transaction and submit it.
966
+ *
967
+ * @param witnessSet CBOR of the witness set produced by signing the tx.
968
+ */
969
+ async submitTx(params) {
970
+ return this.sdk.clients.aggregatorApi.post({
971
+ path: "/aggregator/finalize-and-submit-tx",
972
+ body: { cbor: params.cbor, witness_set: params.witnessSet },
973
+ schema: submitResponseSchema
974
+ });
975
+ }
976
+ /**
977
+ * Build a transaction that cancels one or more open orders.
978
+ *
979
+ * The returned CBOR is **partially signed** by the server. Between 1 and 6
980
+ * orders per call, the server's own limit.
981
+ */
982
+ async cancelOrders(params) {
983
+ if (params.orders.length < 1 || params.orders.length > 6) {
984
+ throw new MinswapError(
985
+ `cancelOrders takes between 1 and 6 orders; received ${params.orders.length}`,
986
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
987
+ { param: "orders", reason: "must contain 1 to 6 entries" }
988
+ );
989
+ }
990
+ return this.sdk.clients.aggregatorApi.post({
991
+ path: "/aggregator/cancel-tx",
992
+ body: {
993
+ sender: params.sender,
994
+ orders: params.orders.map((o) => ({
995
+ tx_in: o.txIn,
996
+ protocol: o.protocol
997
+ }))
998
+ },
999
+ schema: buildTxResponseSchema
1000
+ });
1001
+ }
1002
+ /**
1003
+ * List an address's still-open aggregator orders.
1004
+ *
1005
+ * Only orders whose UTxO is still unspent are returned, so this is genuinely
1006
+ * "open orders" rather than history. For full history including filled and
1007
+ * cancelled orders, use {@link OrderModule.getHistory}.
1008
+ */
1009
+ async getPendingOrders(ownerAddress, options = {}) {
1010
+ return this.sdk.clients.aggregatorApi.get({
1011
+ path: "/aggregator/pending-orders",
1012
+ query: {
1013
+ owner_address: ownerAddress,
1014
+ amount_in_decimal: options.amountInDecimal
1015
+ },
1016
+ schema: pendingOrdersSchema
1017
+ });
1018
+ }
1019
+ };
1020
+ function dropUndefined(obj) {
1021
+ for (const key of Object.keys(obj)) {
1022
+ if (obj[key] === void 0) {
1023
+ delete obj[key];
1024
+ }
1025
+ }
1026
+ return obj;
1027
+ }
1028
+
1029
+ // src/client/pagination.ts
1030
+ function assertLimit(limit, max) {
1031
+ if (limit === void 0) {
1032
+ return;
1033
+ }
1034
+ if (!Number.isInteger(limit) || limit <= 0 || limit > max) {
1035
+ throw new MinswapError(
1036
+ `limit must be an integer between 1 and ${max}, received ${limit}`,
1037
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
1038
+ { param: "limit", reason: `must be an integer in 1..${max}` }
1039
+ );
1040
+ }
1041
+ }
1042
+ function encodeOffsetCursor(offset) {
1043
+ return String(offset);
1044
+ }
1045
+ function decodeOffsetCursor(cursor) {
1046
+ if (cursor === void 0) {
1047
+ return 0;
1048
+ }
1049
+ const offset = Number(cursor);
1050
+ if (!Number.isInteger(offset) || offset < 0) {
1051
+ throw new MinswapError(`Malformed cursor "${cursor}"`, "INVALID_PARAMS" /* INVALID_PARAMS */, {
1052
+ param: "cursor",
1053
+ reason: "must be a cursor returned by a previous call"
1054
+ });
1055
+ }
1056
+ return offset;
1057
+ }
1058
+ function offsetPage(items, offset, limit) {
1059
+ const hasMore = items.length === limit;
1060
+ return {
1061
+ items,
1062
+ hasMore,
1063
+ nextCursor: hasMore ? encodeOffsetCursor(offset + limit) : null
1064
+ };
1065
+ }
1066
+ function cursorPage(items, lastCursor) {
1067
+ const nextCursor = lastCursor ?? null;
1068
+ return {
1069
+ items,
1070
+ nextCursor,
1071
+ hasMore: nextCursor !== null && items.length > 0
1072
+ };
1073
+ }
1074
+ async function* paginate(fetchPage, params = {}) {
1075
+ let cursor = params.cursor;
1076
+ const seen = /* @__PURE__ */ new Set();
1077
+ for (; ; ) {
1078
+ const page = await fetchPage({ ...params, cursor });
1079
+ yield* page.items;
1080
+ if (!page.hasMore || page.nextCursor === null) {
1081
+ return;
1082
+ }
1083
+ if (seen.has(page.nextCursor)) {
1084
+ throw new MinswapError(
1085
+ `Pagination stalled: cursor "${page.nextCursor}" was returned twice`,
1086
+ "API_ERROR" /* API_ERROR */,
1087
+ {
1088
+ endpoint: "<paginate>",
1089
+ status: 200,
1090
+ body: { repeatedCursor: page.nextCursor }
1091
+ }
1092
+ );
1093
+ }
1094
+ seen.add(page.nextCursor);
1095
+ cursor = page.nextCursor;
1096
+ }
1097
+ }
1098
+ async function collect(fetchPage, options = {}) {
1099
+ const { maxItems = 1e3, ...params } = options;
1100
+ const items = [];
1101
+ for await (const item of paginate(fetchPage, params)) {
1102
+ items.push(item);
1103
+ if (items.length >= maxItems) {
1104
+ break;
1105
+ }
1106
+ }
1107
+ return items;
1108
+ }
1109
+
1110
+ // src/client/rpc-provider.ts
1111
+ function parseTxIn(ref) {
1112
+ const parts = ref.split("#");
1113
+ const [txHash, rawIndex] = parts;
1114
+ if (parts.length !== 2 || !txHash || !/^\d+$/.test(rawIndex)) {
1115
+ throw new MinswapError(
1116
+ `Malformed output reference "${ref}", expected "txHash#index"`,
1117
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
1118
+ { param: "ref", reason: 'must look like "txHash#index"' }
1119
+ );
1120
+ }
1121
+ return { txHash, outputIndex: Number(rawIndex) };
1122
+ }
1123
+ function formatTxIn(ref) {
1124
+ return `${ref.txHash}#${ref.outputIndex}`;
1125
+ }
1126
+ function requireRpcProvider(provider, action) {
1127
+ if (!provider) {
1128
+ throw new MinswapError(
1129
+ `${action} needs chain access. Pass an rpcProvider when constructing MinswapSdk: new MinswapSdk({ rpcProvider })`,
1130
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
1131
+ { param: "rpcProvider", reason: `is required by ${action}` }
1132
+ );
1133
+ }
1134
+ return provider;
1135
+ }
1136
+ function lovelaceOf(utxo) {
1137
+ return utxo.assets["lovelace"] ?? 0n;
1138
+ }
1139
+ function isPureAda(utxo) {
1140
+ return Object.keys(utxo.assets).every((unit) => unit === "lovelace");
1141
+ }
1142
+ function selectCollateral(utxos, { minLovelace = 5000000n, count = 1 } = {}) {
1143
+ return utxos.filter((utxo) => isPureAda(utxo) && lovelaceOf(utxo) >= minLovelace).sort((a, b) => {
1144
+ const delta = lovelaceOf(a) - lovelaceOf(b);
1145
+ return delta < 0n ? -1 : delta > 0n ? 1 : 0;
1146
+ }).slice(0, count);
1147
+ }
1148
+
1149
+ // src/modules/wallet-inputs.ts
1150
+ async function resolveWalletInputs(sdk, action, params, needCollateral) {
1151
+ const haveInputs = params.inputsToChoose !== void 0;
1152
+ const haveCollateral = params.collateralUtxos !== void 0 || !needCollateral;
1153
+ if (haveInputs && haveCollateral) {
1154
+ return {
1155
+ inputsToChoose: params.inputsToChoose ?? [],
1156
+ collateralUtxos: params.collateralUtxos ?? []
1157
+ };
1158
+ }
1159
+ const utxos = await requireRpcProvider(sdk.rpcProvider, action).getUtxosByAddress(params.address);
1160
+ let collateralUtxos = [];
1161
+ if (needCollateral) {
1162
+ if (params.collateralUtxos !== void 0) {
1163
+ collateralUtxos = params.collateralUtxos;
1164
+ } else {
1165
+ const selected = selectCollateral(utxos);
1166
+ if (selected.length === 0) {
1167
+ throw new MinswapError(
1168
+ `${action} needs collateral, but no pure-ADA UTxO large enough was found at ${params.address}`,
1169
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
1170
+ {
1171
+ param: "collateralUtxos",
1172
+ reason: "no adequate pure-ADA UTxO; supply collateralUtxos explicitly"
1173
+ }
1174
+ );
1175
+ }
1176
+ collateralUtxos = selected.map((u) => u.cbor);
1177
+ }
1178
+ }
1179
+ return {
1180
+ inputsToChoose: params.inputsToChoose ?? utxos.map((u) => u.cbor),
1181
+ collateralUtxos
1182
+ };
1183
+ }
1184
+ function toBigIntString(value) {
1185
+ return typeof value === "bigint" ? value.toString() : BigInt(value).toString();
1186
+ }
1187
+
1188
+ // src/modules/farm-module.ts
1189
+ var FARM_PROTOCOLS = [
1190
+ "minswap-cpmm-v1",
1191
+ "minswap-cpmm-v2",
1192
+ "minswap-stable-cpmm-v1"
1193
+ ];
1194
+ var FARM_LIST_SORT_FIELDS = [
1195
+ "liquidity_locked_usd",
1196
+ "fee_apr_24h",
1197
+ "fee_apr_1w",
1198
+ "reward_apr",
1199
+ "total_apr_24h",
1200
+ "total_apr_1w",
1201
+ "point"
1202
+ ];
1203
+ var farmSchema = z.object({
1204
+ pool_id: z.string().nullish(),
1205
+ protocol: z.string(),
1206
+ coin_a: poolCoinSchema,
1207
+ coin_b: poolCoinSchema,
1208
+ amount_a_locked: z.number(),
1209
+ amount_b_locked: z.number(),
1210
+ amount_a_locked_usd: z.number(),
1211
+ amount_b_locked_usd: z.number(),
1212
+ price_a: z.number(),
1213
+ price_b: z.number(),
1214
+ liquidity_locked: z.string().nullish(),
1215
+ liquidity_locked_usd: z.number(),
1216
+ fee_apr_24h: z.number(),
1217
+ fee_apr_1w: z.number(),
1218
+ rewards: arrayOf(poolRewardSchema),
1219
+ active: z.boolean(),
1220
+ pool_type: z.string(),
1221
+ point: z.number(),
1222
+ is_exclusive: z.boolean(),
1223
+ nft_id_allowed: z.string().nullish()
1224
+ }).transform((f) => ({
1225
+ /** The farm's LP token id. This is the `lpAsset` the action methods need. */
1226
+ poolId: f.pool_id ?? null,
1227
+ protocol: f.protocol,
1228
+ coinA: f.coin_a,
1229
+ coinB: f.coin_b,
1230
+ amountALocked: f.amount_a_locked,
1231
+ amountBLocked: f.amount_b_locked,
1232
+ amountALockedUsd: f.amount_a_locked_usd,
1233
+ amountBLockedUsd: f.amount_b_locked_usd,
1234
+ priceA: f.price_a,
1235
+ priceB: f.price_b,
1236
+ /** Total staked liquidity, a raw decimal string. */
1237
+ liquidityLocked: f.liquidity_locked ?? null,
1238
+ liquidityLockedUsd: f.liquidity_locked_usd,
1239
+ feeApr24h: f.fee_apr_24h,
1240
+ feeApr1w: f.fee_apr_1w,
1241
+ rewards: f.rewards,
1242
+ active: f.active,
1243
+ poolType: f.pool_type,
1244
+ point: f.point,
1245
+ /** Whether this is a Launch Bowl (exclusive) farm — the `hasLBBonus` flag. */
1246
+ isExclusive: f.is_exclusive,
1247
+ nftIdAllowed: f.nft_id_allowed ?? null
1248
+ }));
1249
+ var cborResultSchema = z.string();
1250
+ function inputAssets(coinIds) {
1251
+ return coinIds?.map(coinIdToInputAsset);
1252
+ }
1253
+ var FarmModule = class {
1254
+ constructor(sdk) {
1255
+ this.sdk = sdk;
1256
+ }
1257
+ sdk;
1258
+ /** List farms with filtering and sorting. */
1259
+ async list(params = {}) {
1260
+ assertLimit(params.limit, 50);
1261
+ const limit = params.limit ?? 20;
1262
+ const offset = decodeOffsetCursor(params.cursor);
1263
+ if (params.coinIds && params.coinIds.length > 2) {
1264
+ throw new MinswapError(
1265
+ "coinIds accepts at most 2 entries \u2014 it filters for farms containing those coins.",
1266
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
1267
+ { param: "coinIds", reason: "at most 2 entries" }
1268
+ );
1269
+ }
1270
+ const { data } = await this.sdk.clients.appApi.get({
1271
+ path: "/api/v1/pool/yield-pools",
1272
+ query: {
1273
+ offset,
1274
+ limit,
1275
+ pool_addresses: params.poolIds,
1276
+ protocols: params.protocols,
1277
+ coin_ids: params.coinIds,
1278
+ exclusive_only: params.exclusiveOnly,
1279
+ active: params.active,
1280
+ keyword: params.search,
1281
+ sort_field: params.sortBy ?? "liquidity_locked_usd",
1282
+ sort_direction: params.order ?? "desc"
1283
+ },
1284
+ schema: arrayOf(farmSchema),
1285
+ currency: params.currency
1286
+ });
1287
+ return offsetPage(data, offset, limit);
1288
+ }
1289
+ /** An address's farm positions. Sourced from the DeFi portfolio. */
1290
+ async getPositions(address) {
1291
+ const { farms } = await this.sdk.portfolio.getDefi(address);
1292
+ return farms;
1293
+ }
1294
+ /**
1295
+ * Stake LP tokens into a farm.
1296
+ *
1297
+ * Routes to a first deposit when the address holds no position in the farm,
1298
+ * or adds to the existing stake otherwise. The distinction is resolved from
1299
+ * the address's positions unless {@link FarmDepositParams.position} is given.
1300
+ */
1301
+ async deposit(params) {
1302
+ const position = params.position !== void 0 ? params.position : await this.findPosition(params.address, params.lpAsset);
1303
+ const amount = toBigIntString(params.amount);
1304
+ if (!position) {
1305
+ const inputs2 = await resolveWalletInputs(this.sdk, "farm.deposit", params, false);
1306
+ const cbor2 = await this.sdk.clients.keyAppApi.execute({
1307
+ operation: "buildFirstDepositV2",
1308
+ document: DOC_FIRST_DEPOSIT,
1309
+ variables: {
1310
+ options: {
1311
+ farmTx: this.farmTx(params, inputs2.inputsToChoose),
1312
+ amount,
1313
+ lbWhitelistAssets: inputAssets(params.lbWhitelistAssets)
1314
+ }
1315
+ },
1316
+ schema: cborResultSchema
1317
+ });
1318
+ return { cbor: cbor2 };
1319
+ }
1320
+ const inputs = await resolveWalletInputs(this.sdk, "farm.deposit", params, true);
1321
+ const cbor = await this.sdk.clients.keyAppApi.execute({
1322
+ operation: "buildStakeDepositV2",
1323
+ document: DOC_STAKE_DEPOSIT,
1324
+ variables: {
1325
+ options: {
1326
+ farmTx: this.farmTx(params, inputs.inputsToChoose),
1327
+ amount,
1328
+ collateralUtxos: inputs.collateralUtxos,
1329
+ additionalLbWhitelistAssets: inputAssets(params.lbWhitelistAssets)
1330
+ }
1331
+ },
1332
+ schema: cborResultSchema
1333
+ });
1334
+ return { cbor };
1335
+ }
1336
+ /**
1337
+ * Withdraw staked LP tokens.
1338
+ *
1339
+ * Routes to withdraw-all when the amount is the entire stake, or a partial
1340
+ * withdrawal otherwise, comparing against the position's staked amount.
1341
+ */
1342
+ async withdraw(params) {
1343
+ const position = params.position !== void 0 ? params.position : await this.findPosition(params.address, params.lpAsset);
1344
+ if (!position) {
1345
+ throw new MinswapError(
1346
+ `No farm position found for ${params.lpAsset} at this address`,
1347
+ "NOT_FOUND" /* NOT_FOUND */,
1348
+ { resource: "farm", id: params.lpAsset }
1349
+ );
1350
+ }
1351
+ const inputs = await resolveWalletInputs(this.sdk, "farm.withdraw", params, true);
1352
+ const amount = BigInt(toBigIntString(params.amount));
1353
+ const staked = stakedLpAmount(position);
1354
+ if (amount >= staked) {
1355
+ const cbor2 = await this.sdk.clients.keyAppApi.execute({
1356
+ operation: "buildStakeWithdrawAllV2",
1357
+ document: DOC_WITHDRAW_ALL,
1358
+ variables: {
1359
+ options: {
1360
+ farmTx: this.farmTx(params, inputs.inputsToChoose),
1361
+ collateralUtxos: inputs.collateralUtxos
1362
+ }
1363
+ },
1364
+ schema: cborResultSchema
1365
+ });
1366
+ return { cbor: cbor2 };
1367
+ }
1368
+ const cbor = await this.sdk.clients.keyAppApi.execute({
1369
+ operation: "buildStakeWithdrawV2",
1370
+ document: DOC_WITHDRAW,
1371
+ variables: {
1372
+ options: {
1373
+ farmTx: this.farmTx(params, inputs.inputsToChoose),
1374
+ amount: amount.toString(),
1375
+ collateralUtxos: inputs.collateralUtxos
1376
+ }
1377
+ },
1378
+ schema: cborResultSchema
1379
+ });
1380
+ return { cbor };
1381
+ }
1382
+ /** Harvest pending rewards from one or more farms in a single transaction. */
1383
+ async harvest(params) {
1384
+ if (params.farms.length === 0) {
1385
+ throw new MinswapError("harvest needs at least one farm", "INVALID_PARAMS" /* INVALID_PARAMS */, {
1386
+ param: "farms",
1387
+ reason: "must not be empty"
1388
+ });
1389
+ }
1390
+ const inputs = await resolveWalletInputs(this.sdk, "farm.harvest", params, true);
1391
+ const cbor = await this.sdk.clients.keyAppApi.execute({
1392
+ operation: "buildMultipleHarvestsV2",
1393
+ document: DOC_MULTIPLE_HARVESTS,
1394
+ variables: {
1395
+ options: {
1396
+ owner: params.address,
1397
+ pools: params.farms.map((f) => ({
1398
+ lpAsset: f.lpAsset,
1399
+ hasLBBonus: f.hasLBBonus
1400
+ })),
1401
+ inputsToChoose: inputs.inputsToChoose,
1402
+ useCoinSelectionStrategy: true,
1403
+ collateralUtxos: inputs.collateralUtxos,
1404
+ shouldSplitChange: params.shouldSplitChange
1405
+ }
1406
+ },
1407
+ schema: cborResultSchema
1408
+ });
1409
+ return { cbor };
1410
+ }
1411
+ /**
1412
+ * Force-withdraw a stake, forfeiting pending rewards.
1413
+ *
1414
+ * A safety valve for when a normal withdrawal cannot proceed. Prefer
1415
+ * {@link FarmModule.withdraw}.
1416
+ */
1417
+ async emergencyWithdraw(params) {
1418
+ const inputs = await resolveWalletInputs(this.sdk, "farm.emergencyWithdraw", params, true);
1419
+ const cbor = await this.sdk.clients.keyAppApi.execute({
1420
+ operation: "buildEmergencyWithdrawV2",
1421
+ document: DOC_EMERGENCY_WITHDRAW,
1422
+ variables: {
1423
+ options: {
1424
+ farmTx: this.farmTx(params, inputs.inputsToChoose),
1425
+ collateralUtxos: inputs.collateralUtxos
1426
+ }
1427
+ },
1428
+ schema: cborResultSchema
1429
+ });
1430
+ return { cbor };
1431
+ }
1432
+ farmTx(params, inputsToChoose) {
1433
+ return {
1434
+ owner: params.address,
1435
+ lpAsset: params.lpAsset,
1436
+ hasLBBonus: params.hasLBBonus,
1437
+ inputsToChoose,
1438
+ useCoinSelectionStrategy: true,
1439
+ shouldSplitChange: params.shouldSplitChange
1440
+ };
1441
+ }
1442
+ async findPosition(address, lpAsset) {
1443
+ const positions = await this.getPositions(address);
1444
+ return positions.find((p) => p.poolId === lpAsset) ?? null;
1445
+ }
1446
+ };
1447
+ function stakedLpAmount(position) {
1448
+ if (position.lpPosition?.lpAmount) {
1449
+ return BigInt(position.lpPosition.lpAmount);
1450
+ }
1451
+ throw new MinswapError(
1452
+ `Farm position ${position.poolId} has no staked LP amount to withdraw against`,
1453
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
1454
+ { param: "position", reason: "position carries no lpPosition.lpAmount" }
1455
+ );
1456
+ }
1457
+ var DOC_FIRST_DEPOSIT = `mutation BuildFirstDepositV2($options: BuildFirstDepositTxOptions!) {
1458
+ buildFirstDepositV2(options: $options)
1459
+ }`;
1460
+ var DOC_STAKE_DEPOSIT = `mutation BuildStakeDepositV2($options: BuildStakeDepositOptions!) {
1461
+ buildStakeDepositV2(options: $options)
1462
+ }`;
1463
+ var DOC_WITHDRAW = `mutation BuildStakeWithdrawV2($options: BuildStakeWithdrawOptions!) {
1464
+ buildStakeWithdrawV2(options: $options)
1465
+ }`;
1466
+ var DOC_WITHDRAW_ALL = `mutation BuildStakeWithdrawAllV2($options: BuildStakeWithdrawAllOptions!) {
1467
+ buildStakeWithdrawAllV2(options: $options)
1468
+ }`;
1469
+ var DOC_MULTIPLE_HARVESTS = `mutation BuildMultipleHarvestsV2($options: BuildMultipleHarvestsOptions!) {
1470
+ buildMultipleHarvestsV2(options: $options)
1471
+ }`;
1472
+ var DOC_EMERGENCY_WITHDRAW = `mutation BuildEmergencyWithdrawV2($options: BuildEmergencyWithdrawOptions!) {
1473
+ buildEmergencyWithdrawV2(options: $options)
1474
+ }`;
1475
+
1476
+ // src/liquidity/config.ts
1477
+ var MAINNET_NETWORK_ENV = 764824073;
1478
+ var BATCHER_FEE = 2000000n;
1479
+ var DEPOSIT_ADA = 2000000n;
1480
+ var DEX_V2 = {
1481
+ lpPolicyId: "f5808c2c990d86da54bfc97d89cee6efa20cd8461616359478d96b4c",
1482
+ orderScriptHash: "c3e28c36c3447315ba5a56f33da6a6ddc1770a876a8d9f0cb3a97c4c",
1483
+ tradingFeeDenominator: 10000n
1484
+ };
1485
+ var DEX_V1 = {
1486
+ lpPolicyId: "e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86",
1487
+ orderScriptHash: "a65ca58a4e9c755fa830173d2a5caed458ac0c73f97db7faae2e7e3b"
1488
+ };
1489
+
1490
+ // src/liquidity/plutus.ts
1491
+ function constr(index, fields) {
1492
+ return { constructor: index, fields };
1493
+ }
1494
+ function integer(value) {
1495
+ return { int: BigInt(value).toString() };
1496
+ }
1497
+ function bytes(hex) {
1498
+ return { bytes: hex };
1499
+ }
1500
+
1501
+ // src/liquidity/datum/dex-v2.ts
1502
+ function killable(killOnFailed) {
1503
+ return constr(0, []);
1504
+ }
1505
+ function direction(dir) {
1506
+ return constr(dir === "A_TO_B" ? 1 : 0, []);
1507
+ }
1508
+ function depositStep(params) {
1509
+ return constr(4, [
1510
+ constr(0, [integer(params.depositAmountA), integer(params.depositAmountB)]),
1511
+ integer(params.minimumLP),
1512
+ killable()
1513
+ ]);
1514
+ }
1515
+ function withdrawStep(params) {
1516
+ return constr(5, [
1517
+ constr(0, [integer(params.withdrawalLPAmount)]),
1518
+ integer(params.minimumAssetA),
1519
+ integer(params.minimumAssetB),
1520
+ killable()
1521
+ ]);
1522
+ }
1523
+ function zapOutStep(params) {
1524
+ return constr(6, [
1525
+ direction(params.direction),
1526
+ constr(0, [integer(params.withdrawalLPAmount)]),
1527
+ integer(params.minimumReceived),
1528
+ killable()
1529
+ ]);
1530
+ }
1531
+ function credential(c) {
1532
+ return constr(c.isScript ? 1 : 0, [bytes(c.hash)]);
1533
+ }
1534
+ function addressData(payment, stake) {
1535
+ const stakeMaybe = stake ? constr(0, [constr(0, [credential(stake)])]) : constr(1, []);
1536
+ return constr(0, [credential(payment), stakeMaybe]);
1537
+ }
1538
+ function orderDatum(params) {
1539
+ const sender = addressData(
1540
+ { hash: params.paymentKeyHash, isScript: params.paymentIsScript },
1541
+ params.stakeKeyHash ? { hash: params.stakeKeyHash, isScript: params.stakeIsScript } : null
1542
+ );
1543
+ const noDatum = constr(0, []);
1544
+ return constr(0, [
1545
+ constr(0, [bytes(params.paymentKeyHash)]),
1546
+ // canceller = SIGNATURE [pubKeyHash]
1547
+ sender,
1548
+ // refundReceiver
1549
+ noDatum,
1550
+ // refundReceiverDatum
1551
+ sender,
1552
+ // successReceiver
1553
+ noDatum,
1554
+ // successReceiverDatum
1555
+ constr(0, [bytes(params.lpAsset.policyId), bytes(params.lpAsset.nameHex)]),
1556
+ params.step,
1557
+ integer(params.maxBatcherFee),
1558
+ constr(1, [])
1559
+ // expiredOptions = Nothing
1560
+ ]);
1561
+ }
1562
+
1563
+ // src/liquidity/datum/dex-v1.ts
1564
+ function asset(a) {
1565
+ return constr(0, [bytes(a.policyId), bytes(a.nameHex)]);
1566
+ }
1567
+ function depositStep2(params) {
1568
+ return constr(2, [integer(params.minimumLP)]);
1569
+ }
1570
+ function withdrawStep2(params) {
1571
+ return constr(3, [integer(params.minimumAssetA), integer(params.minimumAssetB)]);
1572
+ }
1573
+ function oneSideDepositStep(params) {
1574
+ return constr(4, [asset(params.desiredAsset), integer(params.minimumLP)]);
1575
+ }
1576
+ function orderDatum2(params) {
1577
+ const address = addressData(
1578
+ { hash: params.paymentKeyHash, isScript: params.paymentIsScript },
1579
+ params.stakeKeyHash ? { hash: params.stakeKeyHash, isScript: params.stakeIsScript } : null
1580
+ );
1581
+ const datumHashMaybe = params.receiverDatumHash ? constr(0, [bytes(params.receiverDatumHash)]) : constr(1, []);
1582
+ return constr(0, [
1583
+ address,
1584
+ // sender
1585
+ address,
1586
+ // receiver
1587
+ datumHashMaybe,
1588
+ params.step,
1589
+ integer(params.batcherFee),
1590
+ integer(params.outputADA)
1591
+ ]);
1592
+ }
1593
+
1594
+ // src/liquidity/math/dex-v2.ts
1595
+ var FEE_DENOMINATOR = 10000n;
1596
+ function sqrt(value) {
1597
+ if (value < 0n) {
1598
+ throw new Error("sqrt of a negative number");
1599
+ }
1600
+ if (value < 2n) {
1601
+ return value;
1602
+ }
1603
+ let x = value;
1604
+ let y = (x + 1n) / 2n;
1605
+ while (y < x) {
1606
+ x = y;
1607
+ y = (x + value / x) / 2n;
1608
+ }
1609
+ return x;
1610
+ }
1611
+ function calculateAmountOut(params) {
1612
+ const diff = FEE_DENOMINATOR - params.tradingFeeNumerator;
1613
+ const inWithFee = diff * params.amountIn;
1614
+ return inWithFee * params.reserveOut / (FEE_DENOMINATOR * params.reserveIn + inWithFee);
1615
+ }
1616
+ function calculateDepositSwapAmount(params) {
1617
+ const { amountIn, amountOut, reserveIn, reserveOut, tradingFeeNumerator } = params;
1618
+ const x = (amountOut + reserveOut) * reserveIn;
1619
+ const y = 4n * (amountOut + reserveOut) * (amountOut * reserveIn * reserveIn - amountIn * reserveIn * reserveOut);
1620
+ const z14 = 2n * (amountOut + reserveOut);
1621
+ const twoDenomMinusFee = 2n * FEE_DENOMINATOR - tradingFeeNumerator;
1622
+ const a = x * x * (twoDenomMinusFee * twoDenomMinusFee) - y * FEE_DENOMINATOR * (FEE_DENOMINATOR - tradingFeeNumerator);
1623
+ const b = twoDenomMinusFee * x;
1624
+ return [sqrt(a) - b, z14 * (FEE_DENOMINATOR - tradingFeeNumerator)];
1625
+ }
1626
+ function calculateDepositAmount(params) {
1627
+ const { amountA, amountB, pool } = params;
1628
+ const { reserveA, reserveB, totalLiquidity, feeANumerator, feeBNumerator } = pool;
1629
+ const ratioA = amountA * totalLiquidity / reserveA;
1630
+ const ratioB = amountB * totalLiquidity / reserveB;
1631
+ if (ratioA > ratioB) {
1632
+ const [num, den] = calculateDepositSwapAmount({
1633
+ amountIn: amountA,
1634
+ amountOut: amountB,
1635
+ reserveIn: reserveA,
1636
+ reserveOut: reserveB,
1637
+ tradingFeeNumerator: feeANumerator
1638
+ });
1639
+ return (amountA * den - num) * totalLiquidity / (reserveA * den + num);
1640
+ }
1641
+ if (ratioA < ratioB) {
1642
+ const [num, den] = calculateDepositSwapAmount({
1643
+ amountIn: amountB,
1644
+ amountOut: amountA,
1645
+ reserveIn: reserveB,
1646
+ reserveOut: reserveA,
1647
+ tradingFeeNumerator: feeBNumerator
1648
+ });
1649
+ return (amountB * den - num) * totalLiquidity / (reserveB * den + num);
1650
+ }
1651
+ return ratioA;
1652
+ }
1653
+ function calculateWithdrawAmount(params) {
1654
+ const { withdrawalLPAmount, reserveA, reserveB, totalLiquidity } = params;
1655
+ return {
1656
+ withdrawalA: withdrawalLPAmount * reserveA / totalLiquidity,
1657
+ withdrawalB: withdrawalLPAmount * reserveB / totalLiquidity
1658
+ };
1659
+ }
1660
+ function calculateZapOutAmount(params) {
1661
+ const { withdrawalLPAmount, direction: direction2, pool } = params;
1662
+ const { reserveA, reserveB, totalLiquidity, feeANumerator, feeBNumerator } = pool;
1663
+ const { withdrawalA, withdrawalB } = calculateWithdrawAmount({
1664
+ withdrawalLPAmount,
1665
+ reserveA,
1666
+ reserveB,
1667
+ totalLiquidity
1668
+ });
1669
+ const reserveAAfter = reserveA - withdrawalA;
1670
+ const reserveBAfter = reserveB - withdrawalB;
1671
+ if (direction2 === "A_TO_B") {
1672
+ return withdrawalB + calculateAmountOut({
1673
+ reserveIn: reserveAAfter,
1674
+ reserveOut: reserveBAfter,
1675
+ amountIn: withdrawalA,
1676
+ tradingFeeNumerator: feeANumerator
1677
+ });
1678
+ }
1679
+ return withdrawalA + calculateAmountOut({
1680
+ reserveIn: reserveBAfter,
1681
+ reserveOut: reserveAAfter,
1682
+ amountIn: withdrawalB,
1683
+ tradingFeeNumerator: feeBNumerator
1684
+ });
1685
+ }
1686
+ function minimumWithSlippage(amount, slippagePercent) {
1687
+ if (!(slippagePercent >= 0)) {
1688
+ throw new Error(`slippage must be a non-negative percent, received ${slippagePercent}`);
1689
+ }
1690
+ const bps = BigInt(Math.round(slippagePercent * 100));
1691
+ return amount * (10000n - bps) / 10000n;
1692
+ }
1693
+
1694
+ // src/liquidity/math/dex-v1.ts
1695
+ function calculateSwapExactIn(params) {
1696
+ const { amountIn, reserveIn, reserveOut } = params;
1697
+ const numerator = amountIn * 997n * reserveOut;
1698
+ const denominator = amountIn * 997n + reserveIn * 1000n;
1699
+ return numerator / denominator;
1700
+ }
1701
+ function calculateDeposit(params) {
1702
+ const { depositedAmountA, depositedAmountB, reserveA, reserveB, totalLiquidity } = params;
1703
+ const deltaLiquidityA = depositedAmountA * totalLiquidity / reserveA;
1704
+ const deltaLiquidityB = depositedAmountB * totalLiquidity / reserveB;
1705
+ let necessaryAmountA;
1706
+ let necessaryAmountB;
1707
+ let lpAmount;
1708
+ if (deltaLiquidityA > deltaLiquidityB) {
1709
+ necessaryAmountA = depositedAmountB * reserveA / reserveB;
1710
+ necessaryAmountB = depositedAmountB;
1711
+ lpAmount = deltaLiquidityB;
1712
+ } else if (deltaLiquidityA < deltaLiquidityB) {
1713
+ necessaryAmountA = depositedAmountA;
1714
+ necessaryAmountB = depositedAmountA * reserveB / reserveA;
1715
+ lpAmount = deltaLiquidityA;
1716
+ } else {
1717
+ necessaryAmountA = depositedAmountA;
1718
+ necessaryAmountB = depositedAmountB;
1719
+ lpAmount = deltaLiquidityA;
1720
+ }
1721
+ return { necessaryAmountA, necessaryAmountB, lpAmount };
1722
+ }
1723
+ function calculateWithdraw(params) {
1724
+ const { withdrawalLPAmount, reserveA, reserveB, totalLiquidity } = params;
1725
+ return {
1726
+ amountAReceive: withdrawalLPAmount * reserveA / totalLiquidity,
1727
+ amountBReceive: withdrawalLPAmount * reserveB / totalLiquidity
1728
+ };
1729
+ }
1730
+ function calculateZapIn(params) {
1731
+ const { amountIn, reserveIn, reserveOut, totalLiquidity } = params;
1732
+ const swapAmountIn = (sqrt(1997n ** 2n * reserveIn ** 2n + 4n * 997n * 1000n * amountIn * reserveIn) - 1997n * reserveIn) / (2n * 997n);
1733
+ const receiveAmountOut = calculateSwapExactIn({ amountIn: swapAmountIn, reserveIn, reserveOut });
1734
+ const minimumLP = receiveAmountOut * totalLiquidity / (reserveOut - receiveAmountOut);
1735
+ return { minimumLP, swapAmountIn, receiveAmountOut };
1736
+ }
1737
+
1738
+ // src/liquidity/serializer.ts
1739
+ var INTERNAL_SDK = "@minswap/internal-sdk";
1740
+ var cached;
1741
+ async function loadDexSerializer() {
1742
+ cached ??= (async () => {
1743
+ let mod;
1744
+ try {
1745
+ mod = await import(
1746
+ /* @vite-ignore */
1747
+ INTERNAL_SDK
1748
+ );
1749
+ } catch (cause) {
1750
+ throw new MinswapError(
1751
+ `Liquidity operations need ${INTERNAL_SDK} to serialize transactions. Install it: npm install ${INTERNAL_SDK}`,
1752
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
1753
+ { param: INTERNAL_SDK, reason: `could not be imported: ${String(cause)}` }
1754
+ );
1755
+ }
1756
+ await mod.RustModule.load();
1757
+ return mod;
1758
+ })();
1759
+ return cached;
1760
+ }
1761
+ async function toDatumHex(datum) {
1762
+ const { PlutusData: plutus } = await loadDexSerializer();
1763
+ return plutus.toDataHex(datum);
1764
+ }
1765
+ async function loadDexBuilder() {
1766
+ return loadDexSerializer();
1767
+ }
1768
+
1769
+ // src/liquidity/build/assemble.ts
1770
+ async function assembleOrder(order) {
1771
+ const b = await loadDexBuilder();
1772
+ const { TxBuilderV2, TxOut, Value, Address, Asset, Bytes, DatumSource, Utxo, EmulatorProvider } = b;
1773
+ const CoinSelectionAlgorithm = b.CoinSelectionAlgorithm;
1774
+ const valueMap = { lovelace: order.lovelace };
1775
+ for (const a of order.assets) {
1776
+ valueMap[Asset.fromBlockFrostString(a.unit).toString()] = a.amount;
1777
+ }
1778
+ const datumSource = order.datumKind === "inline" ? DatumSource.newInlineDatum(Bytes.fromHex(order.orderDatumHex)) : DatumSource.newOutlineDatum(Bytes.fromHex(order.orderDatumHex));
1779
+ const output = new TxOut(
1780
+ Address.fromBech32(order.orderAddress),
1781
+ new Value(valueMap),
1782
+ datumSource
1783
+ );
1784
+ const txb = new TxBuilderV2(MAINNET_NETWORK_ENV);
1785
+ txb.payTo(output);
1786
+ const txc = await txb.completeUnsafe({
1787
+ changeAddress: Address.fromBech32(order.sender),
1788
+ provider: new EmulatorProvider(MAINNET_NETWORK_ENV),
1789
+ walletUtxos: order.walletUtxoCbors.map((c) => Utxo.fromHex(c)),
1790
+ walletCollaterals: order.collateralCbors?.map((c) => Utxo.fromHex(c)),
1791
+ coinSelectionAlgorithm: CoinSelectionAlgorithm.MINSWAP
1792
+ });
1793
+ return txc.complete();
1794
+ }
1795
+
1796
+ // src/liquidity/build/dex-v1.ts
1797
+ var isAda = (a) => a.policyId === "" && a.nameHex === "";
1798
+ var unitOf = (a) => a.policyId + a.nameHex;
1799
+ async function resolveSender(sender) {
1800
+ const b = await loadDexBuilder();
1801
+ const addr = b.Address.fromBech32(sender);
1802
+ const paymentKeyHash = addr.toPaymentCredential().payload.hex;
1803
+ const stakeAddr = addr.toStakeAddress?.();
1804
+ const stakeKeyHash = stakeAddr ? stakeAddr.toPubKeyHash()?.keyHash.hex ?? null : null;
1805
+ const orderAddress = b.Address.fromPlutusJson(
1806
+ addressData(
1807
+ { hash: DEX_V1.orderScriptHash, isScript: true },
1808
+ stakeKeyHash ? { hash: stakeKeyHash } : null
1809
+ ),
1810
+ MAINNET_NETWORK_ENV
1811
+ ).toString();
1812
+ return { paymentKeyHash, stakeKeyHash, orderAddress };
1813
+ }
1814
+ async function build(sender, senderBech32, step, lovelace, assets, walletUtxoCbors) {
1815
+ const datum = orderDatum2({
1816
+ paymentKeyHash: sender.paymentKeyHash,
1817
+ stakeKeyHash: sender.stakeKeyHash,
1818
+ step,
1819
+ batcherFee: BATCHER_FEE,
1820
+ outputADA: DEPOSIT_ADA
1821
+ });
1822
+ const cbor = await assembleOrder({
1823
+ orderAddress: sender.orderAddress,
1824
+ orderDatumHex: await toDatumHex(datum),
1825
+ datumKind: "outline",
1826
+ lovelace,
1827
+ assets,
1828
+ sender: senderBech32,
1829
+ walletUtxoCbors
1830
+ });
1831
+ return { cbor };
1832
+ }
1833
+ function adaSide(asset2, amount) {
1834
+ return isAda(asset2) ? amount : 0n;
1835
+ }
1836
+ function tokenAsset(asset2, amount) {
1837
+ return isAda(asset2) || amount === 0n ? [] : [{ unit: unitOf(asset2), amount }];
1838
+ }
1839
+ async function buildAddLiquidity(params) {
1840
+ if (params.amountA <= 0n || params.amountB <= 0n) {
1841
+ throw new Error(
1842
+ "V1 add-liquidity needs both amounts > 0; use buildZapIn for a one-sided deposit"
1843
+ );
1844
+ }
1845
+ const { lpAmount } = calculateDeposit({
1846
+ depositedAmountA: params.amountA,
1847
+ depositedAmountB: params.amountB,
1848
+ reserveA: params.reserveA,
1849
+ reserveB: params.reserveB,
1850
+ totalLiquidity: params.totalLiquidity
1851
+ });
1852
+ const minimumLP = minimumWithSlippage(lpAmount, params.slippage);
1853
+ const step = depositStep2({ minimumLP });
1854
+ const lovelace = DEPOSIT_ADA + BATCHER_FEE + adaSide(params.assetA, params.amountA) + adaSide(params.assetB, params.amountB);
1855
+ const assets = [
1856
+ ...tokenAsset(params.assetA, params.amountA),
1857
+ ...tokenAsset(params.assetB, params.amountB)
1858
+ ];
1859
+ const sender = await resolveSender(params.sender);
1860
+ return build(sender, params.sender, step, lovelace, assets, params.walletUtxoCbors);
1861
+ }
1862
+ async function buildZapIn(params) {
1863
+ const { minimumLP: rawLP } = calculateZapIn({
1864
+ amountIn: params.amountIn,
1865
+ reserveIn: params.reserveIn,
1866
+ reserveOut: params.reserveOut,
1867
+ totalLiquidity: params.totalLiquidity
1868
+ });
1869
+ const minimumLP = minimumWithSlippage(rawLP, params.slippage);
1870
+ const step = oneSideDepositStep({
1871
+ desiredAsset: { policyId: params.desiredAsset.policyId, nameHex: params.desiredAsset.nameHex },
1872
+ minimumLP
1873
+ });
1874
+ const lovelace = DEPOSIT_ADA + BATCHER_FEE + adaSide(params.assetIn, params.amountIn);
1875
+ const assets = tokenAsset(params.assetIn, params.amountIn);
1876
+ const sender = await resolveSender(params.sender);
1877
+ return build(sender, params.sender, step, lovelace, assets, params.walletUtxoCbors);
1878
+ }
1879
+ async function buildRemoveLiquidity(params) {
1880
+ const { amountAReceive, amountBReceive } = calculateWithdraw({
1881
+ withdrawalLPAmount: params.lpAmount,
1882
+ reserveA: params.reserveA,
1883
+ reserveB: params.reserveB,
1884
+ totalLiquidity: params.totalLiquidity
1885
+ });
1886
+ const step = withdrawStep2({
1887
+ minimumAssetA: minimumWithSlippage(amountAReceive, params.slippage),
1888
+ minimumAssetB: minimumWithSlippage(amountBReceive, params.slippage)
1889
+ });
1890
+ const assets = [{ unit: unitOf(params.lpAsset), amount: params.lpAmount }];
1891
+ const sender = await resolveSender(params.sender);
1892
+ return build(
1893
+ sender,
1894
+ params.sender,
1895
+ step,
1896
+ DEPOSIT_ADA + BATCHER_FEE,
1897
+ assets,
1898
+ params.walletUtxoCbors
1899
+ );
1900
+ }
1901
+
1902
+ // src/liquidity/build/dex-v2.ts
1903
+ var isAda2 = (a) => a.policyId === "" && a.nameHex === "";
1904
+ var unitOf2 = (a) => a.policyId + a.nameHex;
1905
+ async function resolveSender2(sender) {
1906
+ const b = await loadDexBuilder();
1907
+ const addr = b.Address.fromBech32(sender);
1908
+ const paymentKeyHash = addr.toPaymentCredential().payload.hex;
1909
+ const stakeAddr = addr.toStakeAddress?.();
1910
+ const stakeKeyHash = stakeAddr ? stakeAddr.toPubKeyHash()?.keyHash.hex ?? null : null;
1911
+ const orderAddress = b.Address.fromPlutusJson(
1912
+ addressData(
1913
+ { hash: DEX_V2.orderScriptHash, isScript: true },
1914
+ stakeKeyHash ? { hash: stakeKeyHash } : null
1915
+ ),
1916
+ MAINNET_NETWORK_ENV
1917
+ ).toString();
1918
+ return { paymentKeyHash, stakeKeyHash, orderAddress };
1919
+ }
1920
+ async function build2(sender, senderBech32, lpAsset, step, lovelace, assets, walletUtxoCbors) {
1921
+ const datum = orderDatum({
1922
+ paymentKeyHash: sender.paymentKeyHash,
1923
+ stakeKeyHash: sender.stakeKeyHash,
1924
+ lpAsset: { policyId: lpAsset.policyId, nameHex: lpAsset.nameHex },
1925
+ step,
1926
+ maxBatcherFee: BATCHER_FEE
1927
+ });
1928
+ const cbor = await assembleOrder({
1929
+ orderAddress: sender.orderAddress,
1930
+ orderDatumHex: await toDatumHex(datum),
1931
+ datumKind: "inline",
1932
+ lovelace,
1933
+ assets,
1934
+ sender: senderBech32,
1935
+ walletUtxoCbors
1936
+ });
1937
+ return { cbor };
1938
+ }
1939
+ function adaSide2(asset2, amount) {
1940
+ return isAda2(asset2) ? amount : 0n;
1941
+ }
1942
+ function tokenAsset2(asset2, amount) {
1943
+ return isAda2(asset2) || amount === 0n ? [] : [{ unit: unitOf2(asset2), amount }];
1944
+ }
1945
+ async function buildAddLiquidity2(params) {
1946
+ const lp = calculateDepositAmount({
1947
+ amountA: params.amountA,
1948
+ amountB: params.amountB,
1949
+ pool: params.pool
1950
+ });
1951
+ const minimumLP = minimumWithSlippage(lp, params.slippage);
1952
+ const step = depositStep({
1953
+ depositAmountA: params.amountA,
1954
+ depositAmountB: params.amountB,
1955
+ minimumLP});
1956
+ const lovelace = DEPOSIT_ADA + BATCHER_FEE + adaSide2(params.assetA, params.amountA) + adaSide2(params.assetB, params.amountB);
1957
+ const assets = [
1958
+ ...tokenAsset2(params.assetA, params.amountA),
1959
+ ...tokenAsset2(params.assetB, params.amountB)
1960
+ ];
1961
+ const sender = await resolveSender2(params.sender);
1962
+ return build2(
1963
+ sender,
1964
+ params.sender,
1965
+ params.lpAsset,
1966
+ step,
1967
+ lovelace,
1968
+ assets,
1969
+ params.walletUtxoCbors
1970
+ );
1971
+ }
1972
+ async function buildRemoveLiquidity2(params) {
1973
+ const { withdrawalA, withdrawalB } = calculateWithdrawAmount({
1974
+ withdrawalLPAmount: params.lpAmount,
1975
+ reserveA: params.pool.reserveA,
1976
+ reserveB: params.pool.reserveB,
1977
+ totalLiquidity: params.pool.totalLiquidity
1978
+ });
1979
+ const step = withdrawStep({
1980
+ withdrawalLPAmount: params.lpAmount,
1981
+ minimumAssetA: minimumWithSlippage(withdrawalA, params.slippage),
1982
+ minimumAssetB: minimumWithSlippage(withdrawalB, params.slippage)});
1983
+ const assets = [{ unit: unitOf2(params.lpAsset), amount: params.lpAmount }];
1984
+ const sender = await resolveSender2(params.sender);
1985
+ return build2(
1986
+ sender,
1987
+ params.sender,
1988
+ params.lpAsset,
1989
+ step,
1990
+ DEPOSIT_ADA + BATCHER_FEE,
1991
+ assets,
1992
+ params.walletUtxoCbors
1993
+ );
1994
+ }
1995
+ async function buildZapOut(params) {
1996
+ const received = calculateZapOutAmount({
1997
+ withdrawalLPAmount: params.lpAmount,
1998
+ direction: params.direction,
1999
+ pool: params.pool
2000
+ });
2001
+ const step = zapOutStep({
2002
+ direction: params.direction,
2003
+ withdrawalLPAmount: params.lpAmount,
2004
+ minimumReceived: minimumWithSlippage(received, params.slippage)});
2005
+ const assets = [{ unit: unitOf2(params.lpAsset), amount: params.lpAmount }];
2006
+ const sender = await resolveSender2(params.sender);
2007
+ return build2(
2008
+ sender,
2009
+ params.sender,
2010
+ params.lpAsset,
2011
+ step,
2012
+ DEPOSIT_ADA + BATCHER_FEE,
2013
+ assets,
2014
+ params.walletUtxoCbors
2015
+ );
2016
+ }
2017
+
2018
+ // src/liquidity/datum/stableswap.ts
2019
+ function depositStep3(params) {
2020
+ return constr(1, [integer(params.minimumLP)]);
2021
+ }
2022
+ function withdrawStep3(params) {
2023
+ return constr(2, [{ list: params.minimumAmounts.map(integer) }]);
2024
+ }
2025
+ function withdrawOneCoinStep(params) {
2026
+ return constr(4, [integer(params.assetOutIndex), integer(params.minimumAssetOut)]);
2027
+ }
2028
+ function maybe(value) {
2029
+ return value ? constr(0, [value]) : constr(1, []);
2030
+ }
2031
+ function orderDatum3(params) {
2032
+ const payment = { hash: params.paymentKeyHash, isScript: params.paymentIsScript };
2033
+ const stake = params.stakeKeyHash ? { hash: params.stakeKeyHash, isScript: params.stakeIsScript } : null;
2034
+ const sender = addressData(payment, stake);
2035
+ const r = params.receiver;
2036
+ const receiver = r ? addressData(
2037
+ { hash: r.paymentKeyHash, isScript: r.paymentIsScript },
2038
+ r.stakeKeyHash ? { hash: r.stakeKeyHash, isScript: r.stakeIsScript } : null
2039
+ ) : sender;
2040
+ return constr(0, [
2041
+ sender,
2042
+ receiver,
2043
+ maybe(params.receiverDatumHash ? bytes(params.receiverDatumHash) : null),
2044
+ params.step,
2045
+ integer(params.batcherFee),
2046
+ integer(params.outputADA)
2047
+ ]);
2048
+ }
2049
+
2050
+ // src/liquidity/math/stableswap.ts
2051
+ function zipWith(a, b, f) {
2052
+ const n = Math.min(a.length, b.length);
2053
+ const out = [];
2054
+ for (let i = 0; i < n; i++) {
2055
+ out.push(f(a[i], b[i]));
2056
+ }
2057
+ return out;
2058
+ }
2059
+ function invariant(condition, message) {
2060
+ if (!condition) {
2061
+ throw new Error(message);
2062
+ }
2063
+ }
2064
+ function getD(mulBalances, amp) {
2065
+ const sumMulBalances = mulBalances.reduce((sum, balance) => sum + balance, 0n);
2066
+ if (sumMulBalances === 0n) {
2067
+ return 0n;
2068
+ }
2069
+ const length = BigInt(mulBalances.length);
2070
+ let dPrev = 0n;
2071
+ let d = sumMulBalances;
2072
+ const ann = amp * length;
2073
+ for (let i = 0; i < 255; i++) {
2074
+ let dp = d;
2075
+ for (const mulBalance of mulBalances) {
2076
+ dp = dp * d / (mulBalance * length);
2077
+ }
2078
+ dPrev = d;
2079
+ d = (ann * sumMulBalances + dp * length) * d / ((ann - 1n) * d + (length + 1n) * dp);
2080
+ if (d > dPrev) {
2081
+ if (d - dPrev <= 1n) {
2082
+ break;
2083
+ }
2084
+ } else {
2085
+ if (dPrev - d <= 1n) {
2086
+ break;
2087
+ }
2088
+ }
2089
+ }
2090
+ return d;
2091
+ }
2092
+ function getYD(i, xp, amp, d) {
2093
+ const length = BigInt(xp.length);
2094
+ invariant(0 <= i && i < xp.length, "getYD failed: i must be less than length of xp");
2095
+ let c = d;
2096
+ let s = 0n;
2097
+ const ann = amp * length;
2098
+ let _x = 0n;
2099
+ for (let index = 0; index < Number(length); index++) {
2100
+ if (index !== i) {
2101
+ _x = xp[index];
2102
+ } else {
2103
+ continue;
2104
+ }
2105
+ s += _x;
2106
+ c = c * d / (_x * length);
2107
+ }
2108
+ c = c * d / (ann * length);
2109
+ const b = s + d / ann;
2110
+ let yPrev = 0n;
2111
+ let y = d;
2112
+ for (let index = 0; index < 255; index++) {
2113
+ yPrev = y;
2114
+ y = (y * y + c) / (2n * y + b - d);
2115
+ if (y > yPrev) {
2116
+ if (y - yPrev <= 1n) {
2117
+ break;
2118
+ }
2119
+ } else {
2120
+ if (yPrev - y <= 1n) {
2121
+ break;
2122
+ }
2123
+ }
2124
+ }
2125
+ return y;
2126
+ }
2127
+ function getDMem(balances, multiples, amp) {
2128
+ const mulBalances = zipWith(balances, multiples, (a, b) => a * b);
2129
+ return getD(mulBalances, amp);
2130
+ }
2131
+ function calculateDeposit2(params) {
2132
+ const {
2133
+ amountIns,
2134
+ amp,
2135
+ multiples,
2136
+ datumBalances,
2137
+ totalLiquidity,
2138
+ fee,
2139
+ adminFee,
2140
+ feeDenominator
2141
+ } = params;
2142
+ const tempDatumBalances = [...datumBalances];
2143
+ const length = multiples.length;
2144
+ invariant(
2145
+ amountIns.length === length,
2146
+ `calculateDeposit error: amountIns's length ${amountIns.length} is invalid, must be ${length}`
2147
+ );
2148
+ let newDatumBalances = [];
2149
+ let lpAmount = 0n;
2150
+ if (totalLiquidity === 0n) {
2151
+ for (let i = 0; i < length; ++i) {
2152
+ invariant(amountIns[i] > 0n, `calculateDeposit error: amount index ${i} must be positive`);
2153
+ }
2154
+ newDatumBalances = zipWith(tempDatumBalances, amountIns, (a, b) => a + b);
2155
+ const d1 = getDMem(newDatumBalances, multiples, amp);
2156
+ invariant(d1 > 0n, "calculateDeposit: d1 must be greater than 0");
2157
+ lpAmount = d1;
2158
+ } else {
2159
+ let sumIns = 0n;
2160
+ for (let i = 0; i < length; ++i) {
2161
+ if (amountIns[i] < 0n) {
2162
+ invariant(
2163
+ amountIns[i] > 0n,
2164
+ `calculateDeposit error: amountIns index ${i} must be non-negative`
2165
+ );
2166
+ }
2167
+ sumIns += amountIns[i];
2168
+ }
2169
+ invariant(sumIns > 0n, "calculateDeposit error: sum of amountIns must be positive");
2170
+ const newDatumBalanceWithoutFee = zipWith(tempDatumBalances, amountIns, (a, b) => a + b);
2171
+ const d0 = getDMem(tempDatumBalances, multiples, amp);
2172
+ const d1 = getDMem(newDatumBalanceWithoutFee, multiples, amp);
2173
+ invariant(d1 > d0, `calculateDeposit: d1 must be greater than d0, d1: ${d1}, d0: ${d0}`);
2174
+ const specialFee = fee * BigInt(length) / (4n * (BigInt(length) - 1n));
2175
+ const newDatBalancesWithTradingFee = [];
2176
+ for (let i = 0; i < tempDatumBalances.length; i++) {
2177
+ const oldBalance = tempDatumBalances[i];
2178
+ const newBalance = newDatumBalanceWithoutFee[i];
2179
+ const idealBalance = d1 * oldBalance / d0;
2180
+ let different = 0n;
2181
+ if (newBalance > idealBalance) {
2182
+ different = newBalance - idealBalance;
2183
+ } else {
2184
+ different = idealBalance - newBalance;
2185
+ }
2186
+ const tradingFeeAmount = specialFee * different / feeDenominator;
2187
+ const adminFeeAmount = tradingFeeAmount * adminFee / feeDenominator;
2188
+ newDatumBalances.push(newBalance - adminFeeAmount);
2189
+ newDatBalancesWithTradingFee.push(newBalance - tradingFeeAmount);
2190
+ }
2191
+ for (let i = 0; i < length; ++i) {
2192
+ invariant(
2193
+ newDatBalancesWithTradingFee[i] > 0n,
2194
+ "calculateDeposit error: deposit amount is too small"
2195
+ );
2196
+ }
2197
+ const d2 = getDMem(newDatBalancesWithTradingFee, multiples, amp);
2198
+ lpAmount = totalLiquidity * (d2 - d0) / d0;
2199
+ }
2200
+ invariant(lpAmount > 0n, `calculateDeposit error: lpAmountOut ${lpAmount} must be positive`);
2201
+ return lpAmount;
2202
+ }
2203
+ function calculateWithdraw2(params) {
2204
+ const { withdrawalLPAmount, multiples, datumBalances, totalLiquidity } = params;
2205
+ const tempDatumBalances = [...datumBalances];
2206
+ const length = multiples.length;
2207
+ invariant(
2208
+ withdrawalLPAmount > 0n,
2209
+ "calculateWithdraw error: withdrawalLPAmount must be positive"
2210
+ );
2211
+ const amountOuts = tempDatumBalances.map(
2212
+ (balance) => balance * withdrawalLPAmount / totalLiquidity
2213
+ );
2214
+ let sumOuts = 0n;
2215
+ for (let i = 0; i < length; ++i) {
2216
+ invariant(amountOuts[i] >= 0n, "calculateWithdraw error: amountOuts must be non-negative");
2217
+ sumOuts += amountOuts[i];
2218
+ }
2219
+ invariant(sumOuts > 0n, "calculateWithdraw error: sum of amountOuts must be positive");
2220
+ return amountOuts;
2221
+ }
2222
+ function calculateZapOut(params) {
2223
+ const {
2224
+ amountLpIn,
2225
+ outIndex,
2226
+ amp,
2227
+ multiples,
2228
+ datumBalances,
2229
+ totalLiquidity,
2230
+ fee,
2231
+ adminFee,
2232
+ feeDenominator
2233
+ } = params;
2234
+ const tempDatumBalances = [...datumBalances];
2235
+ const length = multiples.length;
2236
+ invariant(amountLpIn > 0n, `calculateZapOut error: amountLpIn ${amountLpIn} must be positive`);
2237
+ invariant(
2238
+ 0 <= outIndex && outIndex < length,
2239
+ `calculateZapOut error: outIndex ${outIndex} is not valid`
2240
+ );
2241
+ const mulBalances = zipWith(tempDatumBalances, multiples, (a, b) => a * b);
2242
+ const mulOut = multiples[outIndex];
2243
+ const d0 = getD(mulBalances, amp);
2244
+ const d1 = d0 - amountLpIn * d0 / totalLiquidity;
2245
+ const mulBalancesReduced = mulBalances;
2246
+ const newYWithoutFee = getYD(outIndex, mulBalances, amp, d1);
2247
+ const specialFee = fee * BigInt(length) / (4n * (BigInt(length) - 1n));
2248
+ const amountOutWithoutFee = (mulBalances[outIndex] - newYWithoutFee) / mulOut;
2249
+ for (let i = 0; i < length; ++i) {
2250
+ const diff = i === outIndex ? mulBalances[i] * d1 / d0 - newYWithoutFee : mulBalances[i] - mulBalances[i] * d1 / d0;
2251
+ mulBalancesReduced[i] -= diff * specialFee / feeDenominator;
2252
+ }
2253
+ const newY = getYD(outIndex, mulBalancesReduced, amp, d1);
2254
+ const amountOut = (mulBalancesReduced[outIndex] - newY - 1n) / mulOut;
2255
+ tempDatumBalances[outIndex] -= amountOut + (amountOutWithoutFee - amountOut) * adminFee / feeDenominator;
2256
+ return amountOut;
2257
+ }
2258
+ function minimumWithSlippage2(amount, slippagePercent) {
2259
+ if (!(slippagePercent >= 0)) {
2260
+ throw new Error(`slippage must be a non-negative percent, received ${slippagePercent}`);
2261
+ }
2262
+ const bps = BigInt(Math.round(slippagePercent * 100));
2263
+ return amount * (10000n - bps) / 10000n;
2264
+ }
2265
+
2266
+ // src/liquidity/build/stableswap.ts
2267
+ var unitOfLpAsset = (a) => a.policyId + a.nameHex;
2268
+ async function resolveSender3(sender) {
2269
+ const b = await loadDexBuilder();
2270
+ const addr = b.Address.fromBech32(sender);
2271
+ const paymentKeyHash = addr.toPaymentCredential().payload.hex;
2272
+ const stakeAddr = addr.toStakeAddress?.();
2273
+ const stakeKeyHash = stakeAddr ? stakeAddr.toPubKeyHash()?.keyHash.hex ?? null : null;
2274
+ return { paymentKeyHash, stakeKeyHash };
2275
+ }
2276
+ async function build3(sender, senderBech32, config, step, lovelace, assets, walletUtxoCbors) {
2277
+ const datum = orderDatum3({
2278
+ paymentKeyHash: sender.paymentKeyHash,
2279
+ stakeKeyHash: sender.stakeKeyHash,
2280
+ step,
2281
+ batcherFee: BATCHER_FEE,
2282
+ outputADA: DEPOSIT_ADA
2283
+ });
2284
+ const cbor = await assembleOrder({
2285
+ orderAddress: config.orderAddress,
2286
+ orderDatumHex: await toDatumHex(datum),
2287
+ datumKind: "inline",
2288
+ lovelace,
2289
+ assets,
2290
+ sender: senderBech32,
2291
+ walletUtxoCbors
2292
+ });
2293
+ return { cbor };
2294
+ }
2295
+ var isAda3 = (coinId) => coinId === "lovelace";
2296
+ function splitAmounts(config, amounts) {
2297
+ const assets = [];
2298
+ let ada = 0n;
2299
+ for (let i = 0; i < config.assets.length; i++) {
2300
+ const amount = amounts[i] ?? 0n;
2301
+ if (amount === 0n) {
2302
+ continue;
2303
+ }
2304
+ if (isAda3(config.assets[i])) {
2305
+ ada += amount;
2306
+ } else {
2307
+ assets.push({ unit: coinIdToAssetUnit(config.assets[i]), amount });
2308
+ }
2309
+ }
2310
+ return { assets, ada };
2311
+ }
2312
+ async function buildAddLiquidity3(params) {
2313
+ const { config, poolState } = params;
2314
+ const lp = calculateDeposit2({
2315
+ amp: poolState.amp,
2316
+ multiples: config.multiples,
2317
+ datumBalances: poolState.balances,
2318
+ fee: config.fee,
2319
+ adminFee: config.adminFee,
2320
+ feeDenominator: config.feeDenominator,
2321
+ amountIns: params.amounts,
2322
+ totalLiquidity: poolState.totalLiquidity
2323
+ });
2324
+ const step = depositStep3({ minimumLP: minimumWithSlippage2(lp, params.slippage) });
2325
+ const { assets, ada } = splitAmounts(config, params.amounts);
2326
+ const lovelace = DEPOSIT_ADA + BATCHER_FEE + ada;
2327
+ const sender = await resolveSender3(params.sender);
2328
+ return build3(sender, params.sender, config, step, lovelace, assets, params.walletUtxoCbors);
2329
+ }
2330
+ async function buildRemoveLiquidity3(params) {
2331
+ const { config, poolState } = params;
2332
+ const amountOuts = calculateWithdraw2({
2333
+ withdrawalLPAmount: params.lpAmount,
2334
+ multiples: config.multiples,
2335
+ datumBalances: poolState.balances,
2336
+ totalLiquidity: poolState.totalLiquidity
2337
+ });
2338
+ const step = withdrawStep3({
2339
+ minimumAmounts: amountOuts.map((a) => minimumWithSlippage2(a, params.slippage))
2340
+ });
2341
+ const assets = [{ unit: unitOfLpAsset(params.lpAsset), amount: params.lpAmount }];
2342
+ const sender = await resolveSender3(params.sender);
2343
+ return build3(
2344
+ sender,
2345
+ params.sender,
2346
+ config,
2347
+ step,
2348
+ DEPOSIT_ADA + BATCHER_FEE,
2349
+ assets,
2350
+ params.walletUtxoCbors
2351
+ );
2352
+ }
2353
+ async function buildZapOut2(params) {
2354
+ const { config, poolState } = params;
2355
+ const received = calculateZapOut({
2356
+ amp: poolState.amp,
2357
+ multiples: config.multiples,
2358
+ datumBalances: poolState.balances,
2359
+ fee: config.fee,
2360
+ adminFee: config.adminFee,
2361
+ feeDenominator: config.feeDenominator,
2362
+ amountLpIn: params.lpAmount,
2363
+ outIndex: params.assetOutIndex,
2364
+ totalLiquidity: poolState.totalLiquidity
2365
+ });
2366
+ const step = withdrawOneCoinStep({
2367
+ assetOutIndex: BigInt(params.assetOutIndex),
2368
+ minimumAssetOut: minimumWithSlippage2(received, params.slippage)
2369
+ });
2370
+ const assets = [{ unit: unitOfLpAsset(params.lpAsset), amount: params.lpAmount }];
2371
+ const sender = await resolveSender3(params.sender);
2372
+ return build3(
2373
+ sender,
2374
+ params.sender,
2375
+ config,
2376
+ step,
2377
+ DEPOSIT_ADA + BATCHER_FEE,
2378
+ assets,
2379
+ params.walletUtxoCbors
2380
+ );
2381
+ }
2382
+
2383
+ // src/liquidity/config-stableswap.ts
2384
+ var STABLESWAP_CONFIGS = [
2385
+ {
2386
+ key: "djed-iusd",
2387
+ lpAsset: "2c07095028169d7ab4376611abef750623c8f955597a38cd15248640.444a45442d695553442d534c50",
2388
+ orderAddress: "addr1w9xy6edqv9hkptwzewns75ehq53nk8t73je7np5vmj3emps698n9g",
2389
+ assets: [
2390
+ "8db269c3ec630e06ae29f74bc39edd1f87c819f1056206e879a1cd61.446a65644d6963726f555344",
2391
+ "f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b69880.69555344"
2392
+ ],
2393
+ multiples: [1n, 1n],
2394
+ fee: 1000000n,
2395
+ adminFee: 5000000000n,
2396
+ feeDenominator: 10000000000n
2397
+ },
2398
+ {
2399
+ key: "usdc-djed",
2400
+ lpAsset: "ac49e0969d76ed5aa9e9861a77be65f4fc29e9a979dc4c37a99eb8f4.555344432d444a45442d534c50",
2401
+ orderAddress: "addr1w93d8cuht3hvqt2qqfjqgyek3gk5d6ss2j93e5sh505m0ng8cmze2",
2402
+ assets: [
2403
+ "25c5de5f5b286073c593edfd77b48abc7a48e5a4f3d4cd9d428ff935.55534443",
2404
+ "8db269c3ec630e06ae29f74bc39edd1f87c819f1056206e879a1cd61.446a65644d6963726f555344"
2405
+ ],
2406
+ multiples: [1n, 100n],
2407
+ fee: 1000000n,
2408
+ adminFee: 5000000000n,
2409
+ feeDenominator: 10000000000n
2410
+ },
2411
+ {
2412
+ key: "usdm-iusd",
2413
+ lpAsset: "31f92531ac9f1af3079701fab7c66ce997eb07988277ee5b9d640301.5553444d2d695553442d534c50",
2414
+ orderAddress: "addr1wxtv9k2lcum5pmcc4wu44a5tufulszahz84knff87wcawycez9lug",
2415
+ assets: [
2416
+ "c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad.0014df105553444d",
2417
+ "f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b69880.69555344"
2418
+ ],
2419
+ multiples: [1n, 1n],
2420
+ fee: 1000000n,
2421
+ adminFee: 5000000000n,
2422
+ feeDenominator: 10000000000n
2423
+ },
2424
+ {
2425
+ key: "djed-usdm",
2426
+ lpAsset: "5b042cf53c0b2ce4f30a9e743b4871ad8c6dcdf1d845133395f55a8e.444a45442d5553444d2d534c50",
2427
+ orderAddress: "addr1wxr9ppdymqgw6g0hvaaa7wc6j0smwh730ujx6lczgdynehsguav8d",
2428
+ assets: [
2429
+ "8db269c3ec630e06ae29f74bc39edd1f87c819f1056206e879a1cd61.446a65644d6963726f555344",
2430
+ "c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad.0014df105553444d"
2431
+ ],
2432
+ multiples: [1n, 1n],
2433
+ fee: 1000000n,
2434
+ adminFee: 5000000000n,
2435
+ feeDenominator: 10000000000n
2436
+ },
2437
+ {
2438
+ key: "djed-myusd",
2439
+ lpAsset: "b69f5d48c91297142c46b764b69ab57844e3e7af9d7ba9bc63c3c517.444a45442d4d795553442d534c50",
2440
+ orderAddress: "addr1w9ksys0l07s9933kgkn4uxylsss5k6lqvt6e66kfc7am9sgtwqgv0",
2441
+ assets: [
2442
+ "8db269c3ec630e06ae29f74bc39edd1f87c819f1056206e879a1cd61.446a65644d6963726f555344",
2443
+ "92776616f1f32c65a173392e4410a3d8c39dcf6ef768c73af164779c.4d79555344"
2444
+ ],
2445
+ multiples: [1n, 1n],
2446
+ fee: 1000000n,
2447
+ adminFee: 5000000000n,
2448
+ feeDenominator: 10000000000n
2449
+ },
2450
+ {
2451
+ key: "myusd-usdm",
2452
+ lpAsset: "5827249dcaf49ce7ccae2e0577fd9bf9514a4c34adabc7eb57e19259.4d795553442d5553444d2d534c50",
2453
+ orderAddress: "addr1w8akt26kwj9kc2y56p8x3s9e9lp2qqtcxql0rmnz55u6lks99kkjc",
2454
+ assets: [
2455
+ "92776616f1f32c65a173392e4410a3d8c39dcf6ef768c73af164779c.4d79555344",
2456
+ "c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad.0014df105553444d"
2457
+ ],
2458
+ multiples: [1n, 1n],
2459
+ fee: 1000000n,
2460
+ adminFee: 5000000000n,
2461
+ feeDenominator: 10000000000n
2462
+ },
2463
+ {
2464
+ key: "usdc-iusd",
2465
+ lpAsset: "40b6f8a17ba5d9bab02fc776c9677212b40bfc3df77346f0b1edcba6.555344432d695553442d534c50",
2466
+ orderAddress: "addr1w86a53qhsmh0qszg486ell6nchy77yq6txksfz8p4z4r39cd4e04m",
2467
+ assets: [
2468
+ "25c5de5f5b286073c593edfd77b48abc7a48e5a4f3d4cd9d428ff935.55534443",
2469
+ "f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b69880.69555344"
2470
+ ],
2471
+ multiples: [1n, 100n],
2472
+ fee: 1000000n,
2473
+ adminFee: 5000000000n,
2474
+ feeDenominator: 10000000000n
2475
+ },
2476
+ {
2477
+ key: "usdc-iusd-0.1",
2478
+ lpAsset: "48bee898de501ff287165fdfc5be34818f3a41e474ae8f47f8c59f7a.555344432d695553442d302e312d534c50",
2479
+ orderAddress: "addr1wy42rt3rdptdaa2lwlntkx49ksuqrmqqjlu7pf5l5f8upmgj3gq2m",
2480
+ assets: [
2481
+ "25c5de5f5b286073c593edfd77b48abc7a48e5a4f3d4cd9d428ff935.55534443",
2482
+ "f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b69880.69555344"
2483
+ ],
2484
+ multiples: [1n, 100n],
2485
+ fee: 10000000n,
2486
+ adminFee: 500000000n,
2487
+ feeDenominator: 10000000000n
2488
+ },
2489
+ {
2490
+ key: "usdm-usda",
2491
+ lpAsset: "5f0d38b3eb8fea72cd3cbdaa9594a74d0db79b5a27e85be5e9015bd6.5553444d2d555344412d534c50",
2492
+ orderAddress: "addr1w8cafpjmeer4j8t8aseqayhwkf4ezuufue0clvfthxecsacv83rt0",
2493
+ assets: [
2494
+ "c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad.0014df105553444d",
2495
+ "fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456.55534441"
2496
+ ],
2497
+ multiples: [1n, 1n],
2498
+ fee: 5000000n,
2499
+ adminFee: 500000000n,
2500
+ feeDenominator: 10000000000n
2501
+ },
2502
+ {
2503
+ key: "iusd-usda",
2504
+ lpAsset: "5fd1180269cd5a01f397f37a17981424a3ec3bdab1e743a61f3bb113.695553442d555344412d534c50",
2505
+ orderAddress: "addr1w83fd654hwp5kzqkae4hqrasprq72tt4ppeghy20706jweqrcqkf3",
2506
+ assets: [
2507
+ "f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b69880.69555344",
2508
+ "fe7c786ab321f41c654ef6c1af7b3250a613c24e4213e0425a7ae456.55534441"
2509
+ ],
2510
+ multiples: [1n, 1n],
2511
+ fee: 10000000n,
2512
+ adminFee: 500000000n,
2513
+ feeDenominator: 10000000000n
2514
+ },
2515
+ {
2516
+ key: "weth-ieth",
2517
+ lpAsset: "b6b60bf469adb18c21ff3ad06bbdb9e78327b34d4c15db162de53b1c.774554482d694554482d534c50",
2518
+ orderAddress: "addr1wykr5fpg2qjca5lt75qmh9g459vnwr08wj5xlfcwyleyqagryre2v",
2519
+ assets: [
2520
+ "25c5de5f5b286073c593edfd77b48abc7a48e5a4f3d4cd9d428ff935.455448",
2521
+ "f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b69880.69455448"
2522
+ ],
2523
+ multiples: [1n, 100n],
2524
+ fee: 10000000n,
2525
+ adminFee: 500000000n,
2526
+ feeDenominator: 10000000000n
2527
+ },
2528
+ {
2529
+ key: "wbtc-ibtc",
2530
+ lpAsset: "d4e0b170fc503735b260b1a0c99223c2b4e6dd6e87ccdcabfba28b8a.774254432d694254432d534c50",
2531
+ orderAddress: "addr1wx0mfd2vxe6x80fa50fw325n2ufnaaa53xkmrnuukt5d6uqyjjvj4",
2532
+ assets: [
2533
+ "25c5de5f5b286073c593edfd77b48abc7a48e5a4f3d4cd9d428ff935.425443",
2534
+ "f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b69880.69425443"
2535
+ ],
2536
+ multiples: [1n, 100n],
2537
+ fee: 10000000n,
2538
+ adminFee: 500000000n,
2539
+ feeDenominator: 10000000000n
2540
+ },
2541
+ {
2542
+ key: "wsol-isol",
2543
+ lpAsset: "d3facc199b218a60723500bb80fcfc091f5bd67bdb74df4c099d8174.77534f4c2d69534f4c2d534c50",
2544
+ orderAddress: "addr1wxaw7dge3st4v7jreug6t5zfhqlkvsjpkddxvm6e3rcgpysxvuf5z",
2545
+ assets: [
2546
+ "25c5de5f5b286073c593edfd77b48abc7a48e5a4f3d4cd9d428ff935.534f4c",
2547
+ "f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b69880.69534f4c"
2548
+ ],
2549
+ multiples: [1n, 100n],
2550
+ fee: 10000000n,
2551
+ adminFee: 500000000n,
2552
+ feeDenominator: 10000000000n
2553
+ },
2554
+ {
2555
+ key: "usdt-iusd",
2556
+ lpAsset: "628718ccbdfaec8db0894f3ba25374374de672d487788e6158897070.555344542d695553442d534c50",
2557
+ orderAddress: "addr1wyqahucg38jpnenl8rw7rnqvyew6q8vrcshcdqfg4pehdds3f3j2h",
2558
+ assets: [
2559
+ "25c5de5f5b286073c593edfd77b48abc7a48e5a4f3d4cd9d428ff935.55534454",
2560
+ "f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b69880.69555344"
2561
+ ],
2562
+ multiples: [1n, 100n],
2563
+ fee: 10000000n,
2564
+ adminFee: 500000000n,
2565
+ feeDenominator: 10000000000n
2566
+ }
2567
+ ];
2568
+ var BY_LP_ASSET = new Map(STABLESWAP_CONFIGS.map((c) => [c.lpAsset, c]));
2569
+ function getStableswapConfigByLpAsset(lpAsset) {
2570
+ const config = BY_LP_ASSET.get(lpAsset);
2571
+ if (!config) {
2572
+ throw new Error(`No Stableswap config found for LP asset ${lpAsset}`);
2573
+ }
2574
+ return config;
2575
+ }
2576
+ var assetSchema2 = z.object({
2577
+ currencySymbol: z.string(),
2578
+ tokenName: z.string(),
2579
+ metadata: z.object({ decimals: z.number().nullish() }).nullish()
2580
+ });
2581
+ var ammPoolSchema = z.object({
2582
+ type: z.enum(["DEX", "DEX_V2", "STABLESWAP"]),
2583
+ lpAsset: assetSchema2,
2584
+ poolAssets: z.array(assetSchema2),
2585
+ utxo: z.object({
2586
+ address: z.string(),
2587
+ datum: z.string(),
2588
+ txIn: z.string(),
2589
+ // A JSON blob `{"$ledgerValue":{unit: amountString, ...}}`; V1 reserves are
2590
+ // read from it (V2/stableswap take reserves from the datum).
2591
+ value: z.string()
2592
+ })
2593
+ });
2594
+ var poolsSchema = z.array(ammPoolSchema);
2595
+ var POOL_FIELDS = `
2596
+ type
2597
+ lpAsset { currencySymbol tokenName }
2598
+ poolAssets { currencySymbol tokenName metadata { decimals } }
2599
+ utxo { address datum txIn value }
2600
+ `;
2601
+ var BY_LP_ASSETS_QUERY = `query AppliedPoolsByLpAssets($lpAssets: [InputAsset!]!) {
2602
+ appliedPoolsByLPAssets(lpAssets: $lpAssets) {${POOL_FIELDS}}
2603
+ }`;
2604
+ var BY_PAIRS_QUERY = `query AppliedPoolsByPairs($pairs: [InputPair!]!) {
2605
+ appliedPoolsByPairs(pairs: $pairs) {${POOL_FIELDS}}
2606
+ }`;
2607
+ async function getAppliedPoolsByLpAssets(client, lpAssets) {
2608
+ const pools = await client.execute({
2609
+ operation: "appliedPoolsByLPAssets",
2610
+ document: BY_LP_ASSETS_QUERY,
2611
+ variables: { lpAssets },
2612
+ schema: poolsSchema
2613
+ });
2614
+ return Promise.all(pools.map(toAppliedPool));
2615
+ }
2616
+ async function getAppliedPoolsByPairs(client, pairs) {
2617
+ const pools = await client.execute({
2618
+ operation: "appliedPoolsByPairs",
2619
+ document: BY_PAIRS_QUERY,
2620
+ variables: { pairs },
2621
+ schema: poolsSchema
2622
+ });
2623
+ return Promise.all(pools.map(toAppliedPool));
2624
+ }
2625
+ function mapVersion(type) {
2626
+ switch (type) {
2627
+ case "DEX":
2628
+ return "V1";
2629
+ case "DEX_V2":
2630
+ return "V2";
2631
+ case "STABLESWAP":
2632
+ return "STABLESWAP";
2633
+ }
2634
+ }
2635
+ function toPoolAsset(asset2) {
2636
+ return { policyId: asset2.currencySymbol, nameHex: asset2.tokenName };
2637
+ }
2638
+ function unitOf3(a) {
2639
+ return a.policyId === "" ? "lovelace" : `${a.policyId}.${a.nameHex}`;
2640
+ }
2641
+ async function toAppliedPool(pool) {
2642
+ const version = mapVersion(pool.type);
2643
+ const [rawA, rawB] = pool.poolAssets;
2644
+ if (!rawA || !rawB) {
2645
+ throw new MinswapError(
2646
+ `Pool ${pool.utxo.txIn} returned ${pool.poolAssets.length} pool assets, expected 2`,
2647
+ "PARSE_ERROR" /* PARSE_ERROR */,
2648
+ {
2649
+ endpoint: "GraphQL appliedPools",
2650
+ issues: [{ path: "poolAssets", message: `expected 2, got ${pool.poolAssets.length}` }]
2651
+ }
2652
+ );
2653
+ }
2654
+ const fields = await decodeDatumFields(pool.utxo.datum);
2655
+ let assetA;
2656
+ let assetB;
2657
+ let state;
2658
+ if (version === "V2") {
2659
+ assetA = decodeAssetNode(fields[1], "V2 pool datum", "assetA");
2660
+ assetB = decodeAssetNode(fields[2], "V2 pool datum", "assetB");
2661
+ state = decodeV2State(fields);
2662
+ } else if (version === "STABLESWAP") {
2663
+ assetA = toPoolAsset(rawA);
2664
+ assetB = toPoolAsset(rawB);
2665
+ state = decodeStableswapState(fields);
2666
+ } else {
2667
+ assetA = decodeAssetNode(fields[0], "V1 pool datum", "assetA");
2668
+ assetB = decodeAssetNode(fields[1], "V1 pool datum", "assetB");
2669
+ state = decodeV1State(fields, pool.utxo.value, assetA, assetB);
2670
+ }
2671
+ const decimals = { lovelace: 6 };
2672
+ for (const a of [rawA, rawB]) {
2673
+ const d = a.metadata?.decimals;
2674
+ if (a.currencySymbol !== "" && d != null) {
2675
+ decimals[`${a.currencySymbol}.${a.tokenName}`] = d;
2676
+ }
2677
+ }
2678
+ return {
2679
+ version,
2680
+ lpAsset: toPoolAsset(pool.lpAsset),
2681
+ assetA,
2682
+ assetB,
2683
+ state,
2684
+ utxoRef: pool.utxo.txIn,
2685
+ poolAddress: pool.utxo.address,
2686
+ rawDatum: pool.utxo.datum,
2687
+ decimals
2688
+ };
2689
+ }
2690
+ async function decodeDatumFields(datumHex) {
2691
+ const builder = await loadDexBuilder();
2692
+ const decoder = builder.PlutusData;
2693
+ const fields = decoder.fromDataHex(datumHex).fields;
2694
+ if (!fields) {
2695
+ throw parseError("pool datum", "fields", "datum is not a constructor with fields");
2696
+ }
2697
+ return fields;
2698
+ }
2699
+ function parseError(endpoint, path, message) {
2700
+ return new MinswapError(`${endpoint}: ${message}`, "PARSE_ERROR" /* PARSE_ERROR */, {
2701
+ endpoint,
2702
+ issues: [{ path, message }]
2703
+ });
2704
+ }
2705
+ function decodeAssetNode(node, endpoint, field) {
2706
+ const f = node?.fields;
2707
+ if (!f || f.length < 2 || f[0]?.bytes === void 0 || f[1]?.bytes === void 0) {
2708
+ throw parseError(endpoint, field, "expected an asset Constr[policyId, assetNameHex]");
2709
+ }
2710
+ return { policyId: f[0].bytes, nameHex: f[1].bytes };
2711
+ }
2712
+ function readInt(node, endpoint, field) {
2713
+ if (!node || node.int === void 0) {
2714
+ throw parseError(endpoint, field, "expected an integer");
2715
+ }
2716
+ return BigInt(node.int);
2717
+ }
2718
+ function requireFields(fields, min, endpoint) {
2719
+ if (fields.length < min) {
2720
+ throw parseError(endpoint, "fields", `expected >=${min}, got ${fields.length}`);
2721
+ }
2722
+ }
2723
+ function decodeV2State(fields) {
2724
+ requireFields(fields, 8, "V2 pool datum");
2725
+ return {
2726
+ kind: "V2",
2727
+ totalLiquidity: readInt(fields[3], "V2 pool datum", "totalLiquidity"),
2728
+ reserveA: readInt(fields[4], "V2 pool datum", "reserveA"),
2729
+ reserveB: readInt(fields[5], "V2 pool datum", "reserveB"),
2730
+ feeANumerator: readInt(fields[6], "V2 pool datum", "feeANumerator"),
2731
+ feeBNumerator: readInt(fields[7], "V2 pool datum", "feeBNumerator")
2732
+ };
2733
+ }
2734
+ function decodeStableswapState(fields) {
2735
+ requireFields(fields, 3, "Stableswap pool datum");
2736
+ const balancesList = fields[0]?.list;
2737
+ if (!balancesList) {
2738
+ throw parseError("Stableswap pool datum", "balances", "expected a list");
2739
+ }
2740
+ return {
2741
+ kind: "STABLESWAP",
2742
+ balances: balancesList.map((b, i) => readInt(b, "Stableswap pool datum", `balances[${i}]`)),
2743
+ totalLiquidity: readInt(fields[1], "Stableswap pool datum", "totalLiquidity"),
2744
+ amp: readInt(fields[2], "Stableswap pool datum", "amp")
2745
+ };
2746
+ }
2747
+ var ledgerValueSchema = z.object({ $ledgerValue: z.record(z.string(), z.string()) });
2748
+ function decodeV1State(fields, valueJson, assetA, assetB) {
2749
+ requireFields(fields, 3, "V1 pool datum");
2750
+ const totalLiquidity = readInt(fields[2], "V1 pool datum", "totalLiquidity");
2751
+ let parsed;
2752
+ try {
2753
+ parsed = JSON.parse(valueJson);
2754
+ } catch {
2755
+ throw parseError("V1 pool value", "value", "not valid JSON");
2756
+ }
2757
+ const ledger = ledgerValueSchema.safeParse(parsed);
2758
+ if (!ledger.success) {
2759
+ throw parseError("V1 pool value", "$ledgerValue", "missing ledger value map");
2760
+ }
2761
+ const amounts = ledger.data.$ledgerValue;
2762
+ return {
2763
+ kind: "V1",
2764
+ reserveA: BigInt(amounts[unitOf3(assetA)] ?? "0"),
2765
+ reserveB: BigInt(amounts[unitOf3(assetB)] ?? "0"),
2766
+ totalLiquidity
2767
+ };
2768
+ }
2769
+
2770
+ // src/modules/liquidity-module.ts
2771
+ var assetId = (coinId) => {
2772
+ const a = coinIdToInputAsset(coinId);
2773
+ return { policyId: a.currencySymbol, nameHex: a.tokenName };
2774
+ };
2775
+ var coinIdOf = (a) => a.policyId === "" ? "lovelace" : `${a.policyId}.${a.nameHex}`;
2776
+ var eqAsset = (x, y) => x.policyId === y.policyId && x.nameHex === y.nameHex;
2777
+ var LiquidityModule = class {
2778
+ constructor(sdk) {
2779
+ this.sdk = sdk;
2780
+ }
2781
+ sdk;
2782
+ async addLiquidity(params) {
2783
+ const applied = await this.resolvePool(params.pool);
2784
+ const walletUtxoCbors = await this.walletUtxos(params);
2785
+ const amountA = this.depositAmount(applied, params, coinIdOf(applied.assetA));
2786
+ const amountB = this.depositAmount(applied, params, coinIdOf(applied.assetB));
2787
+ const state = applied.state;
2788
+ const common = { sender: params.sender, slippage: params.slippage, walletUtxoCbors };
2789
+ if (state.kind === "V2") {
2790
+ return buildAddLiquidity2({
2791
+ ...common,
2792
+ lpAsset: applied.lpAsset,
2793
+ assetA: applied.assetA,
2794
+ assetB: applied.assetB,
2795
+ amountA,
2796
+ amountB,
2797
+ pool: state
2798
+ });
2799
+ }
2800
+ if (state.kind === "STABLESWAP") {
2801
+ const config = getStableswapConfigByLpAsset(coinIdOf(applied.lpAsset));
2802
+ return buildAddLiquidity3({
2803
+ ...common,
2804
+ lpAsset: applied.lpAsset,
2805
+ config,
2806
+ amounts: config.assets.map((coinId) => this.depositAmount(applied, params, coinId)),
2807
+ poolState: state
2808
+ });
2809
+ }
2810
+ if (amountA > 0n && amountB > 0n) {
2811
+ return buildAddLiquidity({
2812
+ ...common,
2813
+ lpAsset: applied.lpAsset,
2814
+ assetA: applied.assetA,
2815
+ assetB: applied.assetB,
2816
+ amountA,
2817
+ amountB,
2818
+ reserveA: state.reserveA,
2819
+ reserveB: state.reserveB,
2820
+ totalLiquidity: state.totalLiquidity
2821
+ });
2822
+ }
2823
+ const inIsA = amountA > 0n;
2824
+ return buildZapIn({
2825
+ ...common,
2826
+ lpAsset: applied.lpAsset,
2827
+ assetIn: inIsA ? applied.assetA : applied.assetB,
2828
+ amountIn: inIsA ? amountA : amountB,
2829
+ desiredAsset: inIsA ? applied.assetB : applied.assetA,
2830
+ reserveIn: inIsA ? state.reserveA : state.reserveB,
2831
+ reserveOut: inIsA ? state.reserveB : state.reserveA,
2832
+ totalLiquidity: state.totalLiquidity
2833
+ });
2834
+ }
2835
+ /** One-sided deposit. Routes through the version's correct on-chain step. */
2836
+ async zapIn(params) {
2837
+ return this.addLiquidity({
2838
+ pool: params.pool,
2839
+ amounts: { [params.assetIn]: params.amountIn },
2840
+ slippage: params.slippage,
2841
+ sender: params.sender,
2842
+ walletUtxoCbors: params.walletUtxoCbors,
2843
+ amountsInDecimal: params.amountInDecimal
2844
+ });
2845
+ }
2846
+ /**
2847
+ * Withdraw liquidity. Two-sided by default; pass `assetOut` to receive a single
2848
+ * coin instead (a zap-out) — the mirror of a one-sided {@link addLiquidity}.
2849
+ */
2850
+ async removeLiquidity(params) {
2851
+ const applied = await this.resolvePool(params.pool);
2852
+ const walletUtxoCbors = await this.walletUtxos(params);
2853
+ const lpAmount = BigInt(params.lpAmount);
2854
+ const state = applied.state;
2855
+ const common = { sender: params.sender, slippage: params.slippage, walletUtxoCbors };
2856
+ if (params.assetOut !== void 0) {
2857
+ return this.withdrawOneSide(applied, params.assetOut, lpAmount, common);
2858
+ }
2859
+ if (state.kind === "V2") {
2860
+ return buildRemoveLiquidity2({
2861
+ ...common,
2862
+ lpAsset: applied.lpAsset,
2863
+ lpAmount,
2864
+ pool: state
2865
+ });
2866
+ }
2867
+ if (state.kind === "STABLESWAP") {
2868
+ return buildRemoveLiquidity3({
2869
+ ...common,
2870
+ lpAsset: applied.lpAsset,
2871
+ config: getStableswapConfigByLpAsset(coinIdOf(applied.lpAsset)),
2872
+ lpAmount,
2873
+ poolState: state
2874
+ });
2875
+ }
2876
+ return buildRemoveLiquidity({
2877
+ ...common,
2878
+ lpAsset: applied.lpAsset,
2879
+ lpAmount,
2880
+ reserveA: state.reserveA,
2881
+ reserveB: state.reserveB,
2882
+ totalLiquidity: state.totalLiquidity
2883
+ });
2884
+ }
2885
+ /** One-sided withdraw. A convenience wrapper over {@link removeLiquidity} with `assetOut`. */
2886
+ async zapOut(params) {
2887
+ return this.removeLiquidity({
2888
+ pool: params.pool,
2889
+ lpAmount: params.lpAmount,
2890
+ slippage: params.slippage,
2891
+ sender: params.sender,
2892
+ walletUtxoCbors: params.walletUtxoCbors,
2893
+ assetOut: params.assetOut
2894
+ });
2895
+ }
2896
+ async withdrawOneSide(applied, assetOut, lpAmount, common) {
2897
+ const state = applied.state;
2898
+ const out = assetId(assetOut);
2899
+ if (state.kind === "STABLESWAP") {
2900
+ const config = getStableswapConfigByLpAsset(coinIdOf(applied.lpAsset));
2901
+ const assetOutIndex = config.assets.findIndex((c) => c === coinIdOf(out));
2902
+ if (assetOutIndex < 0) {
2903
+ throw notAPoolCoin(assetOut);
2904
+ }
2905
+ return buildZapOut2({
2906
+ ...common,
2907
+ lpAsset: applied.lpAsset,
2908
+ config,
2909
+ lpAmount,
2910
+ assetOutIndex,
2911
+ poolState: state
2912
+ });
2913
+ }
2914
+ if (state.kind === "V1") {
2915
+ throw new MinswapError(
2916
+ "DEX V1 has no zap-out; call removeLiquidity without assetOut for a two-sided withdraw",
2917
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
2918
+ { param: "assetOut", reason: "V1 pools do not support zap-out" }
2919
+ );
2920
+ }
2921
+ let direction2;
2922
+ if (eqAsset(out, applied.assetB)) {
2923
+ direction2 = "A_TO_B";
2924
+ } else if (eqAsset(out, applied.assetA)) {
2925
+ direction2 = "B_TO_A";
2926
+ } else {
2927
+ throw notAPoolCoin(assetOut);
2928
+ }
2929
+ return buildZapOut({
2930
+ ...common,
2931
+ lpAsset: applied.lpAsset,
2932
+ lpAmount,
2933
+ direction: direction2,
2934
+ pool: state
2935
+ });
2936
+ }
2937
+ async resolvePool(ref) {
2938
+ const client = this.sdk.clients.appGraphql;
2939
+ if (typeof ref === "string") {
2940
+ const [pool] = await getAppliedPoolsByLpAssets(client, [coinIdToInputAsset(ref)]);
2941
+ if (!pool) {
2942
+ throw poolNotFound("LP asset", ref);
2943
+ }
2944
+ return pool;
2945
+ }
2946
+ const pools = await getAppliedPoolsByPairs(client, [
2947
+ { assetA: coinIdToInputAsset(ref.assetA), assetB: coinIdToInputAsset(ref.assetB) }
2948
+ ]);
2949
+ const pairId = `${ref.assetA}/${ref.assetB}`;
2950
+ if (pools.length === 0) {
2951
+ throw poolNotFound("pair", pairId);
2952
+ }
2953
+ if (ref.version) {
2954
+ const match = pools.find((p) => p.version === ref.version);
2955
+ if (!match) {
2956
+ throw new MinswapError(
2957
+ `No ${ref.version} pool for ${pairId} (available: ${pools.map((p) => p.version).join(", ")})`,
2958
+ "NOT_FOUND" /* NOT_FOUND */,
2959
+ { resource: "pool", id: `${pairId}@${ref.version}` }
2960
+ );
2961
+ }
2962
+ return match;
2963
+ }
2964
+ if (pools.length > 1) {
2965
+ throw new MinswapError(
2966
+ `${pairId} has ${pools.length} pools (${pools.map((p) => p.version).join(", ")}); pass \`version\` or the pool's LP token to choose one`,
2967
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
2968
+ { param: "pool", reason: "ambiguous pair \u2014 multiple pools across versions" }
2969
+ );
2970
+ }
2971
+ return pools[0];
2972
+ }
2973
+ async walletUtxos(params) {
2974
+ if (params.walletUtxoCbors !== void 0) {
2975
+ return params.walletUtxoCbors;
2976
+ }
2977
+ const provider = requireRpcProvider(this.sdk.rpcProvider, "liquidity");
2978
+ const utxos = await provider.getUtxosByAddress(params.sender);
2979
+ return utxos.map((u) => u.cbor);
2980
+ }
2981
+ /** The deposit amount for `coinId` in raw base units; `0` when the coin is not supplied. */
2982
+ depositAmount(applied, params, coinId) {
2983
+ const value = params.amounts[coinId];
2984
+ if (value === void 0) {
2985
+ return 0n;
2986
+ }
2987
+ return this.toRaw(value, coinId, applied.decimals[coinId], params.amountsInDecimal);
2988
+ }
2989
+ toRaw(value, coinId, decimals, inDecimal) {
2990
+ if (!inDecimal) {
2991
+ return BigInt(value);
2992
+ }
2993
+ if (decimals === void 0) {
2994
+ throw new MinswapError(
2995
+ `Cannot convert a decimal amount for ${coinId}: its decimals are unknown`,
2996
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
2997
+ { param: "amountsInDecimal", reason: `no decimals metadata for ${coinId}` }
2998
+ );
2999
+ }
3000
+ return decimalToRaw(value, decimals);
3001
+ }
3002
+ };
3003
+ function decimalToRaw(value, decimals) {
3004
+ const text = typeof value === "string" ? value.trim() : value.toString();
3005
+ const negative = text.startsWith("-");
3006
+ const parts = (negative ? text.slice(1) : text).split(".");
3007
+ const [intPart, fracPart = ""] = parts;
3008
+ if (parts.length > 2 || !/^\d*$/.test(intPart) || !/^\d*$/.test(fracPart) || intPart + fracPart === "") {
3009
+ throw new MinswapError(`Invalid decimal amount "${text}"`, "INVALID_PARAMS" /* INVALID_PARAMS */, {
3010
+ param: "amount",
3011
+ reason: 'not a decimal number (use a string like "1.5")'
3012
+ });
3013
+ }
3014
+ if (fracPart.length > decimals) {
3015
+ throw new MinswapError(
3016
+ `Decimal amount "${text}" has more than ${decimals} decimal places`,
3017
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
3018
+ { param: "amount", reason: `too many decimal places for a ${decimals}-decimal coin` }
3019
+ );
3020
+ }
3021
+ const raw = BigInt((intPart || "0") + fracPart.padEnd(decimals, "0"));
3022
+ return negative ? -raw : raw;
3023
+ }
3024
+ function poolNotFound(kind, id) {
3025
+ return new MinswapError(`No liquidity pool found for ${kind} ${id}`, "NOT_FOUND" /* NOT_FOUND */, {
3026
+ resource: "pool",
3027
+ id
3028
+ });
3029
+ }
3030
+ function notAPoolCoin(coinId) {
3031
+ return new MinswapError(`${coinId} is not a coin of this pool`, "INVALID_PARAMS" /* INVALID_PARAMS */, {
3032
+ param: "assetOut",
3033
+ reason: "must be one of the pool's coins"
3034
+ });
3035
+ }
3036
+ var ORDER_STATUSES = ["pending", "partially_filled", "filled", "cancelled"];
3037
+ var ORDER_SOURCE_DIRECT = "direct";
3038
+ var orderExtraSchema = z.object({
3039
+ expired_at: z.string().nullish(),
3040
+ max_cancelling_tip: z.number().nullish(),
3041
+ killable: z.boolean().nullish(),
3042
+ limit_amount: z.number().nullish(),
3043
+ limit_rate: z.string().nullish(),
3044
+ stop_amount: z.number().nullish(),
3045
+ stop_loss_rate: z.string().nullish(),
3046
+ routes: arrayOf(z.string()),
3047
+ max_hop_count: z.number().nullish(),
3048
+ min_fill_amount: z.number().nullish(),
3049
+ lp_asset: z.string().nullish()
3050
+ }).transform((e) => ({
3051
+ expiredAt: e.expired_at ?? null,
3052
+ /** Already normalized to ADA by the backend, despite being a string on-chain. */
3053
+ maxCancellingTip: e.max_cancelling_tip ?? null,
3054
+ killable: e.killable ?? null,
3055
+ limitAmount: e.limit_amount ?? null,
3056
+ /** Stays a string upstream; not converted. */
3057
+ limitRate: e.limit_rate ?? null,
3058
+ stopAmount: e.stop_amount ?? null,
3059
+ stopLossRate: e.stop_loss_rate ?? null,
3060
+ routes: e.routes,
3061
+ maxHopCount: e.max_hop_count ?? null,
3062
+ minFillAmount: e.min_fill_amount ?? null,
3063
+ lpAsset: e.lp_asset ?? null
3064
+ }));
3065
+ var orderFillSchema = z.object({
3066
+ // These are dropped from the payload when zero rather than sent as 0.
3067
+ amount_a: zeroableNumber,
3068
+ amount_b: zeroableNumber,
3069
+ lp_amount: zeroableNumber,
3070
+ tx_digest: z.string(),
3071
+ block: z.number(),
3072
+ filled_at: z.number()
3073
+ }).transform((f) => ({
3074
+ amountA: f.amount_a,
3075
+ amountB: f.amount_b,
3076
+ lpAmount: f.lp_amount,
3077
+ txHash: f.tx_digest,
3078
+ block: f.block,
3079
+ /** Epoch milliseconds. */
3080
+ filledAt: f.filled_at
3081
+ }));
3082
+ var orderHopSchema = z.object({
3083
+ type: z.string(),
3084
+ order_ref: z.string(),
3085
+ protocol: z.string().nullish(),
3086
+ pool_id: z.string().nullish(),
3087
+ coin_a: coinBasicInfoSchema,
3088
+ coin_b: coinBasicInfoSchema,
3089
+ a_to_b: z.boolean().nullish(),
3090
+ amount_a: z.number(),
3091
+ amount_b: z.number(),
3092
+ lp_amount: zeroableNumber,
3093
+ batcher_fee: z.number(),
3094
+ deposit_ada: z.number(),
3095
+ extra: orderExtraSchema.nullish(),
3096
+ fills: arrayOf(orderFillSchema)
3097
+ }).transform((h) => ({
3098
+ type: h.type,
3099
+ /**
3100
+ * The hop's UTxO reference, `txHash#index`.
3101
+ *
3102
+ * This is what a cancellation targets, and it is only cancellable while
3103
+ * the hop is still pending.
3104
+ */
3105
+ orderRef: h.order_ref,
3106
+ protocol: h.protocol ?? null,
3107
+ poolId: h.pool_id ?? null,
3108
+ coinA: h.coin_a,
3109
+ coinB: h.coin_b,
3110
+ aToB: h.a_to_b ?? null,
3111
+ amountA: h.amount_a,
3112
+ amountB: h.amount_b,
3113
+ lpAmount: h.lp_amount,
3114
+ batcherFee: h.batcher_fee,
3115
+ depositAda: h.deposit_ada,
3116
+ extra: h.extra ?? null,
3117
+ fills: h.fills
3118
+ }));
3119
+ var orderSplitSchema = z.object({
3120
+ root_id: z.number(),
3121
+ status: z.string(),
3122
+ executed_at: z.number().nullish(),
3123
+ hops: arrayOf(orderHopSchema)
3124
+ }).transform((s) => ({
3125
+ rootId: s.root_id,
3126
+ status: s.status,
3127
+ executedAt: s.executed_at ?? null,
3128
+ hops: s.hops
3129
+ }));
3130
+ var orderSchema = z.object({
3131
+ tx_digest: z.string(),
3132
+ order_key: z.string(),
3133
+ source: z.string().nullish(),
3134
+ status: z.string(),
3135
+ created_at: z.number(),
3136
+ executed_at: z.number().nullish(),
3137
+ splits: arrayOf(orderSplitSchema)
3138
+ }).transform((o) => ({
3139
+ txHash: o.tx_digest,
3140
+ orderKey: o.order_key,
3141
+ /** `null` means the order was placed directly, not via an aggregator. */
3142
+ source: o.source ?? null,
3143
+ status: o.status,
3144
+ /** Epoch milliseconds. */
3145
+ createdAt: o.created_at,
3146
+ executedAt: o.executed_at ?? null,
3147
+ /**
3148
+ * An order can be split across several routes, each a chain of hops.
3149
+ * Cancellable UTxO references live on the hops.
3150
+ */
3151
+ splits: o.splits
3152
+ }));
3153
+ var OrderModule = class {
3154
+ constructor(sdk) {
3155
+ this.sdk = sdk;
3156
+ }
3157
+ sdk;
3158
+ /** Fetch an address's order history, newest first. */
3159
+ async getHistory(params) {
3160
+ assertLimit(params.limit, 50);
3161
+ const { data, pagination } = await this.sdk.clients.appApi.post({
3162
+ path: "/api/v1/order/history",
3163
+ body: {
3164
+ address: params.address,
3165
+ // The cursor is a string because order ids exceed JS-safe integers.
3166
+ pagination: { cursor: params.cursor, limit: params.limit ?? 20 },
3167
+ filters: {
3168
+ statuses: params.statuses,
3169
+ types: params.types,
3170
+ protocols: params.protocols,
3171
+ sources: params.sources,
3172
+ pool_id: params.poolId,
3173
+ time_range: params.timeRange ? [toEpochMs(params.timeRange.from), toEpochMs(params.timeRange.to)] : void 0,
3174
+ search: params.search ?? "",
3175
+ coin_ids: params.coinIds,
3176
+ tx_digest: params.txHash
3177
+ }
3178
+ },
3179
+ schema: arrayOf(orderSchema),
3180
+ currency: params.currency
3181
+ });
3182
+ return cursorPage(data, pagination?.lastCursor);
3183
+ }
3184
+ /**
3185
+ * Collect the UTxO references that can still be cancelled for an order.
3186
+ *
3187
+ * Convenience over walking `splits[].hops[]` by hand; the result feeds
3188
+ * straight into a cancellation request.
3189
+ */
3190
+ static cancellableRefs(order) {
3191
+ if (order.status === "filled" || order.status === "cancelled") {
3192
+ return [];
3193
+ }
3194
+ return order.splits.filter((split) => split.status === "pending" || split.status === "partially_filled").flatMap((split) => split.hops.map((hop) => hop.orderRef));
3195
+ }
3196
+ };
3197
+ var POOL_TYPES = [
3198
+ "cpmm",
3199
+ "clmm",
3200
+ "dlmm",
3201
+ "weighted",
3202
+ "stableswap",
3203
+ "oracle",
3204
+ "unknown"
3205
+ ];
3206
+ var POOL_FILTER_TYPES = POOL_TYPES.filter((t) => t !== "unknown");
3207
+ var POOL_CATEGORY_GROUPS = ["stablecoin", "correlated", "meme", "other"];
3208
+ var POOL_LIST_SORT_FIELDS = [
3209
+ "created_at",
3210
+ "tvl_usd",
3211
+ "apr_24h",
3212
+ "apr_1w",
3213
+ "apr_30d",
3214
+ "fee_24h",
3215
+ "fee_1w",
3216
+ "fee_30d",
3217
+ "volume_24h",
3218
+ "volume_1w",
3219
+ "volume_30d",
3220
+ "total_apr_24h",
3221
+ "total_apr_1w",
3222
+ "total_apr_30d"
3223
+ ];
3224
+ var POOL_EVENT_ACTIONS = ["buy", "sell", "addLiquidity", "removeLiquidity"];
3225
+ var ACTION_TO_WIRE = {
3226
+ buy: "buy",
3227
+ sell: "sell",
3228
+ addLiquidity: "join",
3229
+ removeLiquidity: "exit"
3230
+ };
3231
+ var ACTION_FROM_WIRE = {
3232
+ buy: "buy",
3233
+ sell: "sell",
3234
+ join: "addLiquidity",
3235
+ exit: "removeLiquidity"
3236
+ };
3237
+ var poolSchema = z.object({
3238
+ pool_id: z.string().nullish(),
3239
+ protocol: z.string(),
3240
+ coin_a: poolCoinSchema,
3241
+ coin_b: poolCoinSchema,
3242
+ amount_a: z.number(),
3243
+ amount_b: z.number(),
3244
+ amount_a_usd: z.number(),
3245
+ amount_b_usd: z.number(),
3246
+ price_a: z.number(),
3247
+ price_b: z.number(),
3248
+ price_ab: z.number(),
3249
+ tvl_usd: z.number(),
3250
+ tvl_usd_24h_change: z.number(),
3251
+ fee_rate: z.number(),
3252
+ fee_rate_a: z.number().nullish(),
3253
+ fee_rate_b: z.number().nullish(),
3254
+ fee_24h: z.number(),
3255
+ fee_24h_change: z.number(),
3256
+ fee_1w: z.number(),
3257
+ fee_30d: z.number().nullish(),
3258
+ fee_a_24h: poolFeeSchema,
3259
+ fee_b_24h: poolFeeSchema,
3260
+ fee_a_1w: poolFeeSchema,
3261
+ fee_b_1w: poolFeeSchema,
3262
+ fee_a_30d: poolFeeSchema.nullish(),
3263
+ fee_b_30d: poolFeeSchema.nullish(),
3264
+ volume_24h: z.number(),
3265
+ volume_24h_change: z.number(),
3266
+ volume_1w: z.number(),
3267
+ volume_30d: z.number().nullish(),
3268
+ fee_apr_24h: z.number(),
3269
+ fee_apr_1w: z.number(),
3270
+ fee_apr_30d: z.number().nullish(),
3271
+ rewards: arrayOf(poolRewardSchema),
3272
+ active: z.boolean(),
3273
+ created_at: z.string().nullish(),
3274
+ pool_type: z.string()
3275
+ }).transform((p) => ({
3276
+ poolId: p.pool_id ?? null,
3277
+ protocol: p.protocol,
3278
+ coinA: p.coin_a,
3279
+ coinB: p.coin_b,
3280
+ amountA: p.amount_a,
3281
+ amountB: p.amount_b,
3282
+ amountAUsd: p.amount_a_usd,
3283
+ amountBUsd: p.amount_b_usd,
3284
+ priceA: p.price_a,
3285
+ priceB: p.price_b,
3286
+ priceAB: p.price_ab,
3287
+ tvlUsd: p.tvl_usd,
3288
+ tvlUsd24hChange: p.tvl_usd_24h_change,
3289
+ feeRate: p.fee_rate,
3290
+ /** Per-side fee rates exist only on protocols that support them. */
3291
+ feeRateA: p.fee_rate_a ?? null,
3292
+ feeRateB: p.fee_rate_b ?? null,
3293
+ fee24h: p.fee_24h,
3294
+ fee24hChange: p.fee_24h_change,
3295
+ fee1w: p.fee_1w,
3296
+ /** 30-day windows are Cardano-only and absent elsewhere. */
3297
+ fee30d: p.fee_30d ?? null,
3298
+ feeA24h: p.fee_a_24h,
3299
+ feeB24h: p.fee_b_24h,
3300
+ feeA1w: p.fee_a_1w,
3301
+ feeB1w: p.fee_b_1w,
3302
+ feeA30d: p.fee_a_30d ?? null,
3303
+ feeB30d: p.fee_b_30d ?? null,
3304
+ volume24h: p.volume_24h,
3305
+ volume24hChange: p.volume_24h_change,
3306
+ volume1w: p.volume_1w,
3307
+ volume30d: p.volume_30d ?? null,
3308
+ feeApr24h: p.fee_apr_24h,
3309
+ feeApr1w: p.fee_apr_1w,
3310
+ feeApr30d: p.fee_apr_30d ?? null,
3311
+ rewards: p.rewards,
3312
+ active: p.active,
3313
+ createdAt: p.created_at ?? null,
3314
+ poolType: p.pool_type
3315
+ }));
3316
+ var poolEventSchema = z.object({
3317
+ id: z.string(),
3318
+ pool_id: z.string().nullish(),
3319
+ coin_a: coinBasicInfoSchema,
3320
+ coin_b: coinBasicInfoSchema,
3321
+ amount_a: z.string(),
3322
+ amount_b: z.string(),
3323
+ amount_a_usd: z.string(),
3324
+ amount_b_usd: z.string(),
3325
+ sender: z.string().nullish(),
3326
+ tx_digest: z.string(),
3327
+ created_at: z.string(),
3328
+ action: z.string()
3329
+ }).transform((e) => ({
3330
+ id: e.id,
3331
+ poolId: e.pool_id ?? null,
3332
+ coinA: e.coin_a,
3333
+ coinB: e.coin_b,
3334
+ /** Decimal strings, not numbers — parse with a bignum library. */
3335
+ amountA: e.amount_a,
3336
+ amountB: e.amount_b,
3337
+ amountAUsd: e.amount_a_usd,
3338
+ amountBUsd: e.amount_b_usd,
3339
+ sender: e.sender ?? null,
3340
+ txHash: e.tx_digest,
3341
+ createdAt: e.created_at,
3342
+ action: ACTION_FROM_WIRE[e.action] ?? e.action
3343
+ }));
3344
+ var POOL_IDS_CHUNK = 20;
3345
+ var PoolModule = class {
3346
+ constructor(sdk) {
3347
+ this.sdk = sdk;
3348
+ }
3349
+ sdk;
3350
+ /** List pools with filtering and sorting. */
3351
+ async list(params = {}) {
3352
+ assertLimit(params.limit, 50);
3353
+ const limit = params.limit ?? 20;
3354
+ const offset = decodeOffsetCursor(params.cursor);
3355
+ if (params.coinIds && params.coinIds.length > 2) {
3356
+ throw new MinswapError(
3357
+ "coinIds accepts at most 2 entries \u2014 it filters for pools containing those coins, it is not a bulk lookup. Use getByIds for that.",
3358
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
3359
+ { param: "coinIds", reason: "at most 2 entries" }
3360
+ );
3361
+ }
3362
+ const { data } = await this.sdk.clients.appApi.get({
3363
+ path: "/api/v1/pool/liquidity-pools",
3364
+ query: {
3365
+ offset,
3366
+ limit,
3367
+ pool_addresses: params.poolIds,
3368
+ protocols: params.protocols,
3369
+ coin_ids: params.coinIds,
3370
+ tvl_min: params.minTvl,
3371
+ pool_type: params.poolType,
3372
+ category_group: params.categoryGroup,
3373
+ is_verified: params.isVerified,
3374
+ search: params.search,
3375
+ sort_field: params.sortBy ?? "volume_24h",
3376
+ sort_direction: params.order ?? "desc"
3377
+ },
3378
+ schema: arrayOf(poolSchema),
3379
+ currency: params.currency
3380
+ });
3381
+ return offsetPage(data, offset, limit);
3382
+ }
3383
+ /**
3384
+ * Fetch a single pool.
3385
+ *
3386
+ * @throws {MinswapError} with code `NOT_FOUND` when no such pool exists.
3387
+ */
3388
+ async getById(poolId, options = {}) {
3389
+ const { data } = await this.sdk.clients.appApi.get({
3390
+ path: "/api/v1/pool/detail",
3391
+ query: { pool_id: poolId },
3392
+ schema: poolSchema,
3393
+ currency: options.currency,
3394
+ notFound: { resource: "pool", id: poolId }
3395
+ });
3396
+ return data;
3397
+ }
3398
+ /**
3399
+ * Fetch several pools by id.
3400
+ *
3401
+ * Chunked at 20, the upstream cap on `pool_addresses`. Ids that do not match
3402
+ * a pool are absent from the result rather than raising.
3403
+ */
3404
+ async getByIds(poolIds, options = {}) {
3405
+ if (poolIds.length === 0) {
3406
+ return [];
3407
+ }
3408
+ const chunks = [];
3409
+ for (let i = 0; i < poolIds.length; i += POOL_IDS_CHUNK) {
3410
+ chunks.push(poolIds.slice(i, i + POOL_IDS_CHUNK));
3411
+ }
3412
+ const pages = await Promise.all(
3413
+ chunks.map(
3414
+ (chunk) => this.sdk.clients.appApi.get({
3415
+ path: "/api/v1/pool/liquidity-pools",
3416
+ query: { pool_addresses: chunk, offset: 0, limit: chunk.length },
3417
+ schema: arrayOf(poolSchema),
3418
+ currency: options.currency
3419
+ })
3420
+ )
3421
+ );
3422
+ return pages.flatMap((page) => page.data);
3423
+ }
3424
+ /** OHLC candles for a pool. */
3425
+ async getOhlc(params) {
3426
+ assertBucket(params.bucketMinutes);
3427
+ assertLimit(params.limit, 500);
3428
+ const { data } = await this.sdk.clients.appApi.get({
3429
+ path: "/api/v1/pool/ohlcv",
3430
+ query: {
3431
+ pool_id: params.poolId,
3432
+ // Epoch seconds here; the token chart wants RFC 3339 instead.
3433
+ from: params.from ? toEpochSeconds(params.from) : void 0,
3434
+ to: params.to ? toEpochSeconds(params.to) : void 0,
3435
+ bucket: params.bucketMinutes,
3436
+ limit: params.limit
3437
+ },
3438
+ schema: arrayOf(poolCandleSchema),
3439
+ currency: params.currency
3440
+ });
3441
+ return data;
3442
+ }
3443
+ /** Swaps and liquidity changes for a pool. */
3444
+ async getEvents(params) {
3445
+ assertLimit(params.limit, 50);
3446
+ const { data, pagination } = await this.sdk.clients.appApi.get({
3447
+ path: "/api/v1/pool/events",
3448
+ query: {
3449
+ pool_id: params.poolId,
3450
+ cursor: params.cursor,
3451
+ limit: params.limit ?? 20,
3452
+ desc: params.descending ?? true,
3453
+ action: params.actions?.map((a) => ACTION_TO_WIRE[a]),
3454
+ address: params.address,
3455
+ time_range: params.timeRange ? [toEpochMs(params.timeRange.from), toEpochMs(params.timeRange.to)] : void 0
3456
+ },
3457
+ schema: arrayOf(poolEventSchema),
3458
+ currency: params.currency
3459
+ });
3460
+ return cursorPage(data, pagination?.lastCursor);
3461
+ }
3462
+ };
3463
+ var poolInfoSchema = z.object({
3464
+ fee_apr_24h: z.number(),
3465
+ fee_apr_1w: z.number(),
3466
+ rewards: arrayOf(poolRewardSchema)
3467
+ }).transform((p) => ({
3468
+ feeApr24h: p.fee_apr_24h,
3469
+ feeApr1w: p.fee_apr_1w,
3470
+ rewards: p.rewards
3471
+ }));
3472
+ var lpPositionSchema = z.object({
3473
+ protocol: z.string(),
3474
+ coin_a: coinBasicInfoSchema,
3475
+ coin_b: coinBasicInfoSchema,
3476
+ amount_a: z.number(),
3477
+ amount_b: z.number(),
3478
+ lp_amount: z.string().nullish(),
3479
+ pool_share: z.number(),
3480
+ a_usd_value: z.number(),
3481
+ b_usd_value: z.number(),
3482
+ position_id: z.string(),
3483
+ pool_id: z.string().nullish(),
3484
+ fee_rate: z.number(),
3485
+ fee_rate_a: z.number().nullish(),
3486
+ fee_rate_b: z.number().nullish(),
3487
+ pool_type: z.string(),
3488
+ pool_info: poolInfoSchema,
3489
+ total_usd_value: z.number()
3490
+ }).transform((p) => ({
3491
+ protocol: p.protocol,
3492
+ coinA: p.coin_a,
3493
+ coinB: p.coin_b,
3494
+ amountA: p.amount_a,
3495
+ amountB: p.amount_b,
3496
+ /** Decimal string, not a number. Empty for Minswap CPMM positions. */
3497
+ lpAmount: p.lp_amount ?? null,
3498
+ poolShare: p.pool_share,
3499
+ aUsdValue: p.a_usd_value,
3500
+ bUsdValue: p.b_usd_value,
3501
+ positionId: p.position_id,
3502
+ poolId: p.pool_id ?? null,
3503
+ feeRate: p.fee_rate,
3504
+ feeRateA: p.fee_rate_a ?? null,
3505
+ feeRateB: p.fee_rate_b ?? null,
3506
+ poolType: p.pool_type,
3507
+ poolInfo: p.pool_info,
3508
+ totalUsdValue: p.total_usd_value
3509
+ }));
3510
+ var vaultPairPositionSchema = z.object({
3511
+ protocol: z.string(),
3512
+ coin_a: coinBasicInfoSchema,
3513
+ coin_b: coinBasicInfoSchema,
3514
+ amount_a: z.number(),
3515
+ amount_b: z.number(),
3516
+ a_usd_value: z.number(),
3517
+ b_usd_value: z.number(),
3518
+ pool_id: z.string().nullish(),
3519
+ share: z.number(),
3520
+ total_usd_value: z.number()
3521
+ }).transform((p) => ({
3522
+ protocol: p.protocol,
3523
+ coinA: p.coin_a,
3524
+ coinB: p.coin_b,
3525
+ amountA: p.amount_a,
3526
+ amountB: p.amount_b,
3527
+ aUsdValue: p.a_usd_value,
3528
+ bUsdValue: p.b_usd_value,
3529
+ poolId: p.pool_id ?? null,
3530
+ share: p.share,
3531
+ totalUsdValue: p.total_usd_value
3532
+ }));
3533
+ var farmExtraSchema = z.object({
3534
+ script_version: z.string(),
3535
+ lb_whitelist_assets: arrayOf(z.string())
3536
+ }).transform((e) => ({
3537
+ scriptVersion: e.script_version,
3538
+ lbWhitelistAssets: e.lb_whitelist_assets
3539
+ }));
3540
+ var farmPositionSchema = z.object({
3541
+ protocol: z.string(),
3542
+ staked_coin: coinAmountInfoSchema.nullish(),
3543
+ yield_position_id: z.string(),
3544
+ yield_pool_id: z.string(),
3545
+ yield_pending_rewards: arrayOf(coinAmountInfoSchema),
3546
+ cpmm_position: lpPositionSchema.nullish(),
3547
+ vault_pair_position: vaultPairPositionSchema.nullish(),
3548
+ total_usd_value: z.number(),
3549
+ extra: farmExtraSchema.nullish()
3550
+ }).transform((p) => ({
3551
+ protocol: p.protocol,
3552
+ /**
3553
+ * The staked principal. Exactly one of `stakedCoin`, `lpPosition`, or
3554
+ * `vaultPairPosition` describes it, checked in that precedence order
3555
+ * upstream.
3556
+ */
3557
+ stakedCoin: p.staked_coin ?? null,
3558
+ positionId: p.yield_position_id,
3559
+ /** Identifies the farm; pass to farm actions. */
3560
+ poolId: p.yield_pool_id,
3561
+ pendingRewards: p.yield_pending_rewards,
3562
+ lpPosition: p.cpmm_position ?? null,
3563
+ vaultPairPosition: p.vault_pair_position ?? null,
3564
+ totalUsdValue: p.total_usd_value,
3565
+ extra: p.extra ?? null
3566
+ }));
3567
+ var stakingExtraSchema = z.object({
3568
+ script_version: z.string(),
3569
+ stake_at: z.string(),
3570
+ duration: z.number().nullish(),
3571
+ multiplier: z.number().nullish()
3572
+ }).transform((e) => ({
3573
+ scriptVersion: e.script_version,
3574
+ /** ISO-8601 string, not an epoch number. */
3575
+ stakeAt: e.stake_at,
3576
+ duration: e.duration ?? null,
3577
+ multiplier: e.multiplier ?? null
3578
+ }));
3579
+ var stakingPositionSchema = z.object({
3580
+ protocol: z.string(),
3581
+ staked_coin: coinAmountInfoSchema.nullish(),
3582
+ staking_position_id: z.string(),
3583
+ staking_pool_id: z.string(),
3584
+ staking_pending_rewards: arrayOf(coinAmountInfoSchema),
3585
+ share: z.number(),
3586
+ total_usd_value: z.number(),
3587
+ extra: stakingExtraSchema.nullish()
3588
+ }).transform((p) => ({
3589
+ protocol: p.protocol,
3590
+ stakedCoin: p.staked_coin ?? null,
3591
+ /** The staking UTxO reference; staking actions take this directly. */
3592
+ positionId: p.staking_position_id,
3593
+ poolId: p.staking_pool_id,
3594
+ pendingRewards: p.staking_pending_rewards,
3595
+ /**
3596
+ * Share of the pool. Note `0` is ambiguous upstream between "no share" and
3597
+ * "could not be computed".
3598
+ */
3599
+ share: p.share,
3600
+ totalUsdValue: p.total_usd_value,
3601
+ extra: p.extra ?? null,
3602
+ /**
3603
+ * Tiered or flexible staking.
3604
+ *
3605
+ * The backend encodes this two ways and names it neither: flexible pools
3606
+ * carry a `-flexible` suffix on the pool id, and only tiered positions
3607
+ * carry `duration`/`multiplier`. Both signals are checked here so callers
3608
+ * never parse a string suffix.
3609
+ */
3610
+ kind: p.staking_pool_id.endsWith("-flexible") || p.extra?.duration === null || p.extra?.duration === void 0 ? "flexible" : "tiered"
3611
+ }));
3612
+ var minswapPortfolioSchema = z.object({
3613
+ project_code: z.string(),
3614
+ project_name: z.string(),
3615
+ project_description: z.string(),
3616
+ website_url: z.string(),
3617
+ icon_url: z.string(),
3618
+ cpmm: arrayOf(lpPositionSchema),
3619
+ yields: arrayOf(farmPositionSchema),
3620
+ stakings: arrayOf(stakingPositionSchema),
3621
+ total_usd_value: z.number()
3622
+ }).transform((m) => ({
3623
+ projectCode: m.project_code,
3624
+ projectName: m.project_name,
3625
+ projectDescription: m.project_description,
3626
+ websiteUrl: m.website_url,
3627
+ iconUrl: m.icon_url,
3628
+ lp: m.cpmm,
3629
+ farms: m.yields,
3630
+ stakings: m.stakings,
3631
+ totalUsdValue: m.total_usd_value
3632
+ }));
3633
+ var portfolioDefiSchema = z.object({
3634
+ minswap: minswapPortfolioSchema.nullish(),
3635
+ total_usd_value: z.number()
3636
+ }).transform((d) => ({
3637
+ /** Absent upstream when the wallet holds no Minswap position at all. */
3638
+ lp: d.minswap?.lp ?? [],
3639
+ farms: d.minswap?.farms ?? [],
3640
+ stakings: d.minswap?.stakings ?? [],
3641
+ project: d.minswap ? {
3642
+ code: d.minswap.projectCode,
3643
+ name: d.minswap.projectName,
3644
+ description: d.minswap.projectDescription,
3645
+ websiteUrl: d.minswap.websiteUrl,
3646
+ iconUrl: d.minswap.iconUrl
3647
+ } : null,
3648
+ minswapUsdValue: d.minswap?.totalUsdValue ?? 0,
3649
+ totalUsdValue: d.total_usd_value
3650
+ }));
3651
+ var PortfolioModule = class {
3652
+ constructor(sdk) {
3653
+ this.sdk = sdk;
3654
+ }
3655
+ sdk;
3656
+ /**
3657
+ * Fetch every Minswap position held by an address.
3658
+ *
3659
+ * @param address A Cardano payment or stake address.
3660
+ */
3661
+ async getDefi(address, options = {}) {
3662
+ const { data } = await this.sdk.clients.appApi.get({
3663
+ path: "/api/v1/portfolio/defi",
3664
+ query: {
3665
+ address,
3666
+ // Required by the backend, and `minswap` is the only value it accepts
3667
+ // on Cardano.
3668
+ protocols: "minswap",
3669
+ direct_address: options.directAddress
3670
+ },
3671
+ schema: portfolioDefiSchema,
3672
+ currency: options.currency
3673
+ });
3674
+ return data;
3675
+ }
3676
+ };
3677
+ var stakingPoolSchema = z.object({
3678
+ pool_id: z.string(),
3679
+ coin: poolCoinSchema,
3680
+ amount_locked: z.number(),
3681
+ amount_locked_usd: z.number(),
3682
+ active: z.boolean(),
3683
+ rewards: arrayOf(poolRewardSchema),
3684
+ staked_share: z.number(),
3685
+ stakers: z.number(),
3686
+ positions: z.number()
3687
+ }).transform((p) => ({
3688
+ poolId: p.pool_id,
3689
+ coin: p.coin,
3690
+ amountLocked: p.amount_locked,
3691
+ amountLockedUsd: p.amount_locked_usd,
3692
+ active: p.active,
3693
+ rewards: p.rewards,
3694
+ stakedShare: p.staked_share,
3695
+ stakers: p.stakers,
3696
+ positions: p.positions,
3697
+ /**
3698
+ * Tiered (locked) or flexible staking.
3699
+ *
3700
+ * The backend encodes this only as a `-flexible` suffix on the pool id; the
3701
+ * SDK surfaces it as an explicit field so `stake`/`unstake` can route
3702
+ * without the caller parsing the id.
3703
+ */
3704
+ kind: p.pool_id.endsWith("-flexible") ? "flexible" : "tiered"
3705
+ }));
3706
+ var stakingListSchema = z.object({
3707
+ pools: arrayOf(stakingPoolSchema),
3708
+ total_amount_locked: z.number(),
3709
+ total_amount_locked_usd: z.number(),
3710
+ total_rewards: arrayOf(poolRewardSchema),
3711
+ total_stakers: z.number()
3712
+ }).transform((r) => ({
3713
+ pools: r.pools,
3714
+ totalAmountLocked: r.total_amount_locked,
3715
+ totalAmountLockedUsd: r.total_amount_locked_usd,
3716
+ totalRewards: r.total_rewards,
3717
+ totalStakers: r.total_stakers
3718
+ }));
3719
+ var rewardAmountSchema = arrayOf(
3720
+ z.object({
3721
+ asset: z.object({ currencySymbol: z.string(), tokenName: z.string() }),
3722
+ reward: z.union([z.string(), z.number()])
3723
+ }).transform((r) => ({
3724
+ coinId: inputAssetToCoinId(r.asset),
3725
+ amount: String(r.reward)
3726
+ }))
3727
+ );
3728
+ var StakingModule = class {
3729
+ constructor(sdk) {
3730
+ this.sdk = sdk;
3731
+ }
3732
+ sdk;
3733
+ /**
3734
+ * All MIN staking pools, tiered and flexible, with totals.
3735
+ *
3736
+ * The endpoint takes no parameters — it returns the full set.
3737
+ */
3738
+ async list(options = {}) {
3739
+ const { data } = await this.sdk.clients.appApi.get({
3740
+ path: "/api/v1/pool/ms-staking-pools",
3741
+ schema: stakingListSchema,
3742
+ currency: options.currency
3743
+ });
3744
+ return data;
3745
+ }
3746
+ /** An address's staking positions. Sourced from the DeFi portfolio. */
3747
+ async getPositions(address) {
3748
+ const { stakings } = await this.sdk.portfolio.getDefi(address);
3749
+ return stakings;
3750
+ }
3751
+ /** Stake MIN into the tiered or flexible contract. */
3752
+ async stake(params) {
3753
+ const inputs = await resolveWalletInputs(this.sdk, "staking.stake", params, false);
3754
+ const operation = params.kind === "tiered" ? "minBarBuildTieredStakeTx" : "minBarBuildLiquidStakeTx";
3755
+ const document = params.kind === "tiered" ? DOC_TIERED_STAKE : DOC_LIQUID_STAKE;
3756
+ const cbor = await this.sdk.clients.keyAppApi.execute({
3757
+ operation,
3758
+ document,
3759
+ variables: {
3760
+ options: {
3761
+ owner: params.address,
3762
+ asset: coinIdToInputAsset(params.coinId),
3763
+ amount: toBigIntString(params.amount),
3764
+ inputsToChoose: inputs.inputsToChoose
3765
+ }
3766
+ },
3767
+ schema: z.string()
3768
+ });
3769
+ return { cbor };
3770
+ }
3771
+ /**
3772
+ * Unstake a position in full.
3773
+ *
3774
+ * A tiered unstake additionally reports the rewards it releases. For a partial
3775
+ * flexible withdrawal, this is not the method — unstake takes the whole
3776
+ * position.
3777
+ */
3778
+ async unstake(params) {
3779
+ const inputs = await resolveWalletInputs(this.sdk, "staking.unstake", params, false);
3780
+ if (params.kind === "tiered") {
3781
+ const result = await this.sdk.clients.keyAppApi.execute({
3782
+ operation: "minBarBuildTieredUnstakeTx",
3783
+ document: DOC_TIERED_UNSTAKE,
3784
+ variables: {
3785
+ options: {
3786
+ unstakeUtxoTxIn: params.positionId,
3787
+ inputsToChoose: inputs.inputsToChoose
3788
+ }
3789
+ },
3790
+ schema: z.object({ tx: z.string(), pendingRewards: rewardAmountSchema }).transform((r) => ({ cbor: r.tx, pendingRewards: r.pendingRewards }))
3791
+ });
3792
+ return result;
3793
+ }
3794
+ const cbor = await this.sdk.clients.keyAppApi.execute({
3795
+ operation: "minBarBuildLiquidUnstakeTx",
3796
+ document: DOC_LIQUID_UNSTAKE,
3797
+ variables: {
3798
+ options: {
3799
+ unstakeUtxoTxIn: params.positionId,
3800
+ inputsToChoose: inputs.inputsToChoose
3801
+ }
3802
+ },
3803
+ schema: z.string()
3804
+ });
3805
+ return { cbor, pendingRewards: null };
3806
+ }
3807
+ };
3808
+ var DOC_TIERED_STAKE = `mutation MinBarBuildTieredStakeTx($options: MinBarBuildTieredStakeOptions!) {
3809
+ minBarBuildTieredStakeTx(options: $options)
3810
+ }`;
3811
+ var DOC_LIQUID_STAKE = `mutation MinBarBuildLiquidStakeTx($options: MinBarBuildLiquidStakeOptions!) {
3812
+ minBarBuildLiquidStakeTx(options: $options)
3813
+ }`;
3814
+ var DOC_TIERED_UNSTAKE = `mutation MinBarBuildTieredUnstakeTx($options: MinBarBuildTieredUnstakeOptions!) {
3815
+ minBarBuildTieredUnstakeTx(options: $options) {
3816
+ tx
3817
+ pendingRewards {
3818
+ asset {
3819
+ currencySymbol
3820
+ tokenName
3821
+ }
3822
+ reward
3823
+ }
3824
+ }
3825
+ }`;
3826
+ var DOC_LIQUID_UNSTAKE = `mutation MinBarBuildLiquidUnstakeTx($options: MinBarBuildLiquidUnstakeOptions!) {
3827
+ minBarBuildLiquidUnstakeTx(options: $options)
3828
+ }`;
3829
+ var amountString = z.string();
3830
+ var coinSchema = z.object({
3831
+ coin_type: z.string().nullish(),
3832
+ symbol: z.string(),
3833
+ name: z.string(),
3834
+ logo: z.string(),
3835
+ description: z.string(),
3836
+ liquidity: amountString,
3837
+ market_cap: amountString,
3838
+ fdv: amountString,
3839
+ circulating_supply: amountString,
3840
+ total_supply: amountString,
3841
+ holders: z.number(),
3842
+ creator: z.string(),
3843
+ published_at: z.string(),
3844
+ first_trade_at: z.string().nullish(),
3845
+ verified: z.boolean(),
3846
+ decimals: z.number(),
3847
+ category: z.string().nullish(),
3848
+ scam_label: z.string().nullish()
3849
+ }).transform((c) => ({
3850
+ coinId: c.coin_type ?? null,
3851
+ symbol: c.symbol,
3852
+ name: c.name,
3853
+ logo: c.logo,
3854
+ description: c.description,
3855
+ liquidity: c.liquidity,
3856
+ marketCap: c.market_cap,
3857
+ fdv: c.fdv,
3858
+ circulatingSupply: c.circulating_supply,
3859
+ totalSupply: c.total_supply,
3860
+ holders: c.holders,
3861
+ creator: c.creator,
3862
+ publishedAt: c.published_at,
3863
+ firstTradeAt: c.first_trade_at ?? null,
3864
+ verified: c.verified,
3865
+ decimals: c.decimals,
3866
+ category: c.category ?? null,
3867
+ scamLabel: c.scam_label ?? null
3868
+ }));
3869
+ var priceSchema = z.object({
3870
+ price: z.number(),
3871
+ price_change_1h: z.number(),
3872
+ price_change_6h: z.number(),
3873
+ price_change_1d: z.number(),
3874
+ price_change_7d: z.number(),
3875
+ price_change_30d: z.number(),
3876
+ price_24h_low: z.number(),
3877
+ price_24h_high: z.number(),
3878
+ ath: z.number(),
3879
+ atl: z.number()
3880
+ }).transform((p) => ({
3881
+ price: p.price,
3882
+ priceChange1h: p.price_change_1h,
3883
+ priceChange6h: p.price_change_6h,
3884
+ priceChange1d: p.price_change_1d,
3885
+ priceChange7d: p.price_change_7d,
3886
+ priceChange30d: p.price_change_30d,
3887
+ price24hLow: p.price_24h_low,
3888
+ price24hHigh: p.price_24h_high,
3889
+ ath: p.ath,
3890
+ atl: p.atl
3891
+ }));
3892
+ var volumeSchema = z.object({
3893
+ vol_buy_1h: z.number(),
3894
+ vol_sell_1h: z.number(),
3895
+ vol_buy_24h: z.number(),
3896
+ vol_sell_24h: z.number(),
3897
+ vol_buy_1w: z.number(),
3898
+ vol_sell_1w: z.number()
3899
+ }).transform((v) => ({
3900
+ volBuy1h: v.vol_buy_1h,
3901
+ volSell1h: v.vol_sell_1h,
3902
+ volBuy24h: v.vol_buy_24h,
3903
+ volSell24h: v.vol_sell_24h,
3904
+ volBuy1w: v.vol_buy_1w,
3905
+ volSell1w: v.vol_sell_1w
3906
+ }));
3907
+ var socialMediaSchema = z.object({
3908
+ x: z.string().nullish(),
3909
+ telegram: z.string().nullish(),
3910
+ website: z.string().nullish(),
3911
+ github: z.string().nullish(),
3912
+ discord: z.string().nullish(),
3913
+ facebook: z.string().nullish(),
3914
+ coingecko_url: z.string().nullish(),
3915
+ coinmarketcap_url: z.string().nullish(),
3916
+ banner_url: z.string().nullish(),
3917
+ docs: z.string().nullish()
3918
+ }).transform((s) => ({
3919
+ x: s.x ?? null,
3920
+ telegram: s.telegram ?? null,
3921
+ website: s.website ?? null,
3922
+ github: s.github ?? null,
3923
+ discord: s.discord ?? null,
3924
+ facebook: s.facebook ?? null,
3925
+ coingeckoUrl: s.coingecko_url ?? null,
3926
+ coinmarketcapUrl: s.coinmarketcap_url ?? null,
3927
+ bannerUrl: s.banner_url ?? null,
3928
+ docs: s.docs ?? null
3929
+ }));
3930
+ var securitySchema = z.object({
3931
+ mintable: z.boolean().nullish(),
3932
+ blacklist: z.boolean().nullish(),
3933
+ top_10_holders: z.number().nullish()
3934
+ }).transform((s) => ({
3935
+ mintable: s.mintable ?? null,
3936
+ blacklist: s.blacklist ?? null,
3937
+ /** Share of supply held by the top 10 holders. */
3938
+ top10Holders: s.top_10_holders ?? null
3939
+ }));
3940
+ var tagSchema = z.object({ id: z.number(), name: z.string() });
3941
+ var tokenDetailSchema = z.object({
3942
+ coin: coinSchema,
3943
+ price_change: priceSchema,
3944
+ volume: volumeSchema.nullish(),
3945
+ social_media: socialMediaSchema,
3946
+ tags: arrayOf(tagSchema),
3947
+ rank: z.number().nullish(),
3948
+ security: securitySchema.nullish(),
3949
+ suspicious_labels: arrayOf(z.string())
3950
+ }).transform((d) => ({
3951
+ coin: d.coin,
3952
+ /** Spot price plus change/high/low windows. Named `price_change` upstream. */
3953
+ price: d.price_change,
3954
+ volume: d.volume ?? null,
3955
+ socialMedia: d.social_media,
3956
+ tags: d.tags,
3957
+ rank: d.rank ?? null,
3958
+ security: d.security ?? null,
3959
+ suspiciousLabels: d.suspicious_labels
3960
+ }));
3961
+ var tokenSummarySchema = z.object({
3962
+ coin_type: z.string().nullish(),
3963
+ name: z.string(),
3964
+ symbol: z.string(),
3965
+ icon_url: z.string(),
3966
+ website: z.string(),
3967
+ description: z.string(),
3968
+ decimal: z.number(),
3969
+ verified: z.boolean(),
3970
+ first_trade_at: z.string().nullish(),
3971
+ volume_24h: z.number().nullish(),
3972
+ price: z.number().nullish(),
3973
+ price_change_1d: z.number().nullish(),
3974
+ price_change_7d: z.number().nullish(),
3975
+ price_change_30d: z.number().nullish(),
3976
+ published_at: z.string()
3977
+ }).transform((t) => ({
3978
+ coinId: t.coin_type ?? null,
3979
+ name: t.name,
3980
+ symbol: t.symbol,
3981
+ iconUrl: t.icon_url,
3982
+ website: t.website,
3983
+ description: t.description,
3984
+ decimals: t.decimal,
3985
+ verified: t.verified,
3986
+ firstTradeAt: t.first_trade_at ?? null,
3987
+ volume24h: t.volume_24h ?? null,
3988
+ price: t.price ?? null,
3989
+ priceChange1d: t.price_change_1d ?? null,
3990
+ priceChange7d: t.price_change_7d ?? null,
3991
+ priceChange30d: t.price_change_30d ?? null,
3992
+ publishedAt: t.published_at
3993
+ }));
3994
+ var tokenListItemSchema = z.object({
3995
+ coin_type: z.string().nullish(),
3996
+ name: z.string(),
3997
+ symbol: z.string(),
3998
+ logo: z.string(),
3999
+ price: amountString,
4000
+ price_change_5m: z.number(),
4001
+ price_change_30m: z.number(),
4002
+ price_change_1h: z.number(),
4003
+ price_change_6h: z.number(),
4004
+ price_change_1d: z.number(),
4005
+ price_change_7d: z.number(),
4006
+ price_change_30d: z.number(),
4007
+ vol_change_1d: z.number(),
4008
+ liq_change_1d: z.number(),
4009
+ tx_change_1d: z.number(),
4010
+ tx_24h: z.number(),
4011
+ tx_buy_24h: z.number(),
4012
+ tx_sell_24h: z.number(),
4013
+ volume_1h: amountString,
4014
+ volume_24h: amountString,
4015
+ volume_1w: amountString,
4016
+ vol_buy_24h: amountString,
4017
+ vol_sell_24h: amountString,
4018
+ maker_24h: z.number(),
4019
+ market_cap: amountString,
4020
+ liquidity_usd: amountString,
4021
+ circulating_supply: amountString,
4022
+ total_supply: amountString,
4023
+ published_at: z.string(),
4024
+ first_trade_at: z.string().nullish(),
4025
+ verified: z.boolean(),
4026
+ suspicious_labels: arrayOf(z.string()),
4027
+ rank: z.number().nullish(),
4028
+ decimals: z.number().nullish(),
4029
+ category: z.string().nullish(),
4030
+ holders: z.number(),
4031
+ security: securitySchema.nullish(),
4032
+ boosting_point: z.number(),
4033
+ scam_label: z.string().nullish()
4034
+ }).transform((t) => ({
4035
+ coinId: t.coin_type ?? null,
4036
+ name: t.name,
4037
+ symbol: t.symbol,
4038
+ logo: t.logo,
4039
+ price: t.price,
4040
+ priceChange5m: t.price_change_5m,
4041
+ priceChange30m: t.price_change_30m,
4042
+ priceChange1h: t.price_change_1h,
4043
+ priceChange6h: t.price_change_6h,
4044
+ priceChange1d: t.price_change_1d,
4045
+ priceChange7d: t.price_change_7d,
4046
+ priceChange30d: t.price_change_30d,
4047
+ volumeChange1d: t.vol_change_1d,
4048
+ liquidityChange1d: t.liq_change_1d,
4049
+ txChange1d: t.tx_change_1d,
4050
+ tx24h: t.tx_24h,
4051
+ txBuy24h: t.tx_buy_24h,
4052
+ txSell24h: t.tx_sell_24h,
4053
+ volume1h: t.volume_1h,
4054
+ volume24h: t.volume_24h,
4055
+ volume1w: t.volume_1w,
4056
+ volBuy24h: t.vol_buy_24h,
4057
+ volSell24h: t.vol_sell_24h,
4058
+ maker24h: t.maker_24h,
4059
+ marketCap: t.market_cap,
4060
+ liquidityUsd: t.liquidity_usd,
4061
+ circulatingSupply: t.circulating_supply,
4062
+ totalSupply: t.total_supply,
4063
+ publishedAt: t.published_at,
4064
+ firstTradeAt: t.first_trade_at ?? null,
4065
+ verified: t.verified,
4066
+ suspiciousLabels: t.suspicious_labels,
4067
+ rank: t.rank ?? null,
4068
+ decimals: t.decimals ?? null,
4069
+ category: t.category ?? null,
4070
+ holders: t.holders,
4071
+ security: t.security ?? null,
4072
+ boostingPoint: t.boosting_point,
4073
+ scamLabel: t.scam_label ?? null
4074
+ }));
4075
+ var tokenTradeSchema = z.object({
4076
+ id: z.string(),
4077
+ timestamp: z.number(),
4078
+ action: z.string(),
4079
+ from_coin_ident: z.string().nullish(),
4080
+ from_coin_name: z.string(),
4081
+ from_coin_symbol: z.string(),
4082
+ to_coin_ident: z.string().nullish(),
4083
+ to_coin_name: z.string(),
4084
+ to_coin_symbol: z.string(),
4085
+ price: z.number(),
4086
+ amount_in: z.number(),
4087
+ amount_out: z.number(),
4088
+ usd_value: z.number(),
4089
+ tx_digest: z.string(),
4090
+ sender: z.string(),
4091
+ protocol: z.string(),
4092
+ source: z.string().nullish(),
4093
+ status: z.string()
4094
+ }).transform((t) => ({
4095
+ id: t.id,
4096
+ /** Epoch milliseconds. */
4097
+ timestamp: t.timestamp,
4098
+ action: t.action,
4099
+ fromCoinId: t.from_coin_ident ?? null,
4100
+ fromCoinName: t.from_coin_name,
4101
+ fromCoinSymbol: t.from_coin_symbol,
4102
+ toCoinId: t.to_coin_ident ?? null,
4103
+ toCoinName: t.to_coin_name,
4104
+ toCoinSymbol: t.to_coin_symbol,
4105
+ price: t.price,
4106
+ amountIn: t.amount_in,
4107
+ amountOut: t.amount_out,
4108
+ usdValue: t.usd_value,
4109
+ txHash: t.tx_digest,
4110
+ sender: t.sender,
4111
+ protocol: t.protocol,
4112
+ source: t.source ?? null,
4113
+ status: t.status
4114
+ }));
4115
+ var TOKEN_LIST_SORT_BY = [
4116
+ "MARKETCAP",
4117
+ "VOLUME_1H",
4118
+ "VOLUME_1D",
4119
+ "VOLUME_1W",
4120
+ "PUBLISHED_AT",
4121
+ "LIQUIDITY",
4122
+ "TX_COUNT",
4123
+ "PRICE_CHANGE",
4124
+ "PRICE_CHANGE_5MIN",
4125
+ "PRICE_CHANGE_30MIN",
4126
+ "PRICE_CHANGE_1H",
4127
+ "PRICE_CHANGE_4H",
4128
+ "PRICE_CHANGE_6H",
4129
+ "PRICE_CHANGE_1D",
4130
+ "PRICE_CHANGE_1W"
4131
+ ];
4132
+ var TOKEN_TRADE_ACTIONS = [
4133
+ "buy",
4134
+ "sell",
4135
+ "join",
4136
+ "single_join",
4137
+ "exit",
4138
+ "single_exit"
4139
+ ];
4140
+ function toWireFilters(filters) {
4141
+ const wire = {
4142
+ search_term: filters.searchTerm,
4143
+ coin_ids: filters.coinIds,
4144
+ min_market_cap: filters.minMarketCap,
4145
+ max_market_cap: filters.maxMarketCap,
4146
+ min_liquidity: filters.minLiquidity,
4147
+ max_liquidity: filters.maxLiquidity,
4148
+ min_volume: filters.minVolume,
4149
+ max_volume: filters.maxVolume,
4150
+ min_top_10_holding: filters.minTop10Holding,
4151
+ max_top_10_holding: filters.maxTop10Holding,
4152
+ min_holder: filters.minHolder,
4153
+ max_holder: filters.maxHolder,
4154
+ min_price_1h_change: filters.minPrice1hChange,
4155
+ max_price_1h_change: filters.maxPrice1hChange,
4156
+ min_price_6h_change: filters.minPrice6hChange,
4157
+ max_price_6h_change: filters.maxPrice6hChange,
4158
+ min_price_24h_change: filters.minPrice24hChange,
4159
+ max_price_24h_change: filters.maxPrice24hChange,
4160
+ min_tx_buy_24h: filters.minTxBuy24h,
4161
+ max_tx_buy_24h: filters.maxTxBuy24h,
4162
+ min_tx_sell_24h: filters.minTxSell24h,
4163
+ max_tx_sell_24h: filters.maxTxSell24h,
4164
+ min_tx_24h: filters.minTx24h,
4165
+ max_tx_24h: filters.maxTx24h,
4166
+ publish_at_from: filters.publishAtFrom ? toRfc3339(filters.publishAtFrom) : void 0,
4167
+ publish_at_to: filters.publishAtTo ? toRfc3339(filters.publishAtTo) : void 0,
4168
+ has_x_social: filters.hasXSocial,
4169
+ has_telegram_social: filters.hasTelegramSocial,
4170
+ has_discord_social: filters.hasDiscordSocial,
4171
+ has_website_social: filters.hasWebsiteSocial,
4172
+ has_social: filters.hasSocial,
4173
+ verified: filters.verified,
4174
+ is_boosted: filters.isBoosted
4175
+ };
4176
+ for (const key of Object.keys(wire)) {
4177
+ if (wire[key] === void 0) {
4178
+ delete wire[key];
4179
+ }
4180
+ }
4181
+ return wire;
4182
+ }
4183
+ var INFO_LIST_CHUNK = 100;
4184
+ var TokenModule = class {
4185
+ constructor(sdk) {
4186
+ this.sdk = sdk;
4187
+ }
4188
+ sdk;
4189
+ /**
4190
+ * Fetch full detail for a single token.
4191
+ *
4192
+ * @param coinId `policyId.assetNameHex`, or `lovelace` for ADA.
4193
+ * @throws {MinswapError} with code `NOT_FOUND` when no such token exists.
4194
+ */
4195
+ async getById(coinId, options = {}) {
4196
+ const { data } = await this.sdk.clients.appApi.get({
4197
+ path: "/api/v1/coin/detail",
4198
+ query: { ident: coinId },
4199
+ schema: tokenDetailSchema,
4200
+ currency: options.currency,
4201
+ notFound: { resource: "token", id: coinId }
4202
+ });
4203
+ return data;
4204
+ }
4205
+ /**
4206
+ * Fetch compact records for many tokens at once.
4207
+ *
4208
+ * Requests are chunked at 100 ids, the upstream cap. Tokens that do not
4209
+ * exist are simply absent from the result, so the output length may be
4210
+ * shorter than the input and the order is not guaranteed to match.
4211
+ */
4212
+ async getByIds(coinIds, options = {}) {
4213
+ if (coinIds.length === 0) {
4214
+ return [];
4215
+ }
4216
+ const chunks = [];
4217
+ for (let i = 0; i < coinIds.length; i += INFO_LIST_CHUNK) {
4218
+ chunks.push(coinIds.slice(i, i + INFO_LIST_CHUNK));
4219
+ }
4220
+ const pages = await Promise.all(
4221
+ chunks.map(
4222
+ (chunk) => this.sdk.clients.appApi.post({
4223
+ path: "/api/v1/coin/info-list",
4224
+ body: {
4225
+ search: "",
4226
+ coin_types: chunk,
4227
+ verified: false,
4228
+ pagination: { offset: 0, limit: chunk.length }
4229
+ },
4230
+ schema: arrayOf(tokenSummarySchema),
4231
+ currency: options.currency
4232
+ })
4233
+ )
4234
+ );
4235
+ return pages.flatMap((page) => page.data);
4236
+ }
4237
+ /** List tokens with filtering and sorting. */
4238
+ async list(params = {}) {
4239
+ assertLimit(params.limit, 50);
4240
+ const limit = params.limit ?? 20;
4241
+ const offset = decodeOffsetCursor(params.cursor);
4242
+ const filters = toWireFilters(params.filters ?? {});
4243
+ if (Object.keys(filters).length === 0 && !params.applyBackendDefaults) {
4244
+ filters["min_liquidity"] = 0;
4245
+ }
4246
+ const { data } = await this.sdk.clients.appApi.post({
4247
+ path: "/api/v1/coin/list",
4248
+ body: {
4249
+ pagination: { offset, limit },
4250
+ sort_by: params.sortBy ?? "VOLUME_1D",
4251
+ order: params.order ?? "DESC",
4252
+ filters
4253
+ },
4254
+ schema: arrayOf(tokenListItemSchema),
4255
+ currency: params.currency
4256
+ });
4257
+ return offsetPage(data, offset, limit);
4258
+ }
4259
+ /** OHLC candles for a token, oldest bucket first. */
4260
+ async getOhlc(params) {
4261
+ const bucketMinutes = params.bucketMinutes ?? 1;
4262
+ assertBucket(bucketMinutes);
4263
+ if (params.limit !== void 0 && (!Number.isInteger(params.limit) || params.limit < 1)) {
4264
+ throw new MinswapError(
4265
+ `limit must be a positive integer; received ${params.limit}`,
4266
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
4267
+ { param: "limit", reason: "must be a positive integer" }
4268
+ );
4269
+ }
4270
+ const { data } = await this.sdk.clients.appApi.get({
4271
+ path: "/api/v1/coin/ohlc-chart",
4272
+ query: {
4273
+ coin_type: params.coinId,
4274
+ // This endpoint wants RFC 3339; the pool chart wants epoch seconds.
4275
+ from: params.from ? toRfc3339(params.from) : void 0,
4276
+ to: params.to ? toRfc3339(params.to) : void 0,
4277
+ bucketMinute: bucketMinutes,
4278
+ limit: params.limit
4279
+ },
4280
+ schema: arrayOf(coinCandleSchema),
4281
+ currency: params.currency
4282
+ });
4283
+ return data;
4284
+ }
4285
+ /** Recent trades for a token, newest first. */
4286
+ async getTradeHistory(params) {
4287
+ assertLimit(params.limit, 50);
4288
+ const { data, pagination } = await this.sdk.clients.appApi.post({
4289
+ path: "/api/v1/coin/trading-history",
4290
+ body: {
4291
+ coin_type: params.coinId,
4292
+ pagination: { cursor: params.cursor, limit: params.limit ?? 20 },
4293
+ filters: {
4294
+ time_range: params.timeRange ? [toEpochMs(params.timeRange.from), toEpochMs(params.timeRange.to)] : void 0,
4295
+ actions: params.actions,
4296
+ usd_value: params.usdValue,
4297
+ protocols: params.protocols,
4298
+ senders: params.senders
4299
+ }
4300
+ },
4301
+ schema: arrayOf(tokenTradeSchema),
4302
+ currency: params.currency
4303
+ });
4304
+ return cursorPage(data, pagination?.lastCursor);
4305
+ }
4306
+ };
4307
+
4308
+ // src/sdk.ts
4309
+ var MinswapSdk = class {
4310
+ config;
4311
+ clients;
4312
+ token;
4313
+ pool;
4314
+ portfolio;
4315
+ order;
4316
+ aggregator;
4317
+ farm;
4318
+ staking;
4319
+ liquidity;
4320
+ constructor(config = {}) {
4321
+ this.config = resolveConfig(config);
4322
+ const http = new HttpCore({
4323
+ fetch: this.config.fetch,
4324
+ timeoutMs: this.config.timeoutMs,
4325
+ retry: this.config.retry
4326
+ });
4327
+ this.clients = {
4328
+ appApi: new AppApiClient(http, this.config),
4329
+ aggregatorApi: new AggregatorApiClient(http, this.config),
4330
+ keyAppApi: new KeyAppApiClient(http, this.config),
4331
+ appGraphql: new AppGraphqlClient(http, this.config)
4332
+ };
4333
+ this.token = new TokenModule(this);
4334
+ this.pool = new PoolModule(this);
4335
+ this.portfolio = new PortfolioModule(this);
4336
+ this.order = new OrderModule(this);
4337
+ this.aggregator = new AggregatorModule(this);
4338
+ this.farm = new FarmModule(this);
4339
+ this.staking = new StakingModule(this);
4340
+ this.liquidity = new LiquidityModule(this);
4341
+ }
4342
+ /**
4343
+ * Chain access, when configured.
4344
+ *
4345
+ * Present only if an `rpcProvider` was supplied. Farm and staking actions
4346
+ * require it; every read works without it.
4347
+ */
4348
+ get rpcProvider() {
4349
+ return this.config.rpcProvider;
4350
+ }
4351
+ };
4352
+ var parseBig = JSONBig({ alwaysParseAsBig: true, useNativeBigInt: true }).parse;
4353
+ var bigintish = z.union([z.bigint(), z.number(), z.string()]).transform((v) => BigInt(v));
4354
+ var kupoMatchSchema = z.object({
4355
+ transaction_id: z.string(),
4356
+ transaction_index: bigintish,
4357
+ output_index: bigintish,
4358
+ address: z.string(),
4359
+ value: z.object({
4360
+ coins: bigintish,
4361
+ assets: z.record(z.string(), bigintish).nullish()
4362
+ }),
4363
+ datum_hash: z.string().nullish(),
4364
+ datum_type: z.enum(["inline", "hash"]).nullish(),
4365
+ script_hash: z.string().nullish()
4366
+ });
4367
+ var kupoMatchesSchema = z.array(kupoMatchSchema);
4368
+ var kupoDatumSchema = z.object({ datum: z.string() }).nullish();
4369
+ var KupoRpcProvider = class {
4370
+ http;
4371
+ baseUrl;
4372
+ headers;
4373
+ maxConcurrency;
4374
+ configuredSerializer;
4375
+ serializer;
4376
+ constructor(options) {
4377
+ const fetchImpl = options.fetch ?? globalThis.fetch;
4378
+ if (typeof fetchImpl !== "function") {
4379
+ throw new MinswapError(
4380
+ "No fetch implementation available; pass one via `new KupoRpcProvider({ fetch })`",
4381
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
4382
+ { param: "fetch", reason: "is not available on globalThis" }
4383
+ );
4384
+ }
4385
+ this.baseUrl = options.url.replace(/\/+$/, "");
4386
+ this.headers = options.headers ?? {};
4387
+ this.maxConcurrency = options.maxConcurrency ?? 20;
4388
+ this.configuredSerializer = options.serializer;
4389
+ this.http = new HttpCore({
4390
+ fetch: fetchImpl,
4391
+ timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
4392
+ retry: { ...DEFAULT_RETRY, ...options.retry }
4393
+ });
4394
+ }
4395
+ async getUtxosByAddress(address) {
4396
+ const matches = await this.matches(`${encodeURIComponent(address)}?unspent`);
4397
+ return this.toRpcUtxos(matches);
4398
+ }
4399
+ async getUtxosByRefs(refs) {
4400
+ if (refs.length === 0) {
4401
+ return [];
4402
+ }
4403
+ const batches = await mapConcurrent(
4404
+ refs,
4405
+ this.maxConcurrency,
4406
+ (ref) => this.matches(`${ref.outputIndex}@${ref.txHash}?unspent`)
4407
+ );
4408
+ return this.toRpcUtxos(batches.flat());
4409
+ }
4410
+ async matches(pattern) {
4411
+ return this.http.request({
4412
+ url: `${this.baseUrl}/matches/${pattern}`,
4413
+ method: "GET",
4414
+ endpoint: `GET /matches/${pattern}`,
4415
+ headers: this.headers,
4416
+ schema: kupoMatchesSchema,
4417
+ parseJson: parseBig
4418
+ });
4419
+ }
4420
+ async getDatum(hash) {
4421
+ const result = await this.http.request({
4422
+ url: buildUrl(this.baseUrl, `/datums/${hash}`),
4423
+ method: "GET",
4424
+ endpoint: `GET /datums/${hash}`,
4425
+ headers: this.headers,
4426
+ schema: kupoDatumSchema,
4427
+ parseJson: parseBig
4428
+ });
4429
+ return result?.datum;
4430
+ }
4431
+ async toRpcUtxos(matches) {
4432
+ if (matches.length === 0) {
4433
+ return [];
4434
+ }
4435
+ const serializer = await this.loadSerializer();
4436
+ const hashes = [
4437
+ ...new Set(
4438
+ matches.filter((m) => m.datum_type === "inline" && m.datum_hash).map((m) => m.datum_hash)
4439
+ )
4440
+ ];
4441
+ const datums = /* @__PURE__ */ new Map();
4442
+ const resolved = await mapConcurrent(hashes, this.maxConcurrency, (h) => this.getDatum(h));
4443
+ for (const [i, h] of hashes.entries()) datums.set(h, resolved[i]);
4444
+ return matches.map((match) => this.toRpcUtxo(match, datums, serializer));
4445
+ }
4446
+ toRpcUtxo(match, datums, serializer) {
4447
+ const ref = `${match.transaction_id}#${match.output_index}`;
4448
+ if (match.script_hash) {
4449
+ throw new MinswapError(
4450
+ `UTxO ${ref} carries a reference script, which this provider cannot serialize faithfully yet. Exclude it from the inputs you pass to Minswap.`,
4451
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
4452
+ { param: "utxo", reason: "reference scripts are not supported" }
4453
+ );
4454
+ }
4455
+ const datum = match.datum_hash ? datums.get(match.datum_hash) : void 0;
4456
+ if (match.datum_type === "inline" && !datum) {
4457
+ throw new MinswapError(
4458
+ `UTxO ${ref} has an inline datum that Kupo did not return for hash ${match.datum_hash}`,
4459
+ "API_ERROR" /* API_ERROR */,
4460
+ { endpoint: `GET /datums/${match.datum_hash}`, status: 200, body: null }
4461
+ );
4462
+ }
4463
+ const kupoUtxo = {
4464
+ transaction_id: match.transaction_id,
4465
+ transaction_index: Number(match.transaction_index),
4466
+ output_index: Number(match.output_index),
4467
+ address: match.address,
4468
+ value: {
4469
+ coins: match.value.coins,
4470
+ assets: match.value.assets ?? void 0
4471
+ },
4472
+ datum_hash: match.datum_hash ?? null,
4473
+ datum_type: match.datum_type ?? null,
4474
+ // Ignored by the conversion, which reads only the input ref and output.
4475
+ created_at: { slot_no: 0, header_hash: "" }
4476
+ };
4477
+ let cbor;
4478
+ try {
4479
+ cbor = serializer.toCborHex(kupoUtxo, datum);
4480
+ } catch (cause) {
4481
+ throw new MinswapError(
4482
+ `UTxO ${ref} could not be serialized to CBOR`,
4483
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
4484
+ { param: "utxo", reason: `serializer rejected ${ref}: ${String(cause)}` }
4485
+ );
4486
+ }
4487
+ return {
4488
+ txHash: match.transaction_id,
4489
+ outputIndex: Number(match.output_index),
4490
+ address: match.address,
4491
+ cbor,
4492
+ assets: toAssetMap(match.value),
4493
+ datumHash: match.datum_hash ?? null,
4494
+ datum: datum ?? null,
4495
+ scriptRef: null
4496
+ };
4497
+ }
4498
+ /**
4499
+ * Resolve the serializer on first use.
4500
+ *
4501
+ * Deferred rather than imported at module scope so consumers who never touch
4502
+ * this provider are not made to load a WebAssembly Cardano library.
4503
+ */
4504
+ loadSerializer() {
4505
+ if (this.configuredSerializer) {
4506
+ return Promise.resolve(this.configuredSerializer);
4507
+ }
4508
+ this.serializer ??= importLedger().then((ledger) => ({
4509
+ toCborHex: (utxo, datum) => ledger.Utxo.toHex(ledger.Utxo.fromKupo(utxo, datum))
4510
+ }));
4511
+ return this.serializer;
4512
+ }
4513
+ };
4514
+ var INTERNAL_SDK2 = "@minswap/internal-sdk";
4515
+ async function importLedger() {
4516
+ let mod;
4517
+ try {
4518
+ mod = await import(
4519
+ /* @vite-ignore */
4520
+ INTERNAL_SDK2
4521
+ );
4522
+ } catch (cause) {
4523
+ throw new MinswapError(
4524
+ `KupoRpcProvider needs ${INTERNAL_SDK2} to serialize UTxOs. Install it: npm install ${INTERNAL_SDK2}`,
4525
+ "INVALID_PARAMS" /* INVALID_PARAMS */,
4526
+ { param: INTERNAL_SDK2, reason: `could not be imported: ${String(cause)}` }
4527
+ );
4528
+ }
4529
+ await mod.RustModule.load();
4530
+ return mod;
4531
+ }
4532
+ function toAssetMap(value) {
4533
+ const assets = { lovelace: value.coins };
4534
+ for (const [key, amount] of Object.entries(value.assets ?? {})) {
4535
+ const [policyId, assetName = ""] = key.split(".");
4536
+ assets[`${policyId}${assetName}`] = amount;
4537
+ }
4538
+ return assets;
4539
+ }
4540
+ async function mapConcurrent(items, limit, fn) {
4541
+ const results = new Array(items.length);
4542
+ let next = 0;
4543
+ const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
4544
+ for (; ; ) {
4545
+ const index = next++;
4546
+ if (index >= items.length) {
4547
+ return;
4548
+ }
4549
+ results[index] = await fn(items[index]);
4550
+ }
4551
+ });
4552
+ await Promise.all(workers);
4553
+ return results;
4554
+ }
4555
+
4556
+ export { AGGREGATOR_PROTOCOLS, AggregatorApiClient, AggregatorModule, AppApiClient, AppGraphqlClient, BATCHER_FEE, CANCEL_ADAPTER_PROTOCOL, DEFAULT_ENDPOINTS, DEFAULT_RETRY, DEFAULT_TIMEOUT_MS, DEPOSIT_ADA, DEX_V1, DEX_V2, FARM_LIST_SORT_FIELDS, FARM_PROTOCOLS, FarmModule, HttpCore, KeyAppApiClient, KupoRpcProvider, LiquidityModule, MAINNET_NETWORK_ENV, MinswapError, MinswapErrorCode, MinswapSdk, ORDER_SOURCE_DIRECT, ORDER_STATUSES, OrderModule, POOL_CATEGORY_GROUPS, POOL_EVENT_ACTIONS, POOL_FILTER_TYPES, POOL_LIST_SORT_FIELDS, POOL_TYPES, PoolModule, PortfolioModule, StakingModule, TOKEN_LIST_SORT_BY, TOKEN_TRADE_ACTIONS, TokenModule, VALID_BUCKET_MINUTES, arrayOf, assertBucket, assertLimit, assetUnitToCoinId, buildUrl, coinAmountInfoSchema, coinBasicInfoSchema, coinCandleSchema, coinIdToAssetUnit, coinIdToInputAsset, collect, cursorPage, decimalToRaw, decodeOffsetCursor, encodeOffsetCursor, formatTxIn, getAppliedPoolsByLpAssets, getAppliedPoolsByPairs, inputAssetToCoinId, isPureAda, loadDexBuilder, lovelaceOf, offsetPage, paginate, parseTxIn, poolCandleSchema, poolCoinSchema, poolFeeSchema, poolRewardSchema, requireRpcProvider, resolveConfig, resolveWalletInputs, selectCollateral, toBigIntString, toDatumHex, toEpochMs, toEpochSeconds, toRfc3339, zeroableNumber };
4557
+ //# sourceMappingURL=index.js.map
4558
+ //# sourceMappingURL=index.js.map