@make-software/cspr-trade-mcp-sdk 0.1.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.
Files changed (55) hide show
  1. package/README.md +257 -0
  2. package/dist/assets/proxy_caller.wasm +0 -0
  3. package/dist/index.d.ts +362 -0
  4. package/dist/index.js +1003 -0
  5. package/package.json +32 -0
  6. package/src/api/currencies.ts +11 -0
  7. package/src/api/http.ts +57 -0
  8. package/src/api/index.ts +9 -0
  9. package/src/api/liquidity.ts +22 -0
  10. package/src/api/pairs.ts +77 -0
  11. package/src/api/quotes.ts +23 -0
  12. package/src/api/rates.ts +33 -0
  13. package/src/api/submission.ts +42 -0
  14. package/src/api/swaps.ts +24 -0
  15. package/src/api/tokens.ts +57 -0
  16. package/src/assets/index.ts +21 -0
  17. package/src/assets/proxy_caller.wasm +0 -0
  18. package/src/client.ts +587 -0
  19. package/src/config.ts +60 -0
  20. package/src/index.ts +4 -0
  21. package/src/resolver/currency-resolver.ts +19 -0
  22. package/src/resolver/index.ts +2 -0
  23. package/src/resolver/token-resolver.ts +43 -0
  24. package/src/transactions/approve.ts +14 -0
  25. package/src/transactions/index.ts +5 -0
  26. package/src/transactions/liquidity.ts +92 -0
  27. package/src/transactions/proxy-wasm.ts +33 -0
  28. package/src/transactions/swap.ts +76 -0
  29. package/src/transactions/transaction-builder.ts +44 -0
  30. package/src/types/api.ts +32 -0
  31. package/src/types/index.ts +6 -0
  32. package/src/types/liquidity.ts +72 -0
  33. package/src/types/pair.ts +29 -0
  34. package/src/types/quote.ts +41 -0
  35. package/src/types/token.ts +48 -0
  36. package/src/types/transaction.ts +72 -0
  37. package/src/utils/amounts.ts +30 -0
  38. package/tests/integration/api.integration.test.ts +64 -0
  39. package/tests/unit/api/http.test.ts +68 -0
  40. package/tests/unit/api/liquidity.test.ts +40 -0
  41. package/tests/unit/api/pairs.test.ts +53 -0
  42. package/tests/unit/api/quotes.test.ts +59 -0
  43. package/tests/unit/api/rates.test.ts +27 -0
  44. package/tests/unit/api/tokens.test.ts +100 -0
  45. package/tests/unit/assets/proxy-caller.test.ts +21 -0
  46. package/tests/unit/client.test.ts +73 -0
  47. package/tests/unit/config.test.ts +23 -0
  48. package/tests/unit/resolver/currency-resolver.test.ts +32 -0
  49. package/tests/unit/resolver/token-resolver.test.ts +51 -0
  50. package/tests/unit/transactions/approve.test.ts +13 -0
  51. package/tests/unit/transactions/liquidity.test.ts +59 -0
  52. package/tests/unit/transactions/proxy-wasm.test.ts +50 -0
  53. package/tests/unit/transactions/swap.test.ts +77 -0
  54. package/tests/unit/utils/amounts.test.ts +44 -0
  55. package/tsconfig.json +9 -0
