@sun-protocol/sun-sdk-protocols 0.1.0-beta.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 (60) hide show
  1. package/README.md +30 -0
  2. package/dist/chunk-5XDX5563.js +99 -0
  3. package/dist/chunk-5XDX5563.js.map +1 -0
  4. package/dist/chunk-C2YCVBPI.js +212 -0
  5. package/dist/chunk-C2YCVBPI.js.map +1 -0
  6. package/dist/chunk-ECCGOXNA.js +1317 -0
  7. package/dist/chunk-ECCGOXNA.js.map +1 -0
  8. package/dist/chunk-HD2S3Q7L.js +746 -0
  9. package/dist/chunk-HD2S3Q7L.js.map +1 -0
  10. package/dist/chunk-L5TZCDPH.js +561 -0
  11. package/dist/chunk-L5TZCDPH.js.map +1 -0
  12. package/dist/chunk-OM4OW4KB.js +224 -0
  13. package/dist/chunk-OM4OW4KB.js.map +1 -0
  14. package/dist/chunk-PEGREDLT.js +935 -0
  15. package/dist/chunk-PEGREDLT.js.map +1 -0
  16. package/dist/chunk-TSHXXRH2.js +103 -0
  17. package/dist/chunk-TSHXXRH2.js.map +1 -0
  18. package/dist/chunk-VFVKMC4H.js +521 -0
  19. package/dist/chunk-VFVKMC4H.js.map +1 -0
  20. package/dist/chunk-YMGZDXI4.js +1209 -0
  21. package/dist/chunk-YMGZDXI4.js.map +1 -0
  22. package/dist/common-8VQQ0FSU.d.cts +146 -0
  23. package/dist/common-8VQQ0FSU.d.ts +146 -0
  24. package/dist/execute-BNKh6dT6.d.cts +187 -0
  25. package/dist/execute-CVhcKZxF.d.ts +187 -0
  26. package/dist/index.cjs +5619 -0
  27. package/dist/index.cjs.map +1 -0
  28. package/dist/index.d.cts +139 -0
  29. package/dist/index.d.ts +139 -0
  30. package/dist/index.js +200 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/intents-BSXq1HvW.d.cts +200 -0
  33. package/dist/intents-CwOttD75.d.ts +200 -0
  34. package/dist/liquidity/index.cjs +2441 -0
  35. package/dist/liquidity/index.cjs.map +1 -0
  36. package/dist/liquidity/index.d.cts +83 -0
  37. package/dist/liquidity/index.d.ts +83 -0
  38. package/dist/liquidity/index.js +32 -0
  39. package/dist/liquidity/index.js.map +1 -0
  40. package/dist/positions/index.cjs +1544 -0
  41. package/dist/positions/index.cjs.map +1 -0
  42. package/dist/positions/index.d.cts +159 -0
  43. package/dist/positions/index.d.ts +159 -0
  44. package/dist/positions/index.js +25 -0
  45. package/dist/positions/index.js.map +1 -0
  46. package/dist/pump/index.cjs +1178 -0
  47. package/dist/pump/index.cjs.map +1 -0
  48. package/dist/pump/index.d.cts +16 -0
  49. package/dist/pump/index.d.ts +16 -0
  50. package/dist/pump/index.js +29 -0
  51. package/dist/pump/index.js.map +1 -0
  52. package/dist/swap/index.cjs +2259 -0
  53. package/dist/swap/index.cjs.map +1 -0
  54. package/dist/swap/index.d.cts +33 -0
  55. package/dist/swap/index.d.ts +33 -0
  56. package/dist/swap/index.js +61 -0
  57. package/dist/swap/index.js.map +1 -0
  58. package/dist/types-BTPdCC7k.d.cts +160 -0
  59. package/dist/types-BfrouFmO.d.ts +160 -0
  60. package/package.json +94 -0
