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