package/dist/index.js ADDED
@@ -0,0 +1,1003 @@
1
+ // src/client.ts
2
+ import casperSdk6 from "casper-js-sdk";
3
+
4
+ // src/api/http.ts
5
+ var HttpClientError = class extends Error {
6
+ constructor(message, status, body) {
7
+ super(message);
8
+ this.status = status;
9
+ this.body = body;
10
+ this.name = "HttpClientError";
11
+ }
12
+ };
13
+ var HttpClient = class {
14
+ constructor(baseUrl, headers = {}) {
15
+ this.baseUrl = baseUrl;
16
+ this.headers = headers;
17
+ }
18
+ async get(path, params) {
19
+ const url = this.buildUrl(path, params);
20
+ const response = await fetch(url, {
21
+ method: "GET",
22
+ headers: { "Content-Type": "application/json", ...this.headers }
23
+ });
24
+ return this.handleResponse(response);
25
+ }
26
+ async post(path, body) {
27
+ const url = this.buildUrl(path);
28
+ const response = await fetch(url, {
29
+ method: "POST",
30
+ headers: { "Content-Type": "application/json", ...this.headers },
31
+ body: JSON.stringify(body)
32
+ });
33
+ return this.handleResponse(response);
34
+ }
35
+ buildUrl(path, params) {
36
+ const url = new URL(path, this.baseUrl);
37
+ if (params) {
38
+ for (const [key, value] of Object.entries(params)) {
39
+ if (value !== void 0) {
40
+ url.searchParams.set(key, String(value));
41
+ }
42
+ }
43
+ }
44
+ return url.toString();
45
+ }
46
+ async handleResponse(response) {
47
+ const body = await response.json();
48
+ if (!response.ok) {
49
+ const message = body?.error?.message ?? body?.message ?? `HTTP ${response.status}`;
50
+ throw new HttpClientError(message, response.status, body);
51
+ }
52
+ return body;
53
+ }
54
+ };
55
+
56
+ // src/config.ts
57
+ var TESTNET_CONFIG = {
58
+ chainName: "casper-test",
59
+ apiUrl: "https://cspr-trade-api.dev.make.services",
60
+ nodeRpcUrl: "https://node.testnet.casper.network/rpc",
61
+ routerPackageHash: "hash-04a11a367e708c52557930c4e9c1301f4465100d1b1b6d0a62b48d3e32402867",
62
+ wcsprPackageHash: "hash-3d80df21ba4ee4d66a2a1f60c32570dd5685e4b279f6538162a5fd1314847c1e",
63
+ gasPrice: 1,
64
+ ttl: 18e5
65
+ // 30 minutes
66
+ };
67
+ var MAINNET_CONFIG = {
68
+ chainName: "casper",
69
+ apiUrl: "https://api.cspr.trade",
70
+ nodeRpcUrl: "https://node.mainnet.casper.network/rpc",
71
+ // TODO: confirm mainnet addresses
72
+ routerPackageHash: "hash-0000000000000000000000000000000000000000000000000000000000000000",
73
+ wcsprPackageHash: "hash-0000000000000000000000000000000000000000000000000000000000000000",
74
+ gasPrice: 1,
75
+ ttl: 18e5
76
+ };
77
+ function getNetworkConfig(network) {
78
+ return network === "testnet" ? TESTNET_CONFIG : MAINNET_CONFIG;
79
+ }
80
+ var DEFAULT_SLIPPAGE_BPS = 300;
81
+ var DEFAULT_DEADLINE_MINUTES = 20;
82
+ var GAS_COSTS = {
83
+ approve: 5000000000n,
84
+ // 5 CSPR
85
+ swapCsprForToken: 30000000000n,
86
+ // 30 CSPR
87
+ swapTokenForToken: 30000000000n,
88
+ // 30 CSPR
89
+ addLiquidity: 50000000000n,
90
+ // 50 CSPR
91
+ addNewLiquidity: 500000000000n,
92
+ // 500 CSPR
93
+ removeLiquidity: 30000000000n
94
+ // 30 CSPR
95
+ };
96
+ var CSPR_TOKEN_ID = "cspr";
97
+ var CSPR_DECIMALS = 9;
98
+ var ZERO_HASH = "0000000000000000000000000000000000000000000000000000000000000000";
99
+ var PRICE_IMPACT_WARNING_THRESHOLD = 5;
100
+ var PRICE_IMPACT_HIGH_THRESHOLD = 15;
101
+ var SLIPPAGE_WARNING_THRESHOLD = 10;
102
+
103
+ // src/api/tokens.ts
104
+ var TokensApi = class {
105
+ constructor(http, wcsprPackageHash) {
106
+ this.http = http;
107
+ this.wcsprPackageHash = wcsprPackageHash;
108
+ }
109
+ async getTokens(currencyId) {
110
+ const response = await this.http.get("/tokens", {
111
+ includes: currencyId !== void 0 ? `csprtrade_data(${currencyId})` : void 0
112
+ });
113
+ const wcsprHash = this.wcsprPackageHash?.replace("hash-", "") ?? "";
114
+ const tokens = response.data.map((apiToken) => {
115
+ const rawHash = apiToken.contract_package_hash.replace("hash-", "");
116
+ if (rawHash === wcsprHash) {
117
+ const meta = apiToken.contract_package?.metadata;
118
+ return {
119
+ id: CSPR_TOKEN_ID,
120
+ name: "Casper",
121
+ symbol: "CSPR",
122
+ decimals: meta?.decimals ?? CSPR_DECIMALS,
123
+ packageHash: apiToken.contract_package_hash,
124
+ iconUrl: apiToken.contract_package?.icon_url ?? null,
125
+ fiatPrice: apiToken.contract_package?.csprtrade_data?.price ?? null
126
+ };
127
+ }
128
+ return mapApiTokenToToken(apiToken);
129
+ });
130
+ return tokens;
131
+ }
132
+ async getTokensRaw(currencyId) {
133
+ const response = await this.http.get("/tokens", {
134
+ includes: currencyId !== void 0 ? `csprtrade_data(${currencyId})` : void 0
135
+ });
136
+ return response.data;
137
+ }
138
+ };
139
+ function mapApiTokenToToken(apiToken) {
140
+ const meta = apiToken.contract_package?.metadata;
141
+ return {
142
+ id: apiToken.contract_package_hash,
143
+ name: meta?.name ?? apiToken.contract_package?.name ?? "",
144
+ symbol: meta?.symbol ?? "",
145
+ decimals: meta?.decimals ?? 0,
146
+ packageHash: apiToken.contract_package_hash,
147
+ iconUrl: apiToken.contract_package?.icon_url ?? null,
148
+ fiatPrice: apiToken.contract_package?.csprtrade_data?.price ?? null
149
+ };
150
+ }
151
+
152
+ // src/api/pairs.ts
153
+ var PairsApi = class {
154
+ constructor(http) {
155
+ this.http = http;
156
+ }
157
+ async getPairs(opts) {
158
+ const response = await this.http.get("/pairs", {
159
+ page: opts?.page !== void 0 ? String(opts.page) : void 0,
160
+ page_size: opts?.pageSize !== void 0 ? String(opts.pageSize) : void 0,
161
+ order_by: opts?.orderBy,
162
+ order_direction: opts?.orderDirection,
163
+ token0_contract_package_hash: opts?.token0Hash,
164
+ token1_contract_package_hash: opts?.token1Hash,
165
+ includes: opts?.currencyId !== void 0 ? `csprtrade_data(${opts.currencyId})` : void 0
166
+ });
167
+ return {
168
+ data: response.data.map(mapPair),
169
+ itemCount: response.item_count,
170
+ pageCount: response.page_count
171
+ };
172
+ }
173
+ async getPairDetails(contractPackageHash, currencyId) {
174
+ const response = await this.http.get(
175
+ `/pairs/${contractPackageHash}`,
176
+ {
177
+ includes: currencyId !== void 0 ? `csprtrade_data(${currencyId})` : void 0
178
+ }
179
+ );
180
+ return mapPair(response.data);
181
+ }
182
+ };
183
+ function mapPair(api) {
184
+ const meta0 = api.token0_contract_package?.metadata;
185
+ const meta1 = api.token1_contract_package?.metadata;
186
+ return {
187
+ contractPackageHash: api.contract_package_hash,
188
+ token0: {
189
+ packageHash: api.token0_contract_package_hash,
190
+ symbol: meta0?.symbol ?? "",
191
+ name: meta0?.name ?? "",
192
+ decimals: api.decimals0,
193
+ iconUrl: api.token0_contract_package?.icon_url ?? null
194
+ },
195
+ token1: {
196
+ packageHash: api.token1_contract_package_hash,
197
+ symbol: meta1?.symbol ?? "",
198
+ name: meta1?.name ?? "",
199
+ decimals: api.decimals1,
200
+ iconUrl: api.token1_contract_package?.icon_url ?? null
201
+ },
202
+ reserve0: api.reserve0,
203
+ reserve1: api.reserve1,
204
+ timestamp: api.timestamp,
205
+ fiatPrice0: api.token0_contract_package?.csprtrade_data?.price ?? null,
206
+ fiatPrice1: api.token1_contract_package?.csprtrade_data?.price ?? null
207
+ };
208
+ }
209
+
210
+ // src/api/quotes.ts
211
+ var QuotesApi = class {
212
+ constructor(http) {
213
+ this.http = http;
214
+ }
215
+ async getQuote(params) {
216
+ const response = await this.http.get("/quote", {
217
+ token_in: params.tokenIn,
218
+ token_out: params.tokenOut,
219
+ amount: params.amount,
220
+ type_id: String(params.typeId)
221
+ });
222
+ return response.data;
223
+ }
224
+ };
225
+
226
+ // src/api/liquidity.ts
227
+ var LiquidityApi = class {
228
+ constructor(http) {
229
+ this.http = http;
230
+ }
231
+ async getPositions(accountIdentifier, currencyId) {
232
+ const response = await this.http.get(
233
+ `/accounts/${accountIdentifier}/liquidity-positions`,
234
+ { includes: currencyId !== void 0 ? `csprtrade_data(${currencyId})` : void 0 }
235
+ );
236
+ return response.data;
237
+ }
238
+ async getImpermanentLoss(accountIdentifier, pairHash) {
239
+ const response = await this.http.get(
240
+ `/accounts/${accountIdentifier}/liquidity-position-impermanent-loss`,
241
+ { pair_contract_package_hash: pairHash }
242
+ );
243
+ return response.data;
244
+ }
245
+ };
246
+
247
+ // src/api/rates.ts
248
+ var RatesApi = class {
249
+ constructor(http) {
250
+ this.http = http;
251
+ }
252
+ async getCsprRate(currencyId) {
253
+ const response = await this.http.get(`/rates/${currencyId}/latest`);
254
+ return response.data;
255
+ }
256
+ async getTokenRate(contractPackageHash, currencyId, dexId) {
257
+ const response = await this.http.get(
258
+ `/ft/${contractPackageHash}/rates/latest`,
259
+ {
260
+ currency_id: currencyId !== void 0 ? String(currencyId) : void 0,
261
+ dex_id: dexId !== void 0 ? String(dexId) : void 0
262
+ }
263
+ );
264
+ return response.data;
265
+ }
266
+ async getTokenDexRate(contractPackageHash, targetHash, dexId) {
267
+ const response = await this.http.get(
268
+ `/ft/${contractPackageHash}/dex-rates/latest`,
269
+ {
270
+ target_contract_package_hash: targetHash,
271
+ dex_id: dexId !== void 0 ? String(dexId) : void 0
272
+ }
273
+ );
274
+ return response.data;
275
+ }
276
+ };
277
+
278
+ // src/api/currencies.ts
279
+ var CurrenciesApi = class {
280
+ constructor(http) {
281
+ this.http = http;
282
+ }
283
+ async getCurrencies() {
284
+ const response = await this.http.get("/currencies");
285
+ return response.data;
286
+ }
287
+ };
288
+
289
+ // src/api/swaps.ts
290
+ var SwapsApi = class {
291
+ constructor(http) {
292
+ this.http = http;
293
+ }
294
+ async getSwaps(opts) {
295
+ return this.http.get("/swaps", {
296
+ sender_hash: opts?.senderAccountHash,
297
+ pair_contract_package_hash: opts?.pairContractPackageHash,
298
+ page: opts?.page !== void 0 ? String(opts.page) : void 0,
299
+ page_size: opts?.pageSize !== void 0 ? String(opts.pageSize) : void 0,
300
+ order_direction: opts?.orderDirection
301
+ });
302
+ }
303
+ };
304
+
305
+ // src/resolver/token-resolver.ts
306
+ var TokenResolver = class {
307
+ // 30 seconds
308
+ constructor(fetchTokens) {
309
+ this.fetchTokens = fetchTokens;
310
+ }
311
+ cache = null;
312
+ cacheTimestamp = 0;
313
+ cacheTtlMs = 3e4;
314
+ async resolve(identifier) {
315
+ const tokens = await this.getTokens();
316
+ const id = identifier.trim();
317
+ const bySymbol = tokens.find((t) => t.symbol.toLowerCase() === id.toLowerCase());
318
+ if (bySymbol) return bySymbol;
319
+ const byHash = tokens.find((t) => t.packageHash.toLowerCase() === id.toLowerCase());
320
+ if (byHash) return byHash;
321
+ const byName = tokens.find((t) => t.name.toLowerCase() === id.toLowerCase());
322
+ if (byName) return byName;
323
+ throw new Error(`Token not found: "${identifier}". Use a token symbol (e.g., "CSPR"), name, or contract package hash.`);
324
+ }
325
+ async getTokens() {
326
+ const now = Date.now();
327
+ if (this.cache && now - this.cacheTimestamp < this.cacheTtlMs) {
328
+ return this.cache;
329
+ }
330
+ this.cache = await this.fetchTokens();
331
+ this.cacheTimestamp = now;
332
+ return this.cache;
333
+ }
334
+ invalidateCache() {
335
+ this.cache = null;
336
+ this.cacheTimestamp = 0;
337
+ }
338
+ };
339
+
340
+ // src/resolver/currency-resolver.ts
341
+ var CurrencyResolver = class {
342
+ constructor(fetchCurrencies) {
343
+ this.fetchCurrencies = fetchCurrencies;
344
+ }
345
+ cache = null;
346
+ async resolveToId(code) {
347
+ const currencies = await this.getCurrencies();
348
+ const match = currencies.find((c) => c.code.toLowerCase() === code.toLowerCase());
349
+ return match?.id;
350
+ }
351
+ async getCurrencies() {
352
+ if (this.cache) return this.cache;
353
+ this.cache = await this.fetchCurrencies();
354
+ return this.cache;
355
+ }
356
+ };
357
+
358
+ // src/utils/amounts.ts
359
+ import Big from "big.js";
360
+ function toRawAmount(humanAmount, decimals) {
361
+ const big = new Big(humanAmount);
362
+ const multiplier = new Big(10).pow(decimals);
363
+ return big.times(multiplier).toFixed(0);
364
+ }
365
+ function toFormattedAmount(rawAmount, decimals) {
366
+ const big = new Big(rawAmount);
367
+ const divisor = new Big(10).pow(decimals);
368
+ const result = big.div(divisor);
369
+ const str = result.toFixed();
370
+ if (str.includes(".")) {
371
+ return str.replace(/\.?0+$/, "") || "0";
372
+ }
373
+ return str;
374
+ }
375
+ function calculateMinWithSlippage(rawAmount, slippageBps) {
376
+ const amount = new Big(rawAmount);
377
+ const factor = new Big(1).minus(new Big(slippageBps).div(1e4));
378
+ return amount.times(factor).toFixed(0, Big.roundDown);
379
+ }
380
+ function calculateMaxWithSlippage(rawAmount, slippageBps) {
381
+ const amount = new Big(rawAmount);
382
+ const factor = new Big(1).plus(new Big(slippageBps).div(1e4));
383
+ return amount.times(factor).toFixed(0, Big.roundUp);
384
+ }
385
+
386
+ // src/transactions/proxy-wasm.ts
387
+ import { hexToBytes } from "@noble/hashes/utils";
388
+ import casperSdk from "casper-js-sdk";
389
+ var { Args, CLTypeUInt8, CLValue } = casperSdk;
390
+ function serializeInnerArgs(args) {
391
+ return args.toBytes();
392
+ }
393
+ function buildProxyWasmArgs(params) {
394
+ const { routerPackageHash, entryPoint, innerArgs, attachedValue } = params;
395
+ const rawArgsBytes = serializeInnerArgs(innerArgs);
396
+ const argsBytes = [];
397
+ for (let i = 0; i < rawArgsBytes.length; i++) {
398
+ argsBytes.push(CLValue.newCLUint8(rawArgsBytes[i]));
399
+ }
400
+ return Args.fromMap({
401
+ package_hash: CLValue.newCLByteArray(hexToBytes(routerPackageHash)),
402
+ entry_point: CLValue.newCLString(entryPoint),
403
+ args: CLValue.newCLList(CLTypeUInt8, argsBytes),
404
+ attached_value: CLValue.newCLUInt512(attachedValue),
405
+ amount: CLValue.newCLUInt512(attachedValue)
406
+ });
407
+ }
408
+
409
+ // src/transactions/swap.ts
410
+ import casperSdk2 from "casper-js-sdk";
411
+ var { Args: Args2, CLTypeKey, CLValue: CLValue2, Key } = casperSdk2;
412
+ function getSwapEntryPoint(isFirstTokenNative, isSecondTokenNative, quoteType) {
413
+ if (isFirstTokenNative) {
414
+ return quoteType === "exact_in" ? "swap_exact_cspr_for_tokens" : "swap_cspr_for_exact_tokens";
415
+ }
416
+ if (isSecondTokenNative) {
417
+ return quoteType === "exact_in" ? "swap_exact_tokens_for_cspr" : "swap_tokens_for_exact_cspr";
418
+ }
419
+ return quoteType === "exact_in" ? "swap_exact_tokens_for_tokens" : "swap_tokens_for_exact_tokens";
420
+ }
421
+ function buildSwapInnerArgs(params) {
422
+ const {
423
+ isFirstTokenNative,
424
+ isSecondTokenNative,
425
+ quoteType,
426
+ path,
427
+ accountHash,
428
+ deadline,
429
+ amountIn,
430
+ amountOut,
431
+ amountInMax,
432
+ amountOutMin
433
+ } = params;
434
+ const isBothNotNative = !isFirstTokenNative && !isSecondTokenNative;
435
+ const argsMap = {
436
+ path: CLValue2.newCLList(
437
+ CLTypeKey,
438
+ path.map((hash) => CLValue2.newCLKey(Key.newKey(hash)))
439
+ ),
440
+ to: CLValue2.newCLKey(Key.newKey(accountHash)),
441
+ deadline: CLValue2.newCLUint64(deadline)
442
+ };
443
+ if (isFirstTokenNative && quoteType === "exact_in") {
444
+ argsMap.amount_out_min = CLValue2.newCLUInt256(amountOutMin);
445
+ }
446
+ if (isFirstTokenNative && quoteType === "exact_out") {
447
+ argsMap.amount_out = CLValue2.newCLUInt256(amountOut);
448
+ }
449
+ if ((isSecondTokenNative || isBothNotNative) && quoteType === "exact_in") {
450
+ argsMap.amount_in = CLValue2.newCLUInt256(amountIn);
451
+ argsMap.amount_out_min = CLValue2.newCLUInt256(amountOutMin);
452
+ }
453
+ if ((isSecondTokenNative || isBothNotNative) && quoteType === "exact_out") {
454
+ argsMap.amount_in_max = CLValue2.newCLUInt256(amountInMax);
455
+ argsMap.amount_out = CLValue2.newCLUInt256(amountOut);
456
+ }
457
+ return Args2.fromMap(argsMap);
458
+ }
459
+ function getSwapAttachedValue(params) {
460
+ if (!params.isFirstTokenNative) return "0";
461
+ return params.quoteType === "exact_in" ? params.amountIn : params.amountInMax;
462
+ }
463
+
464
+ // src/transactions/liquidity.ts
465
+ import casperSdk3 from "casper-js-sdk";
466
+ var { Args: Args3, CLValue: CLValue3, Key: Key2 } = casperSdk3;
467
+ function buildAddLiquidityInnerArgs(params) {
468
+ if (params.isCSPRPair) {
469
+ return Args3.fromMap({
470
+ token: CLValue3.newCLKey(Key2.newKey(params.tokenHash)),
471
+ amount_token_desired: CLValue3.newCLUInt256(params.amountTokenDesired),
472
+ amount_token_min: CLValue3.newCLUInt256(params.amountTokenMin),
473
+ amount_cspr_min: CLValue3.newCLUInt256(params.amountCSPRMin),
474
+ to: CLValue3.newCLKey(Key2.newKey(params.accountHash)),
475
+ deadline: CLValue3.newCLUint64(params.deadline)
476
+ });
477
+ }
478
+ return Args3.fromMap({
479
+ token_a: CLValue3.newCLKey(Key2.newKey(params.tokenAHash)),
480
+ token_b: CLValue3.newCLKey(Key2.newKey(params.tokenBHash)),
481
+ amount_a_desired: CLValue3.newCLUInt256(params.amountADesired),
482
+ amount_b_desired: CLValue3.newCLUInt256(params.amountBDesired),
483
+ amount_a_min: CLValue3.newCLUInt256(params.amountAMin),
484
+ amount_b_min: CLValue3.newCLUInt256(params.amountBMin),
485
+ to: CLValue3.newCLKey(Key2.newKey(params.accountHash)),
486
+ deadline: CLValue3.newCLUint64(params.deadline)
487
+ });
488
+ }
489
+ function buildRemoveLiquidityInnerArgs(params) {
490
+ if (params.isCSPRPair) {
491
+ return Args3.fromMap({
492
+ token: CLValue3.newCLKey(Key2.newKey(params.tokenHash)),
493
+ liquidity: CLValue3.newCLUInt256(params.liquidity),
494
+ amount_token_min: CLValue3.newCLUInt256(params.amountTokenMin),
495
+ amount_cspr_min: CLValue3.newCLUInt256(params.amountCSPRMin),
496
+ to: CLValue3.newCLKey(Key2.newKey(params.accountHash)),
497
+ deadline: CLValue3.newCLUint64(params.deadline)
498
+ });
499
+ }
500
+ return Args3.fromMap({
501
+ token_a: CLValue3.newCLKey(Key2.newKey(params.tokenAHash)),
502
+ token_b: CLValue3.newCLKey(Key2.newKey(params.tokenBHash)),
503
+ liquidity: CLValue3.newCLUInt256(params.liquidity),
504
+ amount_a_min: CLValue3.newCLUInt256(params.amountAMin),
505
+ amount_b_min: CLValue3.newCLUInt256(params.amountBMin),
506
+ to: CLValue3.newCLKey(Key2.newKey(params.accountHash)),
507
+ deadline: CLValue3.newCLUint64(params.deadline)
508
+ });
509
+ }
510
+
511
+ // src/transactions/approve.ts
512
+ import casperSdk4 from "casper-js-sdk";
513
+ var { Args: Args4, CLValue: CLValue4, Key: Key3 } = casperSdk4;
514
+ function buildApproveArgs(params) {
515
+ return Args4.fromMap({
516
+ spender: CLValue4.newCLKey(Key3.newKey(params.spenderPackageHash)),
517
+ amount: CLValue4.newCLUInt256(params.amount)
518
+ });
519
+ }
520
+
521
+ // src/transactions/transaction-builder.ts
522
+ import casperSdk5 from "casper-js-sdk";
523
+ var { SessionBuilder, ContractCallBuilder, PublicKey } = casperSdk5;
524
+ function buildWasmTransaction(params) {
525
+ const { publicKey, paymentAmount, wasmBinary, runtimeArgs, networkConfig } = params;
526
+ return new SessionBuilder().from(PublicKey.fromHex(publicKey)).wasm(wasmBinary).installOrUpgrade().runtimeArgs(runtimeArgs).chainName(networkConfig.chainName).payment(Number(paymentAmount)).ttl(networkConfig.ttl).build();
527
+ }
528
+ function buildContractCallTransaction(params) {
529
+ const { publicKey, paymentAmount, contractPackageHash, entryPoint, runtimeArgs, networkConfig } = params;
530
+ const hash = contractPackageHash.replace("hash-", "");
531
+ return new ContractCallBuilder().from(PublicKey.fromHex(publicKey)).byPackageHash(hash).entryPoint(entryPoint).runtimeArgs(runtimeArgs).chainName(networkConfig.chainName).payment(Number(paymentAmount)).ttl(networkConfig.ttl).build();
532
+ }
533
+
534
+ // src/assets/index.ts
535
+ import { readFile } from "fs/promises";
536
+ import { fileURLToPath } from "url";
537
+ import { dirname, join } from "path";
538
+ var cachedWasm = null;
539
+ async function getProxyCallerWasm() {
540
+ if (cachedWasm) return cachedWasm;
541
+ const currentDir = dirname(fileURLToPath(import.meta.url));
542
+ let buffer;
543
+ try {
544
+ buffer = await readFile(join(currentDir, "assets", "proxy_caller.wasm"));
545
+ } catch {
546
+ buffer = await readFile(join(currentDir, "proxy_caller.wasm"));
547
+ }
548
+ cachedWasm = new Uint8Array(buffer);
549
+ return cachedWasm;
550
+ }
551
+
552
+ // src/client.ts
553
+ var { PublicKey: PublicKey2, HttpHandler, RpcClient, Transaction } = casperSdk6;
554
+ var CsprTradeClient = class {
555
+ http;
556
+ tokensApi;
557
+ pairsApi;
558
+ quotesApi;
559
+ liquidityApi;
560
+ ratesApi;
561
+ currenciesApi;
562
+ swapsApi;
563
+ tokenResolver;
564
+ currencyResolver;
565
+ networkConfig;
566
+ signer;
567
+ constructor(config) {
568
+ const baseConfig = getNetworkConfig(config.network);
569
+ this.networkConfig = {
570
+ ...baseConfig,
571
+ apiUrl: config.apiUrl ?? baseConfig.apiUrl,
572
+ routerPackageHash: config.routerPackageHash ?? baseConfig.routerPackageHash,
573
+ wcsprPackageHash: config.wcsprPackageHash ?? baseConfig.wcsprPackageHash
574
+ };
575
+ this.signer = config.signer;
576
+ this.http = new HttpClient(this.networkConfig.apiUrl);
577
+ this.tokensApi = new TokensApi(this.http, this.networkConfig.wcsprPackageHash);
578
+ this.pairsApi = new PairsApi(this.http);
579
+ this.quotesApi = new QuotesApi(this.http);
580
+ this.liquidityApi = new LiquidityApi(this.http);
581
+ this.ratesApi = new RatesApi(this.http);
582
+ this.currenciesApi = new CurrenciesApi(this.http);
583
+ this.swapsApi = new SwapsApi(this.http);
584
+ this.tokenResolver = new TokenResolver(() => this.tokensApi.getTokens());
585
+ this.currencyResolver = new CurrencyResolver(() => this.currenciesApi.getCurrencies());
586
+ }
587
+ // --- Market Data ---
588
+ async getTokens(currency) {
589
+ const currencyId = currency ? await this.currencyResolver.resolveToId(currency) : void 0;
590
+ return this.tokensApi.getTokens(currencyId);
591
+ }
592
+ async getPairs(opts) {
593
+ const currencyId = opts?.currency ? await this.currencyResolver.resolveToId(opts.currency) : void 0;
594
+ return this.pairsApi.getPairs({ ...opts, currencyId });
595
+ }
596
+ async getPairDetails(pairIdentifier, currency) {
597
+ const currencyId = currency ? await this.currencyResolver.resolveToId(currency) : void 0;
598
+ return this.pairsApi.getPairDetails(pairIdentifier, currencyId);
599
+ }
600
+ async getQuote(params) {
601
+ const tokenIn = await this.tokenResolver.resolve(params.tokenIn);
602
+ const tokenOut = await this.tokenResolver.resolve(params.tokenOut);
603
+ const rawAmount = toRawAmount(params.amount, params.type === "exact_in" ? tokenIn.decimals : tokenOut.decimals);
604
+ const quoteTokenIn = tokenIn.id === CSPR_TOKEN_ID ? ZERO_HASH : tokenIn.packageHash;
605
+ const quoteTokenOut = tokenOut.id === CSPR_TOKEN_ID ? ZERO_HASH : tokenOut.packageHash;
606
+ const apiQuote = await this.quotesApi.getQuote({
607
+ tokenIn: quoteTokenIn,
608
+ tokenOut: quoteTokenOut,
609
+ amount: rawAmount,
610
+ typeId: params.type === "exact_in" ? 1 : 2
611
+ });
612
+ const tokens = await this.tokenResolver.getTokens();
613
+ const pathSymbols = apiQuote.path.map((hash) => {
614
+ const wcsprHash = this.networkConfig.wcsprPackageHash.replace("hash-", "");
615
+ if (hash.replace("hash-", "") === wcsprHash) return "CSPR";
616
+ const t = tokens.find((tk) => tk.packageHash.replace("hash-", "") === hash.replace("hash-", ""));
617
+ return t?.symbol ?? hash.slice(0, 12) + "...";
618
+ });
619
+ return {
620
+ amountIn: apiQuote.amount_in,
621
+ amountOut: apiQuote.amount_out,
622
+ amountInFormatted: toFormattedAmount(apiQuote.amount_in, tokenIn.decimals),
623
+ amountOutFormatted: toFormattedAmount(apiQuote.amount_out, tokenOut.decimals),
624
+ executionPrice: apiQuote.execution_price,
625
+ midPrice: apiQuote.mid_price,
626
+ path: apiQuote.path,
627
+ pathSymbols,
628
+ priceImpact: apiQuote.price_impact,
629
+ recommendedSlippageBps: apiQuote.recommended_slippage_bps,
630
+ type: params.type,
631
+ tokenInSymbol: tokenIn.symbol,
632
+ tokenOutSymbol: tokenOut.symbol,
633
+ tokenInDecimals: tokenIn.decimals,
634
+ tokenOutDecimals: tokenOut.decimals
635
+ };
636
+ }
637
+ async getCurrencies() {
638
+ return this.currenciesApi.getCurrencies();
639
+ }
640
+ // --- Account Data ---
641
+ async getLiquidityPositions(publicKey, currency) {
642
+ const currencyId = currency ? await this.currencyResolver.resolveToId(currency) : void 0;
643
+ const raw = await this.liquidityApi.getPositions(publicKey, currencyId);
644
+ return raw.map(mapLiquidityPosition);
645
+ }
646
+ async getImpermanentLoss(publicKey, pairHash) {
647
+ const raw = await this.liquidityApi.getImpermanentLoss(publicKey, pairHash);
648
+ return {
649
+ pairContractPackageHash: raw.pair_contract_package_hash,
650
+ value: raw.value,
651
+ timestamp: raw.timestamp
652
+ };
653
+ }
654
+ async getSwapHistory(opts) {
655
+ const accountHash = opts?.publicKey ? PublicKey2.fromHex(opts.publicKey).accountHash().toHex() : void 0;
656
+ return this.swapsApi.getSwaps({
657
+ senderAccountHash: accountHash,
658
+ pairContractPackageHash: opts?.pairContractPackageHash,
659
+ page: opts?.page,
660
+ pageSize: opts?.pageSize
661
+ });
662
+ }
663
+ // --- Transaction Building ---
664
+ async buildSwap(params) {
665
+ const tokenIn = await this.tokenResolver.resolve(params.tokenIn);
666
+ const tokenOut = await this.tokenResolver.resolve(params.tokenOut);
667
+ const quote = await this.getQuote({
668
+ tokenIn: params.tokenIn,
669
+ tokenOut: params.tokenOut,
670
+ amount: params.amount,
671
+ type: params.type
672
+ });
673
+ const slippageBps = params.slippageBps ?? DEFAULT_SLIPPAGE_BPS;
674
+ const deadlineMinutes = params.deadlineMinutes ?? DEFAULT_DEADLINE_MINUTES;
675
+ const deadline = Date.now() + deadlineMinutes * 60 * 1e3;
676
+ const isFirstTokenNative = tokenIn.id === CSPR_TOKEN_ID;
677
+ const isSecondTokenNative = tokenOut.id === CSPR_TOKEN_ID;
678
+ const amountOutMin = calculateMinWithSlippage(quote.amountOut, slippageBps);
679
+ const amountInMax = calculateMaxWithSlippage(quote.amountIn, slippageBps);
680
+ const accountHash = PublicKey2.fromHex(params.senderPublicKey).accountHash().toPrefixedString();
681
+ const path = quote.path.map((h) => h.startsWith("hash-") ? h : `hash-${h}`);
682
+ const entryPoint = getSwapEntryPoint(isFirstTokenNative, isSecondTokenNative, params.type);
683
+ const innerArgs = buildSwapInnerArgs({
684
+ isFirstTokenNative,
685
+ isSecondTokenNative,
686
+ quoteType: params.type,
687
+ path,
688
+ accountHash,
689
+ deadline,
690
+ amountIn: quote.amountIn,
691
+ amountOut: quote.amountOut,
692
+ amountInMax,
693
+ amountOutMin
694
+ });
695
+ const attachedValue = getSwapAttachedValue({
696
+ isFirstTokenNative,
697
+ quoteType: params.type,
698
+ amountIn: quote.amountIn,
699
+ amountInMax
700
+ });
701
+ const isBothNotNative = !isFirstTokenNative && !isSecondTokenNative;
702
+ const gasCost = isBothNotNative ? GAS_COSTS.swapTokenForToken : GAS_COSTS.swapCsprForToken;
703
+ const proxyArgs = buildProxyWasmArgs({
704
+ routerPackageHash: this.networkConfig.routerPackageHash.replace("hash-", ""),
705
+ entryPoint,
706
+ innerArgs,
707
+ attachedValue
708
+ });
709
+ const wasmBinary = await getProxyCallerWasm();
710
+ const transaction = buildWasmTransaction({
711
+ publicKey: params.senderPublicKey,
712
+ paymentAmount: gasCost.toString(),
713
+ wasmBinary,
714
+ runtimeArgs: proxyArgs,
715
+ networkConfig: this.networkConfig
716
+ });
717
+ const warnings = buildWarnings(quote.priceImpact, slippageBps);
718
+ const approvals = [];
719
+ if (!isFirstTokenNative) {
720
+ const approvalAmount = params.tokenInBalance ?? quote.amountIn;
721
+ const approval = await this.buildApproval({
722
+ tokenContractPackageHash: tokenIn.packageHash,
723
+ spenderPackageHash: this.networkConfig.routerPackageHash,
724
+ amount: approvalAmount,
725
+ senderPublicKey: params.senderPublicKey
726
+ });
727
+ approval.summary = params.tokenInBalance ? `Approve ${tokenIn.symbol} for router (full balance \u2014 one-time)` : `Approve ${tokenIn.symbol} for router (swap amount only)`;
728
+ approvals.push(approval);
729
+ }
730
+ const slippagePct = (slippageBps / 100).toFixed(2);
731
+ const summary = [
732
+ `Swap ${quote.amountInFormatted} ${tokenIn.symbol} for ~${quote.amountOutFormatted} ${tokenOut.symbol}`,
733
+ `Route: ${quote.pathSymbols.join(" \u2192 ")}`,
734
+ `Price impact: ${quote.priceImpact}%`,
735
+ `Max slippage: ${slippagePct}%`,
736
+ `Deadline: ${deadlineMinutes} minutes`,
737
+ `Estimated gas: ${Number(gasCost) / 1e9} CSPR`
738
+ ].join("\n");
739
+ return {
740
+ transactionJson: JSON.stringify(transaction.toJSON()),
741
+ summary,
742
+ estimatedGasCost: `${Number(gasCost) / 1e9} CSPR`,
743
+ approvalsRequired: approvals.length > 0 ? approvals : void 0,
744
+ warnings
745
+ };
746
+ }
747
+ async buildApproval(params) {
748
+ const spender = params.spenderPackageHash || this.networkConfig.routerPackageHash;
749
+ const args = buildApproveArgs({
750
+ spenderPackageHash: spender,
751
+ amount: params.amount
752
+ });
753
+ const transaction = buildContractCallTransaction({
754
+ publicKey: params.senderPublicKey,
755
+ paymentAmount: GAS_COSTS.approve.toString(),
756
+ contractPackageHash: params.tokenContractPackageHash,
757
+ entryPoint: "approve",
758
+ runtimeArgs: args,
759
+ networkConfig: this.networkConfig
760
+ });
761
+ return {
762
+ transactionJson: JSON.stringify(transaction.toJSON()),
763
+ summary: `Approve token spending for ${params.tokenContractPackageHash.slice(0, 16)}...`,
764
+ estimatedGasCost: `${Number(GAS_COSTS.approve) / 1e9} CSPR`,
765
+ warnings: []
766
+ };
767
+ }
768
+ async buildAddLiquidity(params) {
769
+ const tokenA = await this.tokenResolver.resolve(params.tokenA);
770
+ const tokenB = await this.tokenResolver.resolve(params.tokenB);
771
+ const slippageBps = params.slippageBps ?? DEFAULT_SLIPPAGE_BPS;
772
+ const deadlineMinutes = params.deadlineMinutes ?? DEFAULT_DEADLINE_MINUTES;
773
+ const deadline = Date.now() + deadlineMinutes * 60 * 1e3;
774
+ const isCSPRPair = tokenA.id === CSPR_TOKEN_ID || tokenB.id === CSPR_TOKEN_ID;
775
+ const rawAmountA = toRawAmount(params.amountA, tokenA.decimals);
776
+ const rawAmountB = toRawAmount(params.amountB, tokenB.decimals);
777
+ const accountHash = PublicKey2.fromHex(params.senderPublicKey).accountHash().toPrefixedString();
778
+ let innerArgs;
779
+ let entryPoint;
780
+ let attachedValue = "0";
781
+ if (isCSPRPair) {
782
+ const csprToken = tokenA.id === CSPR_TOKEN_ID ? tokenA : tokenB;
783
+ const otherToken = tokenA.id === CSPR_TOKEN_ID ? tokenB : tokenA;
784
+ const motesAmount = tokenA.id === CSPR_TOKEN_ID ? rawAmountA : rawAmountB;
785
+ const tokenAmount = tokenA.id === CSPR_TOKEN_ID ? rawAmountB : rawAmountA;
786
+ innerArgs = buildAddLiquidityInnerArgs({
787
+ isCSPRPair: true,
788
+ tokenHash: otherToken.packageHash,
789
+ amountTokenDesired: tokenAmount,
790
+ amountTokenMin: calculateMinWithSlippage(tokenAmount, slippageBps),
791
+ amountCSPRMin: calculateMinWithSlippage(motesAmount, slippageBps),
792
+ accountHash,
793
+ deadline
794
+ });
795
+ entryPoint = "add_liquidity_cspr";
796
+ attachedValue = motesAmount;
797
+ } else {
798
+ const [sortedA, sortedB, sortedAmountA, sortedAmountB] = tokenA.packageHash < tokenB.packageHash ? [tokenA, tokenB, rawAmountA, rawAmountB] : [tokenB, tokenA, rawAmountB, rawAmountA];
799
+ innerArgs = buildAddLiquidityInnerArgs({
800
+ isCSPRPair: false,
801
+ tokenAHash: sortedA.packageHash,
802
+ tokenBHash: sortedB.packageHash,
803
+ amountADesired: sortedAmountA,
804
+ amountBDesired: sortedAmountB,
805
+ amountAMin: calculateMinWithSlippage(sortedAmountA, slippageBps),
806
+ amountBMin: calculateMinWithSlippage(sortedAmountB, slippageBps),
807
+ accountHash,
808
+ deadline
809
+ });
810
+ entryPoint = "add_liquidity";
811
+ }
812
+ const gasCost = GAS_COSTS.addLiquidity;
813
+ const proxyArgs = buildProxyWasmArgs({
814
+ routerPackageHash: this.networkConfig.routerPackageHash.replace("hash-", ""),
815
+ entryPoint,
816
+ innerArgs,
817
+ attachedValue
818
+ });
819
+ const wasmBinary = await getProxyCallerWasm();
820
+ const transaction = buildWasmTransaction({
821
+ publicKey: params.senderPublicKey,
822
+ paymentAmount: gasCost.toString(),
823
+ wasmBinary,
824
+ runtimeArgs: proxyArgs,
825
+ networkConfig: this.networkConfig
826
+ });
827
+ const tokenBalances = [params.tokenABalance, params.tokenBBalance];
828
+ const rawAmounts = [rawAmountA, rawAmountB];
829
+ const approvals = [];
830
+ for (let i = 0; i < 2; i++) {
831
+ const token = [tokenA, tokenB][i];
832
+ if (token.id === CSPR_TOKEN_ID) continue;
833
+ const approvalAmount = tokenBalances[i] ?? rawAmounts[i];
834
+ const approval = await this.buildApproval({
835
+ tokenContractPackageHash: token.packageHash,
836
+ spenderPackageHash: this.networkConfig.routerPackageHash,
837
+ amount: approvalAmount,
838
+ senderPublicKey: params.senderPublicKey
839
+ });
840
+ approval.summary = tokenBalances[i] ? `Approve ${token.symbol} for router (full balance \u2014 one-time)` : `Approve ${token.symbol} for router (liquidity amount only)`;
841
+ approvals.push(approval);
842
+ }
843
+ const summary = [
844
+ `Add liquidity: ${params.amountA} ${tokenA.symbol} + ${params.amountB} ${tokenB.symbol}`,
845
+ `Slippage tolerance: ${(slippageBps / 100).toFixed(2)}%`,
846
+ `Deadline: ${deadlineMinutes} minutes`,
847
+ `Estimated gas: ${Number(gasCost) / 1e9} CSPR`
848
+ ].join("\n");
849
+ return {
850
+ transactionJson: JSON.stringify(transaction.toJSON()),
851
+ summary,
852
+ estimatedGasCost: `${Number(gasCost) / 1e9} CSPR`,
853
+ approvalsRequired: approvals.length > 0 ? approvals : void 0,
854
+ warnings: []
855
+ };
856
+ }
857
+ async buildRemoveLiquidity(params) {
858
+ const positions = await this.liquidityApi.getPositions(params.senderPublicKey);
859
+ const position = positions.find(
860
+ (p) => p.pair_contract_package_hash === params.pairContractPackageHash
861
+ );
862
+ if (!position) {
863
+ throw new Error(`No liquidity position found for pair ${params.pairContractPackageHash}`);
864
+ }
865
+ const slippageBps = params.slippageBps ?? DEFAULT_SLIPPAGE_BPS;
866
+ const deadlineMinutes = params.deadlineMinutes ?? DEFAULT_DEADLINE_MINUTES;
867
+ const deadline = Date.now() + deadlineMinutes * 60 * 1e3;
868
+ const accountHash = PublicKey2.fromHex(params.senderPublicKey).accountHash().toPrefixedString();
869
+ const lpBalance = BigInt(position.lp_token_balance);
870
+ const lpToBurn = lpBalance * BigInt(params.percentage) / 100n;
871
+ const lpTotalSupply = BigInt(position.pair_lp_tokens_total_supply);
872
+ const reserve0 = BigInt(position.pair.reserve0);
873
+ const reserve1 = BigInt(position.pair.reserve1);
874
+ const estAmount0 = (lpToBurn * reserve0 / lpTotalSupply).toString();
875
+ const estAmount1 = (lpToBurn * reserve1 / lpTotalSupply).toString();
876
+ const wcsprHash = this.networkConfig.wcsprPackageHash.replace("hash-", "");
877
+ const isCSPRPair = position.pair.token0_contract_package_hash.replace("hash-", "") === wcsprHash || position.pair.token1_contract_package_hash.replace("hash-", "") === wcsprHash;
878
+ let innerArgs;
879
+ let entryPoint;
880
+ if (isCSPRPair) {
881
+ const isToken0CSPR = position.pair.token0_contract_package_hash.replace("hash-", "") === wcsprHash;
882
+ innerArgs = buildRemoveLiquidityInnerArgs({
883
+ isCSPRPair: true,
884
+ tokenHash: isToken0CSPR ? position.pair.token1_contract_package_hash : position.pair.token0_contract_package_hash,
885
+ liquidity: lpToBurn.toString(),
886
+ amountTokenMin: calculateMinWithSlippage(isToken0CSPR ? estAmount1 : estAmount0, slippageBps),
887
+ amountCSPRMin: calculateMinWithSlippage(isToken0CSPR ? estAmount0 : estAmount1, slippageBps),
888
+ accountHash,
889
+ deadline
890
+ });
891
+ entryPoint = "remove_liquidity_cspr";
892
+ } else {
893
+ innerArgs = buildRemoveLiquidityInnerArgs({
894
+ isCSPRPair: false,
895
+ tokenAHash: position.pair.token0_contract_package_hash,
896
+ tokenBHash: position.pair.token1_contract_package_hash,
897
+ liquidity: lpToBurn.toString(),
898
+ amountAMin: calculateMinWithSlippage(estAmount0, slippageBps),
899
+ amountBMin: calculateMinWithSlippage(estAmount1, slippageBps),
900
+ accountHash,
901
+ deadline
902
+ });
903
+ entryPoint = "remove_liquidity";
904
+ }
905
+ const proxyArgs = buildProxyWasmArgs({
906
+ routerPackageHash: this.networkConfig.routerPackageHash.replace("hash-", ""),
907
+ entryPoint,
908
+ innerArgs,
909
+ attachedValue: "0"
910
+ });
911
+ const wasmBinary = await getProxyCallerWasm();
912
+ const transaction = buildWasmTransaction({
913
+ publicKey: params.senderPublicKey,
914
+ paymentAmount: GAS_COSTS.removeLiquidity.toString(),
915
+ wasmBinary,
916
+ runtimeArgs: proxyArgs,
917
+ networkConfig: this.networkConfig
918
+ });
919
+ const summary = [
920
+ `Remove ${params.percentage}% liquidity from pair ${params.pairContractPackageHash.slice(0, 16)}...`,
921
+ `LP tokens to burn: ${lpToBurn.toString()}`,
922
+ `Estimated gas: ${Number(GAS_COSTS.removeLiquidity) / 1e9} CSPR`
923
+ ].join("\n");
924
+ return {
925
+ transactionJson: JSON.stringify(transaction.toJSON()),
926
+ summary,
927
+ estimatedGasCost: `${Number(GAS_COSTS.removeLiquidity) / 1e9} CSPR`,
928
+ warnings: []
929
+ };
930
+ }
931
+ // --- Transaction Submission ---
932
+ /** Submit a signed transaction to the Casper node RPC */
933
+ async submitTransaction(signedTransactionJson) {
934
+ const handler = new HttpHandler(this.networkConfig.nodeRpcUrl);
935
+ const rpcClient = new RpcClient(handler);
936
+ const transaction = Transaction.fromJSON(JSON.parse(signedTransactionJson));
937
+ const result = await rpcClient.putTransaction(transaction);
938
+ return {
939
+ transactionHash: result.transactionHash?.toHex() ?? ""
940
+ };
941
+ }
942
+ /** @deprecated Use submitTransaction instead — all transactions now submit via node RPC */
943
+ async submitTransactionDirect(signedTransactionJson) {
944
+ return this.submitTransaction(signedTransactionJson);
945
+ }
946
+ // --- Token Resolution ---
947
+ async resolveToken(identifier) {
948
+ return this.tokenResolver.resolve(identifier);
949
+ }
950
+ };
951
+ function mapLiquidityPosition(raw) {
952
+ const lpBalance = BigInt(raw.lp_token_balance);
953
+ const totalSupply = BigInt(raw.pair_lp_tokens_total_supply);
954
+ const reserve0 = BigInt(raw.pair.reserve0);
955
+ const reserve1 = BigInt(raw.pair.reserve1);
956
+ const poolShare = totalSupply > 0n ? (lpBalance * 10000n / totalSupply).toString() : "0";
957
+ const estToken0 = totalSupply > 0n ? (lpBalance * reserve0 / totalSupply).toString() : "0";
958
+ const estToken1 = totalSupply > 0n ? (lpBalance * reserve1 / totalSupply).toString() : "0";
959
+ const meta0 = raw.pair.token0_contract_package?.metadata;
960
+ const meta1 = raw.pair.token1_contract_package?.metadata;
961
+ return {
962
+ accountHash: raw.account_hash,
963
+ pairContractPackageHash: raw.pair_contract_package_hash,
964
+ lpTokenBalance: raw.lp_token_balance,
965
+ lpTokenTotalSupply: raw.pair_lp_tokens_total_supply,
966
+ pair: {
967
+ token0Symbol: meta0?.symbol ?? "",
968
+ token1Symbol: meta1?.symbol ?? "",
969
+ token0PackageHash: raw.pair.token0_contract_package_hash,
970
+ token1PackageHash: raw.pair.token1_contract_package_hash,
971
+ reserve0: raw.pair.reserve0,
972
+ reserve1: raw.pair.reserve1,
973
+ decimals0: raw.pair.decimals0,
974
+ decimals1: raw.pair.decimals1
975
+ },
976
+ poolShare: (Number(poolShare) / 100).toFixed(2),
977
+ estimatedToken0Amount: estToken0,
978
+ estimatedToken1Amount: estToken1
979
+ };
980
+ }
981
+ function buildWarnings(priceImpact, slippageBps) {
982
+ const warnings = [];
983
+ const impact = parseFloat(priceImpact);
984
+ if (impact > PRICE_IMPACT_HIGH_THRESHOLD) {
985
+ warnings.push(`HIGH PRICE IMPACT: ${priceImpact}% \u2014 you will lose a significant portion of your trade to price impact.`);
986
+ } else if (impact > PRICE_IMPACT_WARNING_THRESHOLD) {
987
+ warnings.push(`Price impact is ${priceImpact}% \u2014 consider trading a smaller amount.`);
988
+ }
989
+ if (slippageBps / 100 > SLIPPAGE_WARNING_THRESHOLD) {
990
+ warnings.push(`High slippage tolerance: ${(slippageBps / 100).toFixed(2)}% \u2014 you may receive significantly less than quoted.`);
991
+ }
992
+ return warnings;
993
+ }
994
+ export {
995
+ CsprTradeClient,
996
+ MAINNET_CONFIG,
997
+ TESTNET_CONFIG,
998
+ calculateMaxWithSlippage,
999
+ calculateMinWithSlippage,
1000
+ getNetworkConfig,
1001
+ toFormattedAmount,
1002
+ toRawAmount
1003
+ };