@@ -0,0 +1,1317 @@
1
+ import {
2
+ planRouterPermit2Authorization
3
+ } from "./chunk-OM4OW4KB.js";
4
+ import {
5
+ assertUsdtTransfer
6
+ } from "./chunk-5XDX5563.js";
7
+ import {
8
+ normalizeTronAddress,
9
+ planApproval,
10
+ readRequiredContract
11
+ } from "./chunk-C2YCVBPI.js";
12
+ import {
13
+ createDeferredV2SwapAction,
14
+ createV2SwapExecutor
15
+ } from "./chunk-TSHXXRH2.js";
16
+ import {
17
+ applySlippageMax,
18
+ applySlippageMin,
19
+ attachApprovalIntent,
20
+ createContextExecutor,
21
+ createProtocolsContext,
22
+ executePlanAction,
23
+ executePlanTransactions,
24
+ executeTransactionPlan,
25
+ isTransactionPlan,
26
+ nextPlanId,
27
+ normalizeBaseUnitAmount,
28
+ normalizeFeeLimit,
29
+ normalizeRecipient,
30
+ normalizeTransactionValidForSeconds,
31
+ requireRuntime,
32
+ requireWalletSnapshot,
33
+ resolveProtocolContract,
34
+ resolveTransactionDeadline,
35
+ slippageToBips
36
+ } from "./chunk-VFVKMC4H.js";
37
+
38
+ // src/swap/intents.ts
39
+ import {
40
+ SDKError
41
+ } from "@sun-protocol/sun-sdk-core";
42
+ var UNIVERSAL_ROUTER_PROTOCOL_ID = "universal-router";
43
+ var PSM_PROTOCOL_ID = "psm";
44
+ var WTRX_PROTOCOL_ID = "wtrx";
45
+ function swapIntentScope(protocol, operation) {
46
+ return {
47
+ domain: "swap",
48
+ protocol,
49
+ ...operation === void 0 ? {} : { operation }
50
+ };
51
+ }
52
+ function routerRouteSymbols(route) {
53
+ const symbols = route.symbols;
54
+ if (!Array.isArray(symbols) || symbols.length < 2 || symbols.some((symbol) => typeof symbol !== "string" || symbol.length === 0)) {
55
+ throw new SDKError("SDK_API_ERROR", "Selected Router route is missing token symbols");
56
+ }
57
+ return {
58
+ from: symbols[0],
59
+ to: symbols[symbols.length - 1]
60
+ };
61
+ }
62
+
63
+ // src/swap/psm.ts
64
+ import {
65
+ buildPsmBuyGemAction,
66
+ buildPsmInfoRead,
67
+ buildPsmSellGemAction,
68
+ createPsmSnapshot,
69
+ normalizePsmInfoResult,
70
+ quotePsm
71
+ } from "@sun-protocol/sun-sdk-psm";
72
+ import {
73
+ SDKError as SDKError3,
74
+ assertTronAddress,
75
+ formatUnits
76
+ } from "@sun-protocol/sun-sdk-core";
77
+
78
+ // src/swap/fee-limit.ts
79
+ var DEFAULT_WTRX_SWAP_FEE_LIMIT = 1e9;
80
+ var DEFAULT_UNIVERSAL_ROUTER_SWAP_FEE_LIMIT = 5e8;
81
+ var DEFAULT_PSM_SWAP_FEE_LIMIT = 25e7;
82
+ var DEFAULT_PSM_APPROVAL_FEE_LIMIT = 1e9;
83
+
84
+ // src/swap/workflow.ts
85
+ import { SDKError as SDKError2 } from "@sun-protocol/sun-sdk-core";
86
+ function assertSwapPlan(plan, protocols, clientName) {
87
+ const protocol = plan.metadata.protocol;
88
+ if (plan.kind !== "swap" || typeof protocol !== "string" || !protocols.includes(protocol) || plan.transactions.length === 0) {
89
+ throw new SDKError2("SDK_INVALID_ARGUMENT", `Expected a ${clientName} swap plan`, {
90
+ planId: plan.id,
91
+ kind: plan.kind,
92
+ protocol,
93
+ transactionCount: plan.transactions.length
94
+ });
95
+ }
96
+ }
97
+
98
+ // src/swap/psm.ts
99
+ function normalizePsmApprovalMode(value) {
100
+ if (value === void 0) return "auto";
101
+ if (value === "auto" || value === "approve" || value === "none") return value;
102
+ throw new SDKError3("SDK_INVALID_ARGUMENT", "unsupported PSM approval mode", {
103
+ approval: value
104
+ });
105
+ }
106
+ function resolvePsmContracts(context, overrides) {
107
+ return {
108
+ psmUsdt: resolveProtocolContract(context, overrides, "psmUsdt"),
109
+ psmUsdd: resolveProtocolContract(context, overrides, "psmUsdd"),
110
+ psm: resolveProtocolContract(context, overrides, "psm"),
111
+ psmGemJoin: resolveProtocolContract(context, overrides, "psmGemJoin"),
112
+ psmInfoReader: resolveProtocolContract(context, overrides, "psmInfoReader")
113
+ };
114
+ }
115
+ function invalidAmountShape(mode) {
116
+ throw new SDKError3(
117
+ "SDK_INVALID_ARGUMENT",
118
+ mode === "exact-in" ? "exact-in PSM swap requires only amountIn" : "exact-out PSM swap requires only amountOut",
119
+ { mode }
120
+ );
121
+ }
122
+ function normalizeSpecifiedAmount(value, label) {
123
+ if (typeof value !== "string") {
124
+ throw new SDKError3("SDK_INVALID_ARGUMENT", `${label} must be a string`, {
125
+ [label]: value
126
+ });
127
+ }
128
+ return normalizeBaseUnitAmount(value, label);
129
+ }
130
+ function normalizeQuoteInput(context, input) {
131
+ const mode = input.mode ?? "exact-in";
132
+ let specifiedAmount;
133
+ if (mode === "exact-in") {
134
+ if (input.amountIn === void 0 || input.amountOut !== void 0) return invalidAmountShape(mode);
135
+ specifiedAmount = normalizeSpecifiedAmount(input.amountIn, "amountIn");
136
+ } else if (mode === "exact-out") {
137
+ if (input.amountOut === void 0 || input.amountIn !== void 0) return invalidAmountShape(mode);
138
+ specifiedAmount = normalizeSpecifiedAmount(input.amountOut, "amountOut");
139
+ } else {
140
+ throw new SDKError3("SDK_INVALID_ARGUMENT", "unsupported PSM quote mode", { mode });
141
+ }
142
+ const tokenIn = assertTronAddress(input.tokenIn, "tokenIn");
143
+ const tokenOut = assertTronAddress(input.tokenOut, "tokenOut");
144
+ const { psmUsdt, psmUsdd } = resolvePsmContracts(context, input.contracts);
145
+ let direction;
146
+ if (tokenIn === psmUsdt && tokenOut === psmUsdd) {
147
+ direction = "usdt-to-usdd";
148
+ } else if (tokenIn === psmUsdd && tokenOut === psmUsdt) {
149
+ direction = "usdd-to-usdt";
150
+ } else {
151
+ throw new SDKError3("SDK_INVALID_ARGUMENT", "PSM swap only supports its configured USDT/USDD pair", {
152
+ tokenIn,
153
+ tokenOut,
154
+ psmUsdt,
155
+ psmUsdd
156
+ });
157
+ }
158
+ return { mode, direction, specifiedAmount, tokenIn, tokenOut };
159
+ }
160
+ async function readPsm(context, input = {}) {
161
+ const contracts = resolvePsmContracts(context, input.contracts);
162
+ const result = await readRequiredContract(
163
+ context,
164
+ buildPsmInfoRead({
165
+ network: context.network,
166
+ psm: contracts.psm,
167
+ infoReader: contracts.psmInfoReader
168
+ }),
169
+ "PSM info read"
170
+ );
171
+ return createPsmSnapshot(normalizePsmInfoResult(result));
172
+ }
173
+ async function quotePsmSwap(context, input) {
174
+ const normalized = normalizeQuoteInput(context, input);
175
+ const snapshot = await readPsm(context, input);
176
+ return {
177
+ ...quotePsm({
178
+ mode: normalized.mode,
179
+ direction: normalized.direction,
180
+ specifiedAmount: normalized.specifiedAmount,
181
+ snapshot
182
+ }),
183
+ kind: "psm",
184
+ source: "psm",
185
+ tokenIn: normalized.tokenIn,
186
+ tokenOut: normalized.tokenOut
187
+ };
188
+ }
189
+ async function planPsmSwap(context, input) {
190
+ const { address: owner } = await requireWalletSnapshot(context, "PSM swap plan");
191
+ const feeLimit = normalizeFeeLimit(input.feeLimit, DEFAULT_PSM_SWAP_FEE_LIMIT);
192
+ const quote = await quotePsmSwap(context, input);
193
+ const recipient = input.recipient !== void 0 ? assertTronAddress(input.recipient, "PSM recipient") : owner;
194
+ const contracts = resolvePsmContracts(context, input.contracts);
195
+ const tokenIn = quote.direction === "usdt-to-usdd" ? contracts.psmUsdt : contracts.psmUsdd;
196
+ const spender = quote.direction === "usdt-to-usdd" ? contracts.psmGemJoin : contracts.psm;
197
+ const approvalMode = normalizePsmApprovalMode(input.approval);
198
+ const approval = await planApproval(context, {
199
+ asset: { type: "token", token: tokenIn },
200
+ owner,
201
+ spender,
202
+ amount: quote.amountIn
203
+ }, {
204
+ mode: approvalMode,
205
+ amount: "max",
206
+ feeLimit: DEFAULT_PSM_APPROVAL_FEE_LIMIT
207
+ });
208
+ const inputSymbol = quote.direction === "usdt-to-usdd" ? "USDT" : "USDD";
209
+ const outputSymbol = quote.direction === "usdt-to-usdd" ? "USDD" : "USDT";
210
+ const inputDecimals = quote.direction === "usdt-to-usdd" ? 6 : 18;
211
+ const outputDecimals = quote.direction === "usdt-to-usdd" ? 18 : 6;
212
+ const intentScope = swapIntentScope(PSM_PROTOCOL_ID, quote.direction);
213
+ const planId = nextPlanId(context, "swap-psm");
214
+ const actionOptions = {
215
+ network: context.network,
216
+ psm: contracts.psm,
217
+ recipient,
218
+ gemAmount: quote.gemAmount,
219
+ feeLimit,
220
+ id: `${planId}-swap`
221
+ };
222
+ const transactionAction = quote.direction === "usdt-to-usdd" ? buildPsmSellGemAction(actionOptions) : buildPsmBuyGemAction(actionOptions);
223
+ const transaction = {
224
+ ...transactionAction,
225
+ intent: {
226
+ kind: "swap",
227
+ scope: intentScope,
228
+ from: { value: formatUnits(BigInt(quote.amountIn), inputDecimals), symbol: inputSymbol },
229
+ to: { value: formatUnits(BigInt(quote.amountOut), outputDecimals), symbol: outputSymbol },
230
+ ...input.recipient === void 0 ? {} : { recipient }
231
+ }
232
+ };
233
+ return {
234
+ id: planId,
235
+ kind: "swap",
236
+ network: context.network,
237
+ owner,
238
+ summary: {
239
+ title: "Swap with PSM",
240
+ lines: [
241
+ `Spend ${quote.amountIn} token units`,
242
+ `Receive ${quote.amountOut} token units`
243
+ ]
244
+ },
245
+ approvals: attachApprovalIntent(approval.actions, intentScope, inputSymbol),
246
+ transactions: [transaction],
247
+ metadata: {
248
+ protocol: "psm",
249
+ mode: quote.mode,
250
+ direction: quote.direction,
251
+ amountIn: quote.amountIn,
252
+ amountOut: quote.amountOut,
253
+ specifiedAmount: quote.specifiedAmount,
254
+ gemAmount: quote.gemAmount,
255
+ feeAmount: quote.feeAmount,
256
+ feeRate: quote.feeRate,
257
+ availableAmountOut: quote.availableAmountOut,
258
+ owner,
259
+ recipient,
260
+ spender,
261
+ approvalMode
262
+ }
263
+ };
264
+ }
265
+ var PsmSwapClient = class {
266
+ constructor(context) {
267
+ this.context = context;
268
+ }
269
+ context;
270
+ read(input = {}) {
271
+ return readPsm(this.context, input);
272
+ }
273
+ quote(input) {
274
+ return quotePsmSwap(this.context, input);
275
+ }
276
+ plan(input) {
277
+ return planPsmSwap(this.context, input);
278
+ }
279
+ executeAction(action) {
280
+ return executePlanAction(action, createContextExecutor(this.context));
281
+ }
282
+ submit(plan) {
283
+ assertSwapPlan(plan, ["psm"], "PSM");
284
+ return executePlanTransactions(plan, createContextExecutor(this.context));
285
+ }
286
+ async execute(input) {
287
+ const plan = isTransactionPlan(input) ? input : await planPsmSwap(this.context, input);
288
+ assertSwapPlan(plan, ["psm"], "PSM");
289
+ return executeTransactionPlan(plan, createContextExecutor(this.context));
290
+ }
291
+ };
292
+ function createPsmSwapClient(context) {
293
+ return new PsmSwapClient(context);
294
+ }
295
+ function createPsmClient(options) {
296
+ return createPsmSwapClient(createProtocolsContext(options));
297
+ }
298
+
299
+ // src/swap/router-execute.ts
300
+ import {
301
+ SDKError as SDKError5
302
+ } from "@sun-protocol/sun-sdk-core";
303
+
304
+ // src/internal/router-action.ts
305
+ import {
306
+ createDeferredContractCallAction
307
+ } from "@sun-protocol/sun-sdk-core";
308
+ var UNIVERSAL_ROUTER_MATERIALIZATION_KIND = "universal-router-swap";
309
+ function toJsonValue(value) {
310
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
311
+ return value;
312
+ }
313
+ if (Array.isArray(value)) {
314
+ return value.map(toJsonValue);
315
+ }
316
+ if (typeof value === "object") {
317
+ const objectValue = value;
318
+ const result = {};
319
+ for (const [key, entry] of Object.entries(objectValue)) {
320
+ if (entry !== void 0) {
321
+ result[key] = toJsonValue(entry);
322
+ }
323
+ }
324
+ return result;
325
+ }
326
+ throw new TypeError(`Value is not JSON-serializable: ${typeof value}`);
327
+ }
328
+ function toJsonObject(value) {
329
+ const normalized = toJsonValue(value);
330
+ if (normalized === null || Array.isArray(normalized) || typeof normalized !== "object") {
331
+ throw new TypeError("Value is not a JSON object");
332
+ }
333
+ return normalized;
334
+ }
335
+ function createUniversalRouterDeferredAction(input) {
336
+ return createDeferredContractCallAction({
337
+ id: input.id,
338
+ materialization: {
339
+ kind: UNIVERSAL_ROUTER_MATERIALIZATION_KIND,
340
+ data: toJsonObject(input.data)
341
+ },
342
+ ...input.intent !== void 0 ? { intent: input.intent } : {}
343
+ });
344
+ }
345
+
346
+ // src/internal/universal-router-adapter.ts
347
+ import {
348
+ SDKError as SDKError4,
349
+ createContractCallAction
350
+ } from "@sun-protocol/sun-sdk-core";
351
+ import {
352
+ Address,
353
+ TradePlanner,
354
+ parseRouteAPIResponse,
355
+ PoolType,
356
+ PoolVersion
357
+ } from "@sun-protocol/universal-router-sdk";
358
+ import { computeV3PoolAddress } from "@sun-protocol/sun-sdk-sunswap-v3";
359
+ function requireString(route, key) {
360
+ const value = route[key];
361
+ if (typeof value !== "string") {
362
+ throw new SDKError4("SDK_INVALID_ARGUMENT", `Router route is missing ${String(key)}`, { key, route });
363
+ }
364
+ return value;
365
+ }
366
+ function requireStringArray(route, key) {
367
+ const value = route[key];
368
+ if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) {
369
+ throw new SDKError4("SDK_INVALID_ARGUMENT", `Router route is missing ${String(key)}`, { key, route });
370
+ }
371
+ return value;
372
+ }
373
+ function normalizePoolKey(poolKey) {
374
+ if (poolKey === null || poolKey === void 0) {
375
+ return null;
376
+ }
377
+ if (typeof poolKey !== "object") {
378
+ throw new SDKError4("SDK_INVALID_ARGUMENT", "Router route poolKey must be an object or null", { poolKey });
379
+ }
380
+ const value = poolKey;
381
+ const token0 = value.token0 ?? value.currency0;
382
+ const token1 = value.token1 ?? value.currency1;
383
+ if (typeof token0 !== "string" || typeof token1 !== "string" || typeof value.hooks !== "string" || typeof value.fee !== "number" || typeof value.parameters !== "string") {
384
+ throw new SDKError4("SDK_INVALID_ARGUMENT", "Router route poolKey has invalid fields", { poolKey });
385
+ }
386
+ return {
387
+ token0,
388
+ token1,
389
+ hooks: value.hooks,
390
+ fee: value.fee,
391
+ parameters: value.parameters
392
+ };
393
+ }
394
+ function normalizeRouteData(route) {
395
+ const poolKeysRaw = route.poolKeys;
396
+ if (!Array.isArray(poolKeysRaw)) {
397
+ throw new SDKError4("SDK_INVALID_ARGUMENT", "Router route is missing poolKeys", { route });
398
+ }
399
+ return {
400
+ amountIn: requireString(route, "amountIn"),
401
+ amountInRaw: requireString(route, "amountInRaw"),
402
+ amountOut: requireString(route, "amountOut"),
403
+ amountOutRaw: requireString(route, "amountOutRaw"),
404
+ amountOutMinimum: typeof route.amountOutMinimum === "string" ? route.amountOutMinimum : "0",
405
+ amountOutMinimumRaw: typeof route.amountOutMinimumRaw === "string" ? route.amountOutMinimumRaw : "0",
406
+ inUsd: typeof route.inUsd === "string" ? route.inUsd : "0",
407
+ outUsd: typeof route.outUsd === "string" ? route.outUsd : "0",
408
+ impact: typeof route.impact === "string" ? route.impact : "0",
409
+ fee: typeof route.fee === "string" ? route.fee : "0",
410
+ containsUnverifiedHook: route.containsUnverifiedHook === true,
411
+ tokens: requireStringArray(route, "tokens"),
412
+ symbols: requireStringArray(route, "symbols"),
413
+ poolFees: requireStringArray(route, "poolFees"),
414
+ poolVersions: requireStringArray(route, "poolVersions"),
415
+ poolKeys: poolKeysRaw.map(normalizePoolKey),
416
+ stepAmountsOut: requireStringArray(route, "stepAmountsOut"),
417
+ ...typeof route.amountInReferralBips === "number" ? { amountInReferralBips: route.amountInReferralBips } : {},
418
+ ...typeof route.amountOutReferralBips === "number" ? { amountOutReferralBips: route.amountOutReferralBips } : {}
419
+ };
420
+ }
421
+ function routeStartsWithNativeTrx(chain, route) {
422
+ return route.tokens[0] === chain.contracts.trx;
423
+ }
424
+ function buildUniversalRouterSwapAction(input) {
425
+ const routeData = normalizeRouteData(input.route);
426
+ const hops = routeData.poolVersions.length;
427
+ if (hops === 0 || routeData.tokens.length !== hops + 1 || routeData.poolFees.length < hops || routeData.poolVersions.some((version) => !Object.values(PoolVersion).includes(version))) {
428
+ throw new SDKError4("SDK_INVALID_ARGUMENT", "Router route has inconsistent hop data");
429
+ }
430
+ routeData.tokens = routeData.tokens.map((token) => normalizeTronAddress(token));
431
+ const swapTradeRoute = parseRouteAPIResponse(routeData, input.network === "nile", {
432
+ slippageBips: input.slippageBips
433
+ });
434
+ if (input.recipient) {
435
+ swapTradeRoute.recipient = new Address(input.recipient);
436
+ }
437
+ const tradePlanner = new TradePlanner([swapTradeRoute], false, {
438
+ permitOptions: input.permit ? { permitEnabled: true, permit: input.permit } : { permitEnabled: false }
439
+ });
440
+ tradePlanner.encode();
441
+ const usdtAddress = input.chain.contracts.usdt;
442
+ const sources = /* @__PURE__ */ new Map();
443
+ swapTradeRoute.pools.forEach((pool, index) => {
444
+ if (pool.type !== PoolType.V3 || routeData.tokens[index + 1] !== usdtAddress) return;
445
+ const address = computeV3PoolAddress({
446
+ network: input.network,
447
+ tokenA: routeData.tokens[index],
448
+ tokenB: usdtAddress,
449
+ fee: pool.fee,
450
+ factory: input.chain.contracts.sunswapV3Factory,
451
+ wrappedNative: input.chain.contracts.wtrx
452
+ });
453
+ sources.set(address, { address, role: "v3-pool" });
454
+ });
455
+ const usdtTransfer = {
456
+ network: input.network,
457
+ transfersUsdt: sources.size > 0,
458
+ sources: [...sources.values()]
459
+ };
460
+ return createContractCallAction({
461
+ id: "universal-router-swap",
462
+ target: input.chain.contracts.universalRouter,
463
+ functionSelector: "execute(bytes,bytes[],uint256)",
464
+ parameters: [
465
+ { type: "bytes", value: tradePlanner.commands },
466
+ { type: "bytes[]", value: tradePlanner.inputs },
467
+ { type: "uint256", value: input.deadline }
468
+ ],
469
+ ...routeStartsWithNativeTrx(input.chain, routeData) ? { callValue: BigInt(routeData.amountInRaw) } : {},
470
+ feeLimit: normalizeFeeLimit(input.feeLimit, DEFAULT_UNIVERSAL_ROUTER_SWAP_FEE_LIMIT),
471
+ metadata: { usdtTransfer }
472
+ });
473
+ }
474
+
475
+ // src/swap/router-execute.ts
476
+ function signatureFromResults(actionId, completed) {
477
+ if (!actionId) return void 0;
478
+ const result = completed.find((item) => item.action.id === actionId)?.result;
479
+ return typeof result === "string" ? result.startsWith("0x") ? result : `0x${result}` : void 0;
480
+ }
481
+ async function materializeUniversalRouterActionWithSignature(context, action, permitSignature) {
482
+ if (action.materialization.kind !== UNIVERSAL_ROUTER_MATERIALIZATION_KIND) {
483
+ throw new SDKError5("SDK_RUNTIME_ERROR", `Unsupported deferred action kind: ${action.materialization.kind}`);
484
+ }
485
+ const data = action.materialization.data;
486
+ if (data.network !== context.network) {
487
+ throw new SDKError5("SDK_USDT_TRANSFER_UNKNOWN", "Network changed; rebuild the swap plan", { reason: "CHECK_FAILED" });
488
+ }
489
+ if (data.signatureActionId && !permitSignature) {
490
+ throw new SDKError5("SDK_INVALID_ARGUMENT", "Router swap requires the planned Permit2 signature", {
491
+ actionId: action.id,
492
+ signatureActionId: data.signatureActionId
493
+ });
494
+ }
495
+ if (data.signatureActionId && !data.permitSingle) {
496
+ throw new SDKError5("SDK_RUNTIME_ERROR", "Router swap plan is missing Permit2 permit data", {
497
+ actionId: action.id,
498
+ signatureActionId: data.signatureActionId
499
+ });
500
+ }
501
+ const normalizedSignature = permitSignature ? permitSignature.startsWith("0x") ? permitSignature : `0x${permitSignature}` : void 0;
502
+ const permit = normalizedSignature && data.permitSingle ? { ...data.permitSingle, signature: normalizedSignature } : void 0;
503
+ return buildUniversalRouterSwapAction({
504
+ network: context.network,
505
+ chain: context.chain,
506
+ route: data.route,
507
+ slippageBips: BigInt(data.slippageBips),
508
+ deadline: await resolveTransactionDeadline(context, data.transactionValidForSeconds),
509
+ ...data.recipient ? { recipient: data.recipient } : {},
510
+ ...permit ? { permit } : {},
511
+ ...data.feeLimit !== void 0 ? { feeLimit: data.feeLimit } : {}
512
+ });
513
+ }
514
+ async function materializeUniversalRouterAction(context, action, completedActions) {
515
+ const data = action.materialization.data;
516
+ const signature = signatureFromResults(data.signatureActionId, completedActions);
517
+ return materializeUniversalRouterActionWithSignature(context, action, signature);
518
+ }
519
+ function createRouterSwapExecutor(context, options) {
520
+ const executor = createContextExecutor(context);
521
+ return {
522
+ ...executor,
523
+ sendAction: async (action) => {
524
+ if (action.target === context.chain.contracts.universalRouter || action.intent?.scope.protocol === "universal-router" && action.intent.kind === "swap") {
525
+ await assertUsdtTransfer(context, action);
526
+ }
527
+ if (!executor.sendAction) throw new SDKError5("SDK_RUNTIME_ERROR", "Swap execution requires a runtime");
528
+ return executor.sendAction(action);
529
+ },
530
+ materializeAction: (action, completed) => options ? materializeUniversalRouterActionWithSignature(context, action, options.permitSignature) : materializeUniversalRouterAction(context, action, completed)
531
+ };
532
+ }
533
+
534
+ // src/swap/tokens.ts
535
+ import { SDKError as SDKError6, assertTronAddress as assertTronAddress2 } from "@sun-protocol/sun-sdk-core";
536
+ function isNativeTrxToken(context, token) {
537
+ return token.toUpperCase() === "TRX" || token === context.chain.contracts.trx;
538
+ }
539
+ function normalizeRouterToken(context, token) {
540
+ return isNativeTrxToken(context, token) ? context.chain.contracts.trx : assertTronAddress2(token, "token");
541
+ }
542
+ function normalizeV2TrxPairPath(context, input) {
543
+ const tokenInIsTrx = isNativeTrxToken(context, input.tokenIn);
544
+ const tokenOutIsTrx = isNativeTrxToken(context, input.tokenOut);
545
+ if (tokenInIsTrx === tokenOutIsTrx) {
546
+ throw new SDKError6("SDK_INVALID_ARGUMENT", "V2 swap currently supports exactly one TRX side", {
547
+ tokenIn: input.tokenIn,
548
+ tokenOut: input.tokenOut
549
+ });
550
+ }
551
+ const token = assertTronAddress2(tokenInIsTrx ? input.tokenOut : input.tokenIn, "token");
552
+ return tokenInIsTrx ? { direction: "trx-to-token", token, path: [context.chain.contracts.wtrx, token] } : { direction: "token-to-trx", token, path: [token, context.chain.contracts.wtrx] };
553
+ }
554
+ function normalizeV2PathToken(context, token, label) {
555
+ if (isNativeTrxToken(context, token) || token === context.chain.contracts.wtrx) {
556
+ return context.chain.contracts.wtrx;
557
+ }
558
+ return assertTronAddress2(token, label);
559
+ }
560
+ function isWrappedTrxToken(context, token) {
561
+ return token === context.chain.contracts.wtrx;
562
+ }
563
+ function detectWtrxSwap(context, input) {
564
+ const amountIn = normalizeBaseUnitAmount(input.amountIn, "amountIn");
565
+ const tokenInIsTrx = isNativeTrxToken(context, input.tokenIn);
566
+ const tokenOutIsTrx = isNativeTrxToken(context, input.tokenOut);
567
+ const tokenInIsWtrx = isWrappedTrxToken(context, input.tokenIn);
568
+ const tokenOutIsWtrx = isWrappedTrxToken(context, input.tokenOut);
569
+ let action;
570
+ if (tokenInIsTrx && tokenOutIsWtrx) action = "wrap";
571
+ if (tokenInIsWtrx && tokenOutIsTrx) action = "unwrap";
572
+ if (!action) return void 0;
573
+ return {
574
+ kind: "wtrx",
575
+ source: "wtrx",
576
+ action,
577
+ tokenIn: input.tokenIn,
578
+ tokenOut: input.tokenOut,
579
+ amountIn,
580
+ amountOut: amountIn,
581
+ route: {
582
+ type: action === "wrap" ? "WTRX_WRAP" : "WTRX_UNWRAP",
583
+ amountIn,
584
+ amountOut: amountIn
585
+ }
586
+ };
587
+ }
588
+ function normalizeV2SwapPath(context, input) {
589
+ const tokenInIsTrx = isNativeTrxToken(context, input.tokenIn);
590
+ const tokenOutIsTrx = isNativeTrxToken(context, input.tokenOut);
591
+ const tokenInIsWtrx = isWrappedTrxToken(context, input.tokenIn);
592
+ const tokenOutIsWtrx = isWrappedTrxToken(context, input.tokenOut);
593
+ if (tokenInIsTrx && (tokenOutIsTrx || tokenOutIsWtrx) || tokenInIsWtrx && tokenOutIsTrx) {
594
+ throw new SDKError6("SDK_INVALID_ARGUMENT", "V2 swap does not handle TRX wrap or unwrap", {
595
+ tokenIn: input.tokenIn,
596
+ tokenOut: input.tokenOut
597
+ });
598
+ }
599
+ const inputToken = normalizeV2PathToken(context, input.tokenIn, "tokenIn");
600
+ const outputToken = normalizeV2PathToken(context, input.tokenOut, "tokenOut");
601
+ const path = (input.path ?? [input.tokenIn, input.tokenOut]).map(
602
+ (token, index) => normalizeV2PathToken(context, token, `path[${index}]`)
603
+ );
604
+ const originalPath = input.path ?? [input.tokenIn, input.tokenOut];
605
+ if (inputToken === outputToken) {
606
+ throw new SDKError6("SDK_INVALID_ARGUMENT", "V2 swap tokenIn and tokenOut must differ", {
607
+ tokenIn: input.tokenIn,
608
+ tokenOut: input.tokenOut
609
+ });
610
+ }
611
+ if (path.length < 2 || path[0] !== inputToken || path[path.length - 1] !== outputToken) {
612
+ throw new SDKError6("SDK_INVALID_ARGUMENT", "V2 swap path must start with tokenIn and end with tokenOut", {
613
+ tokenIn: input.tokenIn,
614
+ tokenOut: input.tokenOut,
615
+ path: originalPath
616
+ });
617
+ }
618
+ if (path.some((token, index) => index > 0 && token === path[index - 1])) {
619
+ throw new SDKError6("SDK_INVALID_ARGUMENT", "V2 swap path must not contain duplicate adjacent tokens", {
620
+ tokenIn: input.tokenIn,
621
+ tokenOut: input.tokenOut,
622
+ path: originalPath
623
+ });
624
+ }
625
+ const direction = tokenInIsTrx ? "trx-to-token" : tokenOutIsTrx ? "token-to-trx" : "token-to-token";
626
+ return { direction, token: tokenInIsTrx ? outputToken : inputToken, path };
627
+ }
628
+
629
+ // src/swap/plan.ts
630
+ import {
631
+ SDKError as SDKError7,
632
+ assertTronAddress as assertTronAddress3,
633
+ createContractCallAction as createContractCallAction2,
634
+ formatUnits as formatUnits2
635
+ } from "@sun-protocol/sun-sdk-core";
636
+ function stringifyRoute(route) {
637
+ try {
638
+ return JSON.stringify(route);
639
+ } catch {
640
+ return void 0;
641
+ }
642
+ }
643
+ function findSelectedRouteIndex(quote, route) {
644
+ const serializedRoute = stringifyRoute(route);
645
+ return quote.routes.findIndex((candidate) => {
646
+ if (candidate === route) return true;
647
+ if (serializedRoute === void 0) return false;
648
+ return stringifyRoute(candidate) === serializedRoute;
649
+ });
650
+ }
651
+ function isWtrxRoute(route) {
652
+ return typeof route === "object" && route !== null && (route.type === "WTRX_WRAP" || route.type === "WTRX_UNWRAP");
653
+ }
654
+ function buildWtrxSwapPlan(context, quote, route, owner, feeLimit) {
655
+ if (stringifyRoute(route) !== stringifyRoute(quote.route)) {
656
+ throw new SDKError7("SDK_INVALID_ARGUMENT", "Selected WTRX route must come from the quote", {
657
+ quoteRoute: quote.route,
658
+ route
659
+ });
660
+ }
661
+ const resolvedFeeLimit = normalizeFeeLimit(feeLimit, DEFAULT_WTRX_SWAP_FEE_LIMIT);
662
+ const intent = {
663
+ kind: quote.action,
664
+ scope: swapIntentScope(WTRX_PROTOCOL_ID, quote.action),
665
+ amount: formatUnits2(BigInt(quote.amountIn), context.chain.nativeCurrency.decimals)
666
+ };
667
+ const transaction = quote.action === "wrap" ? createContractCallAction2({
668
+ id: `${nextPlanId(context, "swap-wtrx")}-wrap`,
669
+ target: context.chain.contracts.wtrx,
670
+ functionSelector: "deposit()",
671
+ parameters: [],
672
+ callValue: BigInt(quote.amountIn),
673
+ feeLimit: resolvedFeeLimit,
674
+ intent
675
+ }) : createContractCallAction2({
676
+ id: `${nextPlanId(context, "swap-wtrx")}-unwrap`,
677
+ target: context.chain.contracts.wtrx,
678
+ functionSelector: "withdraw(uint256)",
679
+ parameters: [{ type: "uint256", value: quote.amountIn }],
680
+ feeLimit: resolvedFeeLimit,
681
+ intent
682
+ });
683
+ return {
684
+ id: nextPlanId(context, "swap-wtrx"),
685
+ kind: "swap",
686
+ network: context.network,
687
+ owner,
688
+ summary: {
689
+ title: quote.action === "wrap" ? "Wrap TRX" : "Unwrap WTRX",
690
+ lines: [`Amount ${quote.amountIn}`]
691
+ },
692
+ approvals: [],
693
+ transactions: [transaction],
694
+ metadata: {
695
+ protocol: "wtrx",
696
+ action: quote.action,
697
+ amountIn: quote.amountIn,
698
+ amountOut: quote.amountOut,
699
+ tokenIn: quote.tokenIn,
700
+ tokenOut: quote.tokenOut,
701
+ feeLimit: resolvedFeeLimit
702
+ }
703
+ };
704
+ }
705
+ async function planRouterSwap(context, input) {
706
+ const { wallet, address: owner } = await requireWalletSnapshot(context, "Router swap plan");
707
+ if (input.quote.kind === "wtrx") {
708
+ if (!isWtrxRoute(input.route)) {
709
+ throw new SDKError7("SDK_INVALID_ARGUMENT", "WTRX swap planning requires a WTRX route");
710
+ }
711
+ return buildWtrxSwapPlan(context, input.quote, input.route, owner, input.feeLimit);
712
+ }
713
+ const selectedRouteIndex = findSelectedRouteIndex(input.quote, input.route);
714
+ if (selectedRouteIndex < 0) {
715
+ throw new SDKError7("SDK_INVALID_ARGUMENT", "Selected route must come from quote.routes", {
716
+ routeCount: input.quote.routes.length,
717
+ tokenIn: input.quote.tokenIn,
718
+ tokenOut: input.quote.tokenOut,
719
+ amountIn: input.quote.amountIn
720
+ });
721
+ }
722
+ const selectedRoute = input.quote.routes[selectedRouteIndex];
723
+ if (!selectedRoute) {
724
+ throw new SDKError7("SDK_RUNTIME_ERROR", "Selected router route could not be resolved after validation", {
725
+ selectedRouteIndex,
726
+ routeCount: input.quote.routes.length
727
+ });
728
+ }
729
+ const symbols = routerRouteSymbols(selectedRoute);
730
+ const intentScope = swapIntentScope(UNIVERSAL_ROUTER_PROTOCOL_ID);
731
+ const recipient = await normalizeRecipient(context, input.recipient, "Router swap recipient", owner);
732
+ const transactionValidForSeconds = normalizeTransactionValidForSeconds(input.transactionValidForSeconds);
733
+ const slippageBips = slippageToBips(input.slippage ?? "0.5%");
734
+ const feeLimit = normalizeFeeLimit(input.feeLimit, DEFAULT_UNIVERSAL_ROUTER_SWAP_FEE_LIMIT);
735
+ const tokenIn = input.quote.tokenIn;
736
+ const approvals = [];
737
+ let signatureActionId;
738
+ let permitSingle;
739
+ if (!isNativeTrxToken(context, tokenIn)) {
740
+ const authorization = await planRouterPermit2Authorization(context, {
741
+ owner,
742
+ token: assertTronAddress3(tokenIn, "tokenIn"),
743
+ amount: input.quote.amountIn,
744
+ approval: input.approval ?? "auto",
745
+ approveAmountType: "max",
746
+ walletCapabilities: wallet.capabilities
747
+ });
748
+ approvals.push(...attachApprovalIntent(authorization.actions, intentScope, symbols.from));
749
+ signatureActionId = authorization.signatureActionId;
750
+ permitSingle = authorization.permitSingle;
751
+ }
752
+ const transaction = createUniversalRouterDeferredAction({
753
+ id: `${nextPlanId(context, "swap-router")}-execute`,
754
+ data: {
755
+ network: context.network,
756
+ route: selectedRoute,
757
+ slippageBips: slippageBips.toString(),
758
+ transactionValidForSeconds,
759
+ recipient,
760
+ feeLimit,
761
+ ...signatureActionId ? { signatureActionId } : {},
762
+ ...permitSingle ? { permitSingle } : {}
763
+ },
764
+ intent: {
765
+ kind: "swap",
766
+ scope: intentScope,
767
+ from: { value: selectedRoute.amountIn, symbol: symbols.from },
768
+ to: { value: selectedRoute.amountOut, symbol: symbols.to },
769
+ ...input.recipient === void 0 ? {} : { recipient }
770
+ }
771
+ });
772
+ return {
773
+ id: nextPlanId(context, "swap-router"),
774
+ kind: "swap",
775
+ network: context.network,
776
+ owner,
777
+ summary: {
778
+ title: "Swap with Universal Router",
779
+ lines: [`Spend ${selectedRoute.amountIn}`, `Expected output ${selectedRoute.amountOut}`]
780
+ },
781
+ approvals,
782
+ transactions: [transaction],
783
+ metadata: {
784
+ protocol: "universal-router",
785
+ selectedRouteIndex,
786
+ tokenIn: input.quote.tokenIn,
787
+ tokenOut: input.quote.tokenOut,
788
+ amountIn: selectedRoute.amountInRaw,
789
+ amountOut: selectedRoute.amountOutRaw,
790
+ amountInFormatted: selectedRoute.amountIn,
791
+ amountOutFormatted: selectedRoute.amountOut,
792
+ slippageBips,
793
+ recipient,
794
+ transactionValidForSeconds,
795
+ feeLimit
796
+ }
797
+ };
798
+ }
799
+
800
+ // src/swap/quote.ts
801
+ import { SDKError as SDKError8 } from "@sun-protocol/sun-sdk-core";
802
+ function requireRoutes(response) {
803
+ if (response.code !== 0) {
804
+ throw new SDKError8("SDK_NO_ROUTE", response.message ?? "Router API returned no route", {
805
+ code: response.code,
806
+ message: response.message
807
+ });
808
+ }
809
+ if (!Array.isArray(response.data) || response.data.length === 0) {
810
+ throw new SDKError8("SDK_NO_ROUTE", "Router API returned no route", {
811
+ code: response.code
812
+ });
813
+ }
814
+ return response.data;
815
+ }
816
+ function normalizeRouteTypeList(input) {
817
+ if (input.routerRouteTypes !== void 0 && input.typeList !== void 0) {
818
+ throw new SDKError8("SDK_INVALID_ARGUMENT", "Use routerRouteTypes or typeList, not both", {
819
+ routerRouteTypes: input.routerRouteTypes,
820
+ typeList: input.typeList
821
+ });
822
+ }
823
+ return input.routerRouteTypes !== void 0 ? input.routerRouteTypes.join(",") : input.typeList;
824
+ }
825
+ function requireRouterAmountString(route, field, routeIndex) {
826
+ const value = route[field];
827
+ if (typeof value !== "string" || value.length === 0) {
828
+ throw new SDKError8("SDK_API_ERROR", `Router route has invalid ${field}`, {
829
+ routeIndex,
830
+ field,
831
+ value
832
+ });
833
+ }
834
+ return value;
835
+ }
836
+ function normalizeRouterRawAmount(value, field, routeIndex) {
837
+ try {
838
+ return normalizeBaseUnitAmount(value, `Router route ${field}`);
839
+ } catch {
840
+ throw new SDKError8("SDK_API_ERROR", `Router route has invalid ${field}`, {
841
+ routeIndex,
842
+ field,
843
+ value
844
+ });
845
+ }
846
+ }
847
+ function validateRouterRoute(route, routeIndex, requestedAmountIn) {
848
+ if (typeof route !== "object" || route === null || Array.isArray(route)) {
849
+ throw new SDKError8("SDK_API_ERROR", "Router route must be an object", {
850
+ routeIndex,
851
+ route
852
+ });
853
+ }
854
+ const routeRecord = route;
855
+ const amountIn = requireRouterAmountString(routeRecord, "amountIn", routeIndex);
856
+ const amountOut = requireRouterAmountString(routeRecord, "amountOut", routeIndex);
857
+ const amountInRaw = normalizeRouterRawAmount(
858
+ requireRouterAmountString(routeRecord, "amountInRaw", routeIndex),
859
+ "amountInRaw",
860
+ routeIndex
861
+ );
862
+ const amountOutRaw = normalizeRouterRawAmount(
863
+ requireRouterAmountString(routeRecord, "amountOutRaw", routeIndex),
864
+ "amountOutRaw",
865
+ routeIndex
866
+ );
867
+ if (amountInRaw !== requestedAmountIn) {
868
+ throw new SDKError8("SDK_API_ERROR", "Router route input amount does not match the request", {
869
+ routeIndex,
870
+ requestedAmountIn,
871
+ routeAmountInRaw: amountInRaw
872
+ });
873
+ }
874
+ return {
875
+ ...routeRecord,
876
+ amountIn,
877
+ amountInRaw,
878
+ amountOut,
879
+ amountOutRaw
880
+ };
881
+ }
882
+ async function quoteRouterSwap(context, input) {
883
+ const wtrxQuote = detectWtrxSwap(context, input);
884
+ if (wtrxQuote) return wtrxQuote;
885
+ const amountIn = normalizeBaseUnitAmount(input.amountIn, "amountIn");
886
+ const typeList = normalizeRouteTypeList(input);
887
+ const routerEndpoint = context.endpoints.router;
888
+ if (!routerEndpoint) {
889
+ throw new SDKError8("SDK_UNSUPPORTED_NETWORK", "Router API endpoint is not configured for this network", {
890
+ network: context.network
891
+ });
892
+ }
893
+ const routesResponse = await context.routerApi.getRoutes(routerEndpoint, {
894
+ fromToken: normalizeRouterToken(context, input.tokenIn),
895
+ toToken: normalizeRouterToken(context, input.tokenOut),
896
+ amountIn,
897
+ ...typeList !== void 0 ? { typeList } : {},
898
+ ...input.includeUnverifiedV4Hook !== void 0 ? { includeUnverifiedV4Hook: input.includeUnverifiedV4Hook } : {}
899
+ });
900
+ const routes = requireRoutes(routesResponse).map((route, routeIndex) => validateRouterRoute(route, routeIndex, amountIn));
901
+ const bestRoute = routes[0];
902
+ if (!bestRoute) {
903
+ throw new SDKError8("SDK_NO_ROUTE", "Router API returned no route");
904
+ }
905
+ return {
906
+ kind: "router",
907
+ source: "router",
908
+ tokenIn: input.tokenIn,
909
+ tokenOut: input.tokenOut,
910
+ amountIn: bestRoute.amountInRaw,
911
+ amountOut: bestRoute.amountOutRaw,
912
+ amountInFormatted: bestRoute.amountIn,
913
+ amountOutFormatted: bestRoute.amountOut,
914
+ routes,
915
+ bestRoute
916
+ };
917
+ }
918
+ var quoteSwap = quoteRouterSwap;
919
+
920
+ // src/swap/router.ts
921
+ var SwapRouterClient = class {
922
+ constructor(context) {
923
+ this.context = context;
924
+ }
925
+ context;
926
+ quote(input) {
927
+ return quoteRouterSwap(this.context, input);
928
+ }
929
+ plan(input) {
930
+ return planRouterSwap(this.context, input);
931
+ }
932
+ executeAction(action) {
933
+ return executePlanAction(action, createRouterSwapExecutor(this.context));
934
+ }
935
+ async submit(plan, options = {}) {
936
+ assertSwapPlan(plan, ["universal-router", "wtrx"], "Router");
937
+ return executePlanTransactions(
938
+ plan,
939
+ createRouterSwapExecutor(this.context, options)
940
+ );
941
+ }
942
+ async execute(input) {
943
+ const plan = isTransactionPlan(input) ? input : await planRouterSwap(this.context, input);
944
+ assertSwapPlan(plan, ["universal-router", "wtrx"], "Router");
945
+ return executeTransactionPlan(plan, createRouterSwapExecutor(this.context));
946
+ }
947
+ };
948
+ function createSwapRouterClient(options) {
949
+ return new SwapRouterClient(createProtocolsContext(options));
950
+ }
951
+
952
+ // src/swap/v2.ts
953
+ import {
954
+ buildV2RouterAllowedTradingPairRead,
955
+ buildV2RouterAmountsInRead,
956
+ buildV2RouterAmountsOutRead,
957
+ normalizeV2AmountsResult
958
+ } from "@sun-protocol/sun-sdk-sunswap-v2";
959
+ import {
960
+ SDKError as SDKError9,
961
+ assertTronAddress as assertTronAddress4
962
+ } from "@sun-protocol/sun-sdk-core";
963
+ function normalizeV2Mode(input) {
964
+ const mode = input.mode ?? "exact-in";
965
+ if (mode === "exact-in") {
966
+ normalizeBaseUnitAmount(input.amountIn ?? "", "amountIn");
967
+ if (input.amountOut !== void 0) {
968
+ throw new SDKError9("SDK_INVALID_ARGUMENT", "exact-in V2 swap does not accept amountOut");
969
+ }
970
+ return mode;
971
+ }
972
+ normalizeBaseUnitAmount(input.amountOut ?? "", "amountOut");
973
+ if (input.amountIn !== void 0) {
974
+ throw new SDKError9("SDK_INVALID_ARGUMENT", "exact-out V2 swap does not accept amountIn");
975
+ }
976
+ return mode;
977
+ }
978
+ function requireAmount(amounts, index, operation) {
979
+ const amount = amounts[index];
980
+ if (amount === void 0) {
981
+ throw new SDKError9("SDK_CONTRACT_ERROR", "V2 router returned an incomplete amount path", {
982
+ operation,
983
+ amounts
984
+ });
985
+ }
986
+ return amount;
987
+ }
988
+ function requireAmountPath(amounts, path, operation) {
989
+ if (amounts.length !== path.length) {
990
+ throw new SDKError9("SDK_CONTRACT_ERROR", "V2 router returned an incomplete amount path", {
991
+ operation,
992
+ amounts,
993
+ path
994
+ });
995
+ }
996
+ }
997
+ function normalizeAllowedTradingPairResult(result) {
998
+ if (typeof result === "boolean") return result;
999
+ if (Array.isArray(result) && typeof result[0] === "boolean") return result[0];
1000
+ return Boolean(result);
1001
+ }
1002
+ async function assertV2PathAllowed(context, path) {
1003
+ const runtime = requireRuntime(context, "V2 allowed trading pair check");
1004
+ for (let index = 0; index < path.length - 1; index += 1) {
1005
+ const tokenA = assertTronAddress4(path[index] ?? "", `path[${index}]`);
1006
+ const tokenB = assertTronAddress4(path[index + 1] ?? "", `path[${index + 1}]`);
1007
+ const allowed = normalizeAllowedTradingPairResult(await runtime.readContract(buildV2RouterAllowedTradingPairRead({
1008
+ network: context.network,
1009
+ tokenA,
1010
+ tokenB
1011
+ })));
1012
+ if (!allowed) {
1013
+ throw new SDKError9("SDK_NO_ROUTE", "SunSwap V2 router does not allow this trading pair", {
1014
+ operation: "v2-allowed-trading-pair-check",
1015
+ blockedPair: [tokenA, tokenB],
1016
+ path
1017
+ });
1018
+ }
1019
+ }
1020
+ }
1021
+ async function quoteV2Swap(context, input) {
1022
+ const runtime = requireRuntime(context, "V2 swap quote");
1023
+ const mode = normalizeV2Mode(input);
1024
+ const pathInfo = normalizeV2SwapPath(context, input);
1025
+ await assertV2PathAllowed(context, pathInfo.path);
1026
+ if (mode === "exact-in") {
1027
+ const amountIn = normalizeBaseUnitAmount(input.amountIn ?? "", "amountIn");
1028
+ const amounts2 = normalizeV2AmountsResult(await runtime.readContract(buildV2RouterAmountsOutRead({
1029
+ network: context.network,
1030
+ amountIn,
1031
+ path: pathInfo.path
1032
+ })));
1033
+ requireAmountPath(amounts2, pathInfo.path, "v2-exact-in-quote");
1034
+ return {
1035
+ kind: "v2",
1036
+ source: "v2",
1037
+ mode,
1038
+ direction: pathInfo.direction,
1039
+ tokenIn: input.tokenIn,
1040
+ tokenOut: input.tokenOut,
1041
+ amountIn,
1042
+ amountOut: requireAmount(amounts2, amounts2.length - 1, "v2-exact-in-quote"),
1043
+ path: pathInfo.path
1044
+ };
1045
+ }
1046
+ const amountOut = normalizeBaseUnitAmount(input.amountOut ?? "", "amountOut");
1047
+ const amounts = normalizeV2AmountsResult(await runtime.readContract(buildV2RouterAmountsInRead({
1048
+ network: context.network,
1049
+ amountOut,
1050
+ path: pathInfo.path
1051
+ })));
1052
+ requireAmountPath(amounts, pathInfo.path, "v2-exact-out-quote");
1053
+ return {
1054
+ kind: "v2",
1055
+ source: "v2",
1056
+ mode,
1057
+ direction: pathInfo.direction,
1058
+ tokenIn: input.tokenIn,
1059
+ tokenOut: input.tokenOut,
1060
+ amountIn: requireAmount(amounts, 0, "v2-exact-out-quote"),
1061
+ amountOut,
1062
+ path: pathInfo.path
1063
+ };
1064
+ }
1065
+ async function planV2Swap(context, input) {
1066
+ const { address: owner } = await requireWalletSnapshot(context, "V2 swap plan");
1067
+ const quote = await quoteV2Swap(context, input);
1068
+ const recipient = await normalizeRecipient(context, input.recipient, "V2 swap recipient", owner);
1069
+ const transactionValidForSeconds = normalizeTransactionValidForSeconds(input.transactionValidForSeconds);
1070
+ const slippageBips = slippageToBips(input.slippage ?? "0.5%");
1071
+ const approvals = [];
1072
+ if (quote.mode === "exact-in" && quote.direction === "trx-to-token") {
1073
+ const minAmountOut = applySlippageMin(BigInt(quote.amountOut), slippageBips).toString();
1074
+ const transaction2 = createDeferredV2SwapAction({
1075
+ data: {
1076
+ operation: "swap-exact-eth-for-tokens",
1077
+ amountIn: quote.amountIn,
1078
+ amountOutMin: minAmountOut,
1079
+ path: quote.path,
1080
+ recipient,
1081
+ transactionValidForSeconds
1082
+ }
1083
+ });
1084
+ return {
1085
+ id: nextPlanId(context, "swap-v2"),
1086
+ kind: "swap",
1087
+ network: context.network,
1088
+ owner,
1089
+ summary: {
1090
+ title: "Swap with SunSwap V2",
1091
+ lines: [`Spend ${quote.amountIn} sun`, `Receive at least ${minAmountOut} token units`]
1092
+ },
1093
+ approvals,
1094
+ transactions: [transaction2],
1095
+ metadata: {
1096
+ protocol: "sunswap-v2",
1097
+ mode: quote.mode,
1098
+ direction: quote.direction,
1099
+ amountIn: quote.amountIn,
1100
+ quotedAmountOut: quote.amountOut,
1101
+ minAmountOut,
1102
+ slippageBips,
1103
+ path: quote.path,
1104
+ recipient,
1105
+ transactionValidForSeconds
1106
+ }
1107
+ };
1108
+ }
1109
+ if (quote.mode === "exact-out" && quote.direction === "trx-to-token") {
1110
+ const maxAmountIn2 = applySlippageMax(BigInt(quote.amountIn), slippageBips).toString();
1111
+ const transaction2 = createDeferredV2SwapAction({
1112
+ data: {
1113
+ operation: "swap-eth-for-exact-tokens",
1114
+ amountOut: quote.amountOut,
1115
+ amountInMax: maxAmountIn2,
1116
+ path: quote.path,
1117
+ recipient,
1118
+ transactionValidForSeconds
1119
+ }
1120
+ });
1121
+ return {
1122
+ id: nextPlanId(context, "swap-v2"),
1123
+ kind: "swap",
1124
+ network: context.network,
1125
+ owner,
1126
+ summary: {
1127
+ title: "Swap with SunSwap V2",
1128
+ lines: [`Spend at most ${maxAmountIn2} sun`, `Receive ${quote.amountOut} token units`]
1129
+ },
1130
+ approvals,
1131
+ transactions: [transaction2],
1132
+ metadata: {
1133
+ protocol: "sunswap-v2",
1134
+ mode: quote.mode,
1135
+ direction: quote.direction,
1136
+ amountOut: quote.amountOut,
1137
+ quotedAmountIn: quote.amountIn,
1138
+ maxAmountIn: maxAmountIn2,
1139
+ slippageBips,
1140
+ path: quote.path,
1141
+ recipient,
1142
+ transactionValidForSeconds
1143
+ }
1144
+ };
1145
+ }
1146
+ const tokenInputAmount = quote.mode === "exact-in" ? quote.amountIn : applySlippageMax(BigInt(quote.amountIn), slippageBips).toString();
1147
+ const approval = await planApproval(context, {
1148
+ asset: { type: "token", token: assertTronAddress4(input.tokenIn, "tokenIn") },
1149
+ owner,
1150
+ spender: context.chain.contracts.sunswapV2Router,
1151
+ amount: tokenInputAmount
1152
+ }, {
1153
+ mode: input.approval ?? "auto",
1154
+ amount: "exact"
1155
+ });
1156
+ approvals.push(...approval.actions);
1157
+ if (quote.mode === "exact-in") {
1158
+ const minAmountOut = applySlippageMin(BigInt(quote.amountOut), slippageBips).toString();
1159
+ const transaction2 = quote.direction === "token-to-trx" ? createDeferredV2SwapAction({
1160
+ data: {
1161
+ operation: "swap-exact-tokens-for-eth",
1162
+ amountIn: quote.amountIn,
1163
+ amountOutMin: minAmountOut,
1164
+ path: quote.path,
1165
+ recipient,
1166
+ transactionValidForSeconds
1167
+ }
1168
+ }) : createDeferredV2SwapAction({
1169
+ data: {
1170
+ operation: "swap-exact-tokens-for-tokens",
1171
+ amountIn: quote.amountIn,
1172
+ amountOutMin: minAmountOut,
1173
+ path: quote.path,
1174
+ recipient,
1175
+ transactionValidForSeconds
1176
+ }
1177
+ });
1178
+ return {
1179
+ id: nextPlanId(context, "swap-v2"),
1180
+ kind: "swap",
1181
+ network: context.network,
1182
+ owner,
1183
+ summary: {
1184
+ title: "Swap with SunSwap V2",
1185
+ lines: [
1186
+ `Sell ${quote.amountIn} token units`,
1187
+ `Receive at least ${minAmountOut} ${quote.direction === "token-to-trx" ? "sun" : "token units"}`
1188
+ ]
1189
+ },
1190
+ approvals,
1191
+ transactions: [transaction2],
1192
+ metadata: {
1193
+ protocol: "sunswap-v2",
1194
+ mode: quote.mode,
1195
+ direction: quote.direction,
1196
+ amountIn: quote.amountIn,
1197
+ quotedAmountOut: quote.amountOut,
1198
+ minAmountOut,
1199
+ slippageBips,
1200
+ approvalMode: input.approval ?? "auto",
1201
+ path: quote.path,
1202
+ recipient,
1203
+ transactionValidForSeconds,
1204
+ owner,
1205
+ spender: context.chain.contracts.sunswapV2Router
1206
+ }
1207
+ };
1208
+ }
1209
+ const maxAmountIn = tokenInputAmount;
1210
+ const transaction = quote.direction === "token-to-trx" ? createDeferredV2SwapAction({
1211
+ data: {
1212
+ operation: "swap-tokens-for-exact-eth",
1213
+ amountOut: quote.amountOut,
1214
+ amountInMax: maxAmountIn,
1215
+ path: quote.path,
1216
+ recipient,
1217
+ transactionValidForSeconds
1218
+ }
1219
+ }) : createDeferredV2SwapAction({
1220
+ data: {
1221
+ operation: "swap-tokens-for-exact-tokens",
1222
+ amountOut: quote.amountOut,
1223
+ amountInMax: maxAmountIn,
1224
+ path: quote.path,
1225
+ recipient,
1226
+ transactionValidForSeconds
1227
+ }
1228
+ });
1229
+ return {
1230
+ id: nextPlanId(context, "swap-v2"),
1231
+ kind: "swap",
1232
+ network: context.network,
1233
+ owner,
1234
+ summary: {
1235
+ title: "Swap with SunSwap V2",
1236
+ lines: [
1237
+ `Spend at most ${maxAmountIn} token units`,
1238
+ `Receive ${quote.amountOut} ${quote.direction === "token-to-trx" ? "sun" : "token units"}`
1239
+ ]
1240
+ },
1241
+ approvals,
1242
+ transactions: [transaction],
1243
+ metadata: {
1244
+ protocol: "sunswap-v2",
1245
+ mode: quote.mode,
1246
+ direction: quote.direction,
1247
+ amountOut: quote.amountOut,
1248
+ quotedAmountIn: quote.amountIn,
1249
+ maxAmountIn,
1250
+ slippageBips,
1251
+ approvalMode: input.approval ?? "auto",
1252
+ path: quote.path,
1253
+ recipient,
1254
+ transactionValidForSeconds,
1255
+ owner,
1256
+ spender: context.chain.contracts.sunswapV2Router
1257
+ }
1258
+ };
1259
+ }
1260
+ var V2SwapClient = class {
1261
+ constructor(context) {
1262
+ this.context = context;
1263
+ }
1264
+ context;
1265
+ quote(_input) {
1266
+ return quoteV2Swap(this.context, _input);
1267
+ }
1268
+ plan(input) {
1269
+ return planV2Swap(this.context, input);
1270
+ }
1271
+ executeAction(action) {
1272
+ return executePlanAction(action, createV2SwapExecutor(this.context));
1273
+ }
1274
+ submit(plan) {
1275
+ assertSwapPlan(plan, ["sunswap-v2"], "SunSwap V2");
1276
+ return executePlanTransactions(plan, createV2SwapExecutor(this.context));
1277
+ }
1278
+ async execute(input) {
1279
+ const plan = isTransactionPlan(input) ? input : await planV2Swap(this.context, input);
1280
+ assertSwapPlan(plan, ["sunswap-v2"], "SunSwap V2");
1281
+ return executeTransactionPlan(plan, createV2SwapExecutor(this.context));
1282
+ }
1283
+ };
1284
+ function createV2SwapClient(context) {
1285
+ return new V2SwapClient(context);
1286
+ }
1287
+ function createV2Client(options) {
1288
+ return createV2SwapClient(createProtocolsContext(options));
1289
+ }
1290
+
1291
+ export {
1292
+ UNIVERSAL_ROUTER_PROTOCOL_ID,
1293
+ PSM_PROTOCOL_ID,
1294
+ WTRX_PROTOCOL_ID,
1295
+ readPsm,
1296
+ quotePsmSwap,
1297
+ planPsmSwap,
1298
+ PsmSwapClient,
1299
+ createPsmSwapClient,
1300
+ createPsmClient,
1301
+ materializeUniversalRouterAction,
1302
+ createRouterSwapExecutor,
1303
+ isNativeTrxToken,
1304
+ normalizeRouterToken,
1305
+ normalizeV2TrxPairPath,
1306
+ isWrappedTrxToken,
1307
+ detectWtrxSwap,
1308
+ planRouterSwap,
1309
+ quoteRouterSwap,
1310
+ quoteSwap,
1311
+ SwapRouterClient,
1312
+ createSwapRouterClient,
1313
+ V2SwapClient,
1314
+ createV2SwapClient,
1315
+ createV2Client
1316
+ };
1317
+ //# sourceMappingURL=chunk-ECCGOXNA.js.map