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