@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.
- package/README.md +30 -0
- package/dist/chunk-5XDX5563.js +99 -0
- package/dist/chunk-5XDX5563.js.map +1 -0
- package/dist/chunk-C2YCVBPI.js +212 -0
- package/dist/chunk-C2YCVBPI.js.map +1 -0
- package/dist/chunk-ECCGOXNA.js +1317 -0
- package/dist/chunk-ECCGOXNA.js.map +1 -0
- package/dist/chunk-HD2S3Q7L.js +746 -0
- package/dist/chunk-HD2S3Q7L.js.map +1 -0
- package/dist/chunk-L5TZCDPH.js +561 -0
- package/dist/chunk-L5TZCDPH.js.map +1 -0
- package/dist/chunk-OM4OW4KB.js +224 -0
- package/dist/chunk-OM4OW4KB.js.map +1 -0
- package/dist/chunk-PEGREDLT.js +935 -0
- package/dist/chunk-PEGREDLT.js.map +1 -0
- package/dist/chunk-TSHXXRH2.js +103 -0
- package/dist/chunk-TSHXXRH2.js.map +1 -0
- package/dist/chunk-VFVKMC4H.js +521 -0
- package/dist/chunk-VFVKMC4H.js.map +1 -0
- package/dist/chunk-YMGZDXI4.js +1209 -0
- package/dist/chunk-YMGZDXI4.js.map +1 -0
- package/dist/common-8VQQ0FSU.d.cts +146 -0
- package/dist/common-8VQQ0FSU.d.ts +146 -0
- package/dist/execute-BNKh6dT6.d.cts +187 -0
- package/dist/execute-CVhcKZxF.d.ts +187 -0
- package/dist/index.cjs +5619 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +139 -0
- package/dist/index.d.ts +139 -0
- package/dist/index.js +200 -0
- package/dist/index.js.map +1 -0
- package/dist/intents-BSXq1HvW.d.cts +200 -0
- package/dist/intents-CwOttD75.d.ts +200 -0
- package/dist/liquidity/index.cjs +2441 -0
- package/dist/liquidity/index.cjs.map +1 -0
- package/dist/liquidity/index.d.cts +83 -0
- package/dist/liquidity/index.d.ts +83 -0
- package/dist/liquidity/index.js +32 -0
- package/dist/liquidity/index.js.map +1 -0
- package/dist/positions/index.cjs +1544 -0
- package/dist/positions/index.cjs.map +1 -0
- package/dist/positions/index.d.cts +159 -0
- package/dist/positions/index.d.ts +159 -0
- package/dist/positions/index.js +25 -0
- package/dist/positions/index.js.map +1 -0
- package/dist/pump/index.cjs +1178 -0
- package/dist/pump/index.cjs.map +1 -0
- package/dist/pump/index.d.cts +16 -0
- package/dist/pump/index.d.ts +16 -0
- package/dist/pump/index.js +29 -0
- package/dist/pump/index.js.map +1 -0
- package/dist/swap/index.cjs +2259 -0
- package/dist/swap/index.cjs.map +1 -0
- package/dist/swap/index.d.cts +33 -0
- package/dist/swap/index.d.ts +33 -0
- package/dist/swap/index.js +61 -0
- package/dist/swap/index.js.map +1 -0
- package/dist/types-BTPdCC7k.d.cts +160 -0
- package/dist/types-BfrouFmO.d.ts +160 -0
- package/package.json +94 -0
|
@@ -0,0 +1,2441 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/liquidity/index.ts
|
|
21
|
+
var liquidity_exports = {};
|
|
22
|
+
__export(liquidity_exports, {
|
|
23
|
+
LiquidityClient: () => LiquidityClient,
|
|
24
|
+
SUNSWAP_V2_LIQUIDITY_PROTOCOL_ID: () => SUNSWAP_V2_LIQUIDITY_PROTOCOL_ID,
|
|
25
|
+
SUNSWAP_V3_LIQUIDITY_PROTOCOL_ID: () => SUNSWAP_V3_LIQUIDITY_PROTOCOL_ID,
|
|
26
|
+
SUNSWAP_V4_LIQUIDITY_PROTOCOL_ID: () => SUNSWAP_V4_LIQUIDITY_PROTOCOL_ID,
|
|
27
|
+
V2LiquidityClient: () => V2LiquidityClient,
|
|
28
|
+
V3LiquidityClient: () => V3LiquidityClient,
|
|
29
|
+
V4LiquidityClient: () => V4LiquidityClient,
|
|
30
|
+
createLiquidityClient: () => createLiquidityClient,
|
|
31
|
+
createV2LiquidityClient: () => createV2LiquidityClient,
|
|
32
|
+
planV2AddLiquidity: () => planV2AddLiquidity,
|
|
33
|
+
planV2RemoveLiquidity: () => planV2RemoveLiquidity
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(liquidity_exports);
|
|
36
|
+
|
|
37
|
+
// src/liquidity/v2.ts
|
|
38
|
+
var import_sun_sdk_core10 = require("@sun-protocol/sun-sdk-core");
|
|
39
|
+
var import_sun_sdk_sunswap_v22 = require("@sun-protocol/sun-sdk-sunswap-v2");
|
|
40
|
+
|
|
41
|
+
// src/approval/plan.ts
|
|
42
|
+
var import_sun_sdk_core5 = require("@sun-protocol/sun-sdk-core");
|
|
43
|
+
|
|
44
|
+
// src/common.ts
|
|
45
|
+
var import_sun_sdk_core3 = require("@sun-protocol/sun-sdk-core");
|
|
46
|
+
|
|
47
|
+
// src/context.ts
|
|
48
|
+
var import_sun_sdk_api2 = require("@sun-protocol/sun-sdk-api");
|
|
49
|
+
var import_sun_sdk_chains = require("@sun-protocol/sun-sdk-chains");
|
|
50
|
+
var import_sun_sdk_core2 = require("@sun-protocol/sun-sdk-core");
|
|
51
|
+
var import_sun_sdk_runtime = require("@sun-protocol/sun-sdk-runtime");
|
|
52
|
+
|
|
53
|
+
// src/execute-http-action.ts
|
|
54
|
+
var import_sun_sdk_api = require("@sun-protocol/sun-sdk-api");
|
|
55
|
+
var import_sun_sdk_core = require("@sun-protocol/sun-sdk-core");
|
|
56
|
+
async function executeHttpAction(action, fetchImpl, jsonParser = import_sun_sdk_api.defaultJsonParser) {
|
|
57
|
+
if (action.type !== "http-request" || action.execute !== "api") {
|
|
58
|
+
throw new import_sun_sdk_core.SDKError("SDK_INVALID_ARGUMENT", `Action ${action.id} is not an HTTP action`, {
|
|
59
|
+
actionId: action.id,
|
|
60
|
+
actionType: action.type
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
const request = action.request;
|
|
64
|
+
const headers = {
|
|
65
|
+
Accept: "application/json",
|
|
66
|
+
...request.headers
|
|
67
|
+
};
|
|
68
|
+
let body;
|
|
69
|
+
if (request.body !== void 0) {
|
|
70
|
+
if (typeof request.body === "string") {
|
|
71
|
+
body = request.body;
|
|
72
|
+
} else {
|
|
73
|
+
body = JSON.stringify(request.body);
|
|
74
|
+
if (!headers["Content-Type"]) headers["Content-Type"] = "application/json";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return (0, import_sun_sdk_api.requestJson)(request.url, {
|
|
78
|
+
service: "Protocols HTTP action",
|
|
79
|
+
fetchImpl,
|
|
80
|
+
jsonParser,
|
|
81
|
+
init: {
|
|
82
|
+
method: request.method,
|
|
83
|
+
headers,
|
|
84
|
+
...body !== void 0 ? { body } : {}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/context.ts
|
|
90
|
+
function requireRuntime(context, operation) {
|
|
91
|
+
if (!context.runtime) {
|
|
92
|
+
throw new import_sun_sdk_core2.SDKError("SDK_RUNTIME_ERROR", `${operation} requires a runtime`, {
|
|
93
|
+
operation,
|
|
94
|
+
network: context.network
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return context.runtime;
|
|
98
|
+
}
|
|
99
|
+
function resolveProtocolContract(context, overrides, name) {
|
|
100
|
+
const configured = context.chain.contracts;
|
|
101
|
+
const address = overrides?.[name] ?? configured[name];
|
|
102
|
+
if (!address) {
|
|
103
|
+
throw new import_sun_sdk_core2.SDKError("SDK_INVALID_ARGUMENT", `No configured address for ${name}`, { name });
|
|
104
|
+
}
|
|
105
|
+
return (0, import_sun_sdk_core2.assertTronAddress)(address, name);
|
|
106
|
+
}
|
|
107
|
+
async function requireWalletAddress(context, operation) {
|
|
108
|
+
return (await requireWalletSnapshot(context, operation)).address;
|
|
109
|
+
}
|
|
110
|
+
async function requireWalletSnapshot(context, operation) {
|
|
111
|
+
const runtime = requireRuntime(context, operation);
|
|
112
|
+
const wallet = await runtime.requireWallet(operation);
|
|
113
|
+
return {
|
|
114
|
+
wallet,
|
|
115
|
+
address: (0, import_sun_sdk_core2.assertTronAddress)(await wallet.getAddress(), "wallet address")
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function createContextExecutor(context) {
|
|
119
|
+
const runtime = context.runtime;
|
|
120
|
+
let walletPromise;
|
|
121
|
+
const requireExecutionWallet = () => {
|
|
122
|
+
walletPromise ??= runtime?.requireWallet("transaction plan execution");
|
|
123
|
+
if (!walletPromise) {
|
|
124
|
+
return Promise.reject(new import_sun_sdk_core2.SDKError("SDK_RUNTIME_ERROR", "Transaction plan execution requires a runtime"));
|
|
125
|
+
}
|
|
126
|
+
return walletPromise;
|
|
127
|
+
};
|
|
128
|
+
return {
|
|
129
|
+
network: context.network,
|
|
130
|
+
...runtime ? {
|
|
131
|
+
getAddress: async () => {
|
|
132
|
+
const wallet = await requireExecutionWallet();
|
|
133
|
+
return (0, import_sun_sdk_core2.assertTronAddress)(await wallet.getAddress(), "wallet address");
|
|
134
|
+
},
|
|
135
|
+
sendAction: async (action) => {
|
|
136
|
+
const wallet = await requireExecutionWallet();
|
|
137
|
+
return runtime.sendAction(action, { wallet });
|
|
138
|
+
},
|
|
139
|
+
...runtime.waitForTransactionReceipt ? { waitForTransactionReceipt: runtime.waitForTransactionReceipt.bind(runtime) } : {},
|
|
140
|
+
signTypedData: async (payload) => {
|
|
141
|
+
const wallet = await requireExecutionWallet();
|
|
142
|
+
return runtime.signTypedData(payload, { wallet });
|
|
143
|
+
}
|
|
144
|
+
} : {},
|
|
145
|
+
executeHttpAction: (action) => executeHttpAction(action, context.fetchImpl, context.jsonParser)
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/common.ts
|
|
150
|
+
var PERCENT_PATTERN = /^(\d+)(?:\.(\d+))?%$/;
|
|
151
|
+
var BASE_UNIT_PATTERN = /^\d+$/;
|
|
152
|
+
function parseSlippage(value) {
|
|
153
|
+
if (value instanceof import_sun_sdk_core3.Percent) {
|
|
154
|
+
validateSlippage(value);
|
|
155
|
+
return value;
|
|
156
|
+
}
|
|
157
|
+
const match = PERCENT_PATTERN.exec(value);
|
|
158
|
+
if (!match) {
|
|
159
|
+
throw new import_sun_sdk_core3.SDKError("SDK_INVALID_ARGUMENT", "Slippage must be a percentage string", {
|
|
160
|
+
slippage: value
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
const whole = match[1] ?? "0";
|
|
164
|
+
const fraction = match[2] ?? "";
|
|
165
|
+
const scale = 10n ** BigInt(fraction.length);
|
|
166
|
+
const percentageUnits = BigInt(whole) * scale + BigInt(fraction || "0");
|
|
167
|
+
const percent = new import_sun_sdk_core3.Percent(percentageUnits.toString(), (100n * scale).toString());
|
|
168
|
+
validateSlippage(percent);
|
|
169
|
+
return percent;
|
|
170
|
+
}
|
|
171
|
+
function validateSlippage(value) {
|
|
172
|
+
if (value.lessThan(0) || value.greaterThan(1)) {
|
|
173
|
+
throw new import_sun_sdk_core3.SDKError("SDK_INVALID_ARGUMENT", "Slippage must be between 0% and 100%", {
|
|
174
|
+
slippage: value.toSignificant()
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function slippageToBips(value) {
|
|
179
|
+
const percent = parseSlippage(value);
|
|
180
|
+
const numerator = BigInt(percent.numerator.toString()) * 10000n;
|
|
181
|
+
const denominator = BigInt(percent.denominator.toString());
|
|
182
|
+
if (numerator % denominator !== 0n) {
|
|
183
|
+
throw new import_sun_sdk_core3.SDKError("SDK_INVALID_ARGUMENT", "Slippage must resolve to a whole basis point", {
|
|
184
|
+
slippage: typeof value === "string" ? value : value.toSignificant()
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
return Number(numerator / denominator);
|
|
188
|
+
}
|
|
189
|
+
function applySlippageMax(expected, slippageBips) {
|
|
190
|
+
const numerator = expected * BigInt(1e4 + slippageBips);
|
|
191
|
+
return (numerator + 9999n) / 10000n;
|
|
192
|
+
}
|
|
193
|
+
function normalizeBaseUnitAmount(value, label = "amount", options = {}) {
|
|
194
|
+
if (typeof value === "number" && !Number.isSafeInteger(value)) {
|
|
195
|
+
throw new import_sun_sdk_core3.SDKError("SDK_INVALID_ARGUMENT", `${label} must be a safe integer`, {
|
|
196
|
+
label,
|
|
197
|
+
value
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const text = value.toString();
|
|
201
|
+
if (!BASE_UNIT_PATTERN.test(text)) {
|
|
202
|
+
throw new import_sun_sdk_core3.SDKError("SDK_INVALID_ARGUMENT", `${label} must be an unsigned base-unit integer`, {
|
|
203
|
+
label,
|
|
204
|
+
value: text
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
const amount = BigInt(text);
|
|
208
|
+
if (options.allowZero ? amount < 0n : amount <= 0n) {
|
|
209
|
+
throw new import_sun_sdk_core3.SDKError("SDK_INVALID_ARGUMENT", `${label} must be ${options.allowZero ? "non-negative" : "positive"}`, {
|
|
210
|
+
label,
|
|
211
|
+
value: text
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return amount.toString();
|
|
215
|
+
}
|
|
216
|
+
function normalizeDeadline(context, options = {}) {
|
|
217
|
+
if (options.deadline !== void 0) {
|
|
218
|
+
return normalizeBaseUnitAmount(options.deadline, "deadline");
|
|
219
|
+
}
|
|
220
|
+
const ttlSeconds = options.ttlSeconds ?? 1200;
|
|
221
|
+
if (!Number.isSafeInteger(ttlSeconds) || ttlSeconds <= 0) {
|
|
222
|
+
throw new import_sun_sdk_core3.SDKError("SDK_INVALID_ARGUMENT", "ttlSeconds must be a positive safe integer", {
|
|
223
|
+
ttlSeconds
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return (BigInt(Math.floor(context.now() / 1e3)) + BigInt(ttlSeconds)).toString();
|
|
227
|
+
}
|
|
228
|
+
async function normalizeRecipient(context, recipient, operation, walletAddress) {
|
|
229
|
+
if (recipient !== void 0) return (0, import_sun_sdk_core3.assertTronAddress)(recipient, "recipient");
|
|
230
|
+
if (walletAddress !== void 0) return walletAddress;
|
|
231
|
+
return requireWalletAddress(context, operation);
|
|
232
|
+
}
|
|
233
|
+
function nextPlanId(context, prefix) {
|
|
234
|
+
return `${prefix}-${context.idFactory()}`;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/approval/check.ts
|
|
238
|
+
async function readClassicAllowance(runtime, requirement) {
|
|
239
|
+
if (requirement.asset.type === "native") return "0";
|
|
240
|
+
const result = await runtime.readContract({
|
|
241
|
+
target: requirement.asset.token,
|
|
242
|
+
functionSelector: "allowance(address,address)",
|
|
243
|
+
parameters: [
|
|
244
|
+
{ type: "address", value: requirement.owner },
|
|
245
|
+
{ type: "address", value: requirement.spender }
|
|
246
|
+
],
|
|
247
|
+
outputs: [{ type: "uint256", name: "allowance" }]
|
|
248
|
+
});
|
|
249
|
+
return BigInt(result).toString();
|
|
250
|
+
}
|
|
251
|
+
async function checkClassicAllowance(runtime, requirement) {
|
|
252
|
+
if (requirement.asset.type === "native") {
|
|
253
|
+
return { allowance: requirement.amount, sufficient: true };
|
|
254
|
+
}
|
|
255
|
+
const allowance = await readClassicAllowance(runtime, requirement);
|
|
256
|
+
return {
|
|
257
|
+
allowance,
|
|
258
|
+
sufficient: BigInt(allowance) >= BigInt(requirement.amount)
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// src/approval/classic.ts
|
|
263
|
+
var import_sun_sdk_core4 = require("@sun-protocol/sun-sdk-core");
|
|
264
|
+
function buildClassicApproveAction(requirement, options = {}) {
|
|
265
|
+
if (requirement.asset.type === "native") return void 0;
|
|
266
|
+
return (0, import_sun_sdk_core4.createApproveAction)({
|
|
267
|
+
id: `approve-${requirement.asset.token}-${requirement.spender}`,
|
|
268
|
+
target: requirement.asset.token,
|
|
269
|
+
spender: requirement.spender,
|
|
270
|
+
amount: options.amount === "exact" ? requirement.amount : import_sun_sdk_core4.MaxUint256.toString(),
|
|
271
|
+
...options.feeLimit !== void 0 ? { feeLimit: options.feeLimit } : {}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/approval/plan.ts
|
|
276
|
+
async function planApproval(context, requirement, options = {}) {
|
|
277
|
+
const mode = options.mode ?? "auto";
|
|
278
|
+
normalizeBaseUnitAmount(requirement.amount, "approval amount");
|
|
279
|
+
if (requirement.asset.type === "native") {
|
|
280
|
+
return {
|
|
281
|
+
mode,
|
|
282
|
+
requirement,
|
|
283
|
+
allowance: requirement.amount,
|
|
284
|
+
sufficient: true,
|
|
285
|
+
actions: []
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
if (mode === "none") {
|
|
289
|
+
return { mode, requirement, actions: [] };
|
|
290
|
+
}
|
|
291
|
+
if (mode === "permit2") {
|
|
292
|
+
throw new import_sun_sdk_core5.SDKError(
|
|
293
|
+
"SDK_WALLET_CAPABILITY_ERROR",
|
|
294
|
+
"Permit2 approval is unavailable until a Permit2 adapter is configured",
|
|
295
|
+
{ mode, token: requirement.asset.token }
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
const runtime = requireRuntime(context, "Token allowance check");
|
|
299
|
+
const check = await checkClassicAllowance(runtime, requirement);
|
|
300
|
+
if (check.sufficient) {
|
|
301
|
+
return {
|
|
302
|
+
mode,
|
|
303
|
+
requirement,
|
|
304
|
+
allowance: check.allowance,
|
|
305
|
+
sufficient: true,
|
|
306
|
+
actions: []
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
const action = buildClassicApproveAction(requirement, options);
|
|
310
|
+
return {
|
|
311
|
+
mode,
|
|
312
|
+
requirement,
|
|
313
|
+
allowance: check.allowance,
|
|
314
|
+
sufficient: false,
|
|
315
|
+
actions: action ? [action] : []
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// src/execute-plan.ts
|
|
320
|
+
var import_sun_sdk_core7 = require("@sun-protocol/sun-sdk-core");
|
|
321
|
+
|
|
322
|
+
// src/errors.ts
|
|
323
|
+
var import_sun_sdk_core6 = require("@sun-protocol/sun-sdk-core");
|
|
324
|
+
function isWalletRejection(error) {
|
|
325
|
+
if (!error || typeof error !== "object") return false;
|
|
326
|
+
const candidate = error;
|
|
327
|
+
return candidate.code === 4001 || typeof candidate.message === "string" && /reject|denied|cancel/i.test(candidate.message);
|
|
328
|
+
}
|
|
329
|
+
function normalizeProtocolError(error, fallbackCode, message, context = {}) {
|
|
330
|
+
if (error instanceof import_sun_sdk_core6.SDKError) return error;
|
|
331
|
+
const code = isWalletRejection(error) ? "SDK_WALLET_REJECTED" : fallbackCode;
|
|
332
|
+
return new import_sun_sdk_core6.SDKError(code, message, { ...context, cause: error });
|
|
333
|
+
}
|
|
334
|
+
function withExecutionContext(error, context) {
|
|
335
|
+
return new import_sun_sdk_core6.SDKError(error.code, error.message, {
|
|
336
|
+
...error.context,
|
|
337
|
+
actionId: context.actionId,
|
|
338
|
+
completedActionIds: context.completedActionIds,
|
|
339
|
+
cause: error
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// src/execute-plan.ts
|
|
344
|
+
async function executePlanAction(action, executor, completedActions = []) {
|
|
345
|
+
try {
|
|
346
|
+
if (action.waitForReceipt && !executor.waitForTransactionReceipt) {
|
|
347
|
+
throw new import_sun_sdk_core7.SDKError(
|
|
348
|
+
"SDK_RUNTIME_ERROR",
|
|
349
|
+
`Action ${action.id} requires transaction receipt waiting capability`,
|
|
350
|
+
{ actionId: action.id, finality: action.waitForReceipt }
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
if (action.execute === "runtime") {
|
|
354
|
+
let actionToSend;
|
|
355
|
+
let materializedAction;
|
|
356
|
+
if (action.type === "deferred-contract-call") {
|
|
357
|
+
if (!executor.materializeAction) {
|
|
358
|
+
throw new import_sun_sdk_core7.SDKError("SDK_RUNTIME_ERROR", `Action ${action.id} requires a deferred action materializer`);
|
|
359
|
+
}
|
|
360
|
+
const resolvedAction = await executor.materializeAction(action, completedActions);
|
|
361
|
+
materializedAction = {
|
|
362
|
+
...resolvedAction,
|
|
363
|
+
id: action.id,
|
|
364
|
+
...action.intent === void 0 ? {} : { intent: action.intent }
|
|
365
|
+
};
|
|
366
|
+
actionToSend = materializedAction;
|
|
367
|
+
} else if (action.type === "contract-call" || action.type === "approve") {
|
|
368
|
+
actionToSend = action;
|
|
369
|
+
} else {
|
|
370
|
+
throw new import_sun_sdk_core7.SDKError(
|
|
371
|
+
"SDK_RUNTIME_ERROR",
|
|
372
|
+
`Runtime action ${action.id} materialized to unsupported type: ${action.type}`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
if (!executor.sendAction) {
|
|
376
|
+
throw new import_sun_sdk_core7.SDKError("SDK_RUNTIME_ERROR", `Action ${action.id} requires a runtime executor`);
|
|
377
|
+
}
|
|
378
|
+
const result = await executor.sendAction(actionToSend);
|
|
379
|
+
const receipt = action.waitForReceipt && executor.waitForTransactionReceipt ? await executor.waitForTransactionReceipt(result.txid, { finality: action.waitForReceipt }) : void 0;
|
|
380
|
+
if (receipt && !receipt.success) {
|
|
381
|
+
throw new import_sun_sdk_core7.SDKError("SDK_CONTRACT_ERROR", `Transaction ${result.txid} failed`, {
|
|
382
|
+
actionId: action.id,
|
|
383
|
+
txid: result.txid,
|
|
384
|
+
receipt
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
action,
|
|
389
|
+
result,
|
|
390
|
+
txid: result.txid,
|
|
391
|
+
...receipt ? { receipt } : {},
|
|
392
|
+
...materializedAction ? { materializedAction } : {}
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
if (action.execute === "wallet-signature") {
|
|
396
|
+
if (!action.typedData) {
|
|
397
|
+
throw new import_sun_sdk_core7.SDKError("SDK_INVALID_ARGUMENT", `Action ${action.id} is missing typedData`);
|
|
398
|
+
}
|
|
399
|
+
if (!executor.signTypedData) {
|
|
400
|
+
throw new import_sun_sdk_core7.SDKError("SDK_WALLET_CAPABILITY_ERROR", `Action ${action.id} requires typed-data signing`);
|
|
401
|
+
}
|
|
402
|
+
const signature = await executor.signTypedData(action.typedData);
|
|
403
|
+
return { action, result: signature };
|
|
404
|
+
}
|
|
405
|
+
if (action.execute === "api") {
|
|
406
|
+
if (!executor.executeHttpAction) {
|
|
407
|
+
throw new import_sun_sdk_core7.SDKError("SDK_RUNTIME_ERROR", `Action ${action.id} requires an API executor`);
|
|
408
|
+
}
|
|
409
|
+
return { action, result: await executor.executeHttpAction(action) };
|
|
410
|
+
}
|
|
411
|
+
throw new import_sun_sdk_core7.SDKError("SDK_RUNTIME_ERROR", "Unsupported action executor");
|
|
412
|
+
} catch (error) {
|
|
413
|
+
const normalized = normalizeProtocolError(
|
|
414
|
+
error,
|
|
415
|
+
action.execute === "api" ? "SDK_API_ERROR" : "SDK_RUNTIME_ERROR",
|
|
416
|
+
`Failed to execute action ${action.id}`
|
|
417
|
+
);
|
|
418
|
+
throw withExecutionContext(normalized, {
|
|
419
|
+
actionId: action.id,
|
|
420
|
+
completedActionIds: completedActions.map((result) => result.action.id)
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
async function executeTransactionPlan(plan, executor) {
|
|
425
|
+
if (executor.network && executor.network !== plan.network) {
|
|
426
|
+
throw new import_sun_sdk_core7.SDKError("SDK_INVALID_ARGUMENT", "Plan and executor network mismatch", {
|
|
427
|
+
planNetwork: plan.network,
|
|
428
|
+
executorNetwork: executor.network
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
if (plan.owner) {
|
|
432
|
+
if (!executor.getAddress) {
|
|
433
|
+
throw new import_sun_sdk_core7.SDKError("SDK_RUNTIME_ERROR", `Plan ${plan.id} requires a wallet address`, {
|
|
434
|
+
planId: plan.id,
|
|
435
|
+
owner: plan.owner
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
const actualAddress = await executor.getAddress();
|
|
439
|
+
if (actualAddress !== plan.owner) {
|
|
440
|
+
throw new import_sun_sdk_core7.SDKError(
|
|
441
|
+
"SDK_WALLET_ACCOUNT_MISMATCH",
|
|
442
|
+
"Transaction plan was created for a different wallet",
|
|
443
|
+
{
|
|
444
|
+
planId: plan.id,
|
|
445
|
+
expectedAddress: plan.owner,
|
|
446
|
+
actualAddress
|
|
447
|
+
}
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
const orderedActions = [...plan.approvals, ...plan.transactions];
|
|
452
|
+
const receiptAction = orderedActions.find((action) => action.waitForReceipt !== void 0);
|
|
453
|
+
if (receiptAction && !executor.waitForTransactionReceipt) {
|
|
454
|
+
throw new import_sun_sdk_core7.SDKError(
|
|
455
|
+
"SDK_RUNTIME_ERROR",
|
|
456
|
+
`Action ${receiptAction.id} requires transaction receipt waiting capability`,
|
|
457
|
+
{ planId: plan.id, actionId: receiptAction.id, finality: receiptAction.waitForReceipt }
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
const actionResults = [];
|
|
461
|
+
const txids = [];
|
|
462
|
+
let finalResult;
|
|
463
|
+
for (const action of orderedActions) {
|
|
464
|
+
const actionResult = await executePlanAction(action, executor, actionResults);
|
|
465
|
+
actionResults.push(actionResult);
|
|
466
|
+
if (action.execute === "runtime") {
|
|
467
|
+
if (!actionResult.txid) {
|
|
468
|
+
throw new import_sun_sdk_core7.SDKError("SDK_RUNTIME_ERROR", `Runtime action ${action.id} did not return a transaction id`);
|
|
469
|
+
}
|
|
470
|
+
txids.push(actionResult.txid);
|
|
471
|
+
const raw = typeof actionResult.result === "object" && actionResult.result !== null ? actionResult.result.raw : void 0;
|
|
472
|
+
finalResult = { type: "transaction", txid: actionResult.txid, raw };
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
if (action.execute === "wallet-signature") {
|
|
476
|
+
finalResult = { type: "signature", signature: actionResult.result };
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
finalResult = { type: "api", data: actionResult.result };
|
|
480
|
+
}
|
|
481
|
+
return {
|
|
482
|
+
plan,
|
|
483
|
+
actions: actionResults,
|
|
484
|
+
txids,
|
|
485
|
+
finalResult
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// src/internal/reads.ts
|
|
490
|
+
var import_sun_sdk_core8 = require("@sun-protocol/sun-sdk-core");
|
|
491
|
+
async function readRequiredContract(context, call, operation) {
|
|
492
|
+
const runtime = requireRuntime(context, operation);
|
|
493
|
+
const value = await runtime.readContract(call);
|
|
494
|
+
if (value === void 0 || value === null) {
|
|
495
|
+
throw new import_sun_sdk_core8.SDKError("SDK_CONTRACT_ERROR", `${operation} returned an empty result`, { call });
|
|
496
|
+
}
|
|
497
|
+
return value;
|
|
498
|
+
}
|
|
499
|
+
async function readMulticallRequired(context, calls, operation) {
|
|
500
|
+
const runtime = requireRuntime(context, operation);
|
|
501
|
+
const results = await runtime.multicall(calls);
|
|
502
|
+
return results.map((result, index) => {
|
|
503
|
+
if (!result.success || result.value === void 0) {
|
|
504
|
+
throw new import_sun_sdk_core8.SDKError("SDK_CONTRACT_ERROR", `${operation} multicall item failed`, {
|
|
505
|
+
index,
|
|
506
|
+
call: calls[index],
|
|
507
|
+
error: result.error
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
return result.value;
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// src/internal/tokens.ts
|
|
515
|
+
var import_sun_sdk_chains2 = require("@sun-protocol/sun-sdk-chains");
|
|
516
|
+
var import_sun_sdk_core9 = require("@sun-protocol/sun-sdk-core");
|
|
517
|
+
var import_sun_sdk_sunswap_v2 = require("@sun-protocol/sun-sdk-sunswap-v2");
|
|
518
|
+
var PERCENT_PATTERN2 = /^(\d+)(?:\.(\d+))?%$/;
|
|
519
|
+
function isNativeTrxInput(context, token) {
|
|
520
|
+
return token.toUpperCase() === "TRX" || token === context.chain.contracts.trx;
|
|
521
|
+
}
|
|
522
|
+
function evmHexToTronAddress(address, label = "address") {
|
|
523
|
+
const normalized = address.startsWith("0x") ? address.toLowerCase() : `0x${address.toLowerCase()}`;
|
|
524
|
+
return (0, import_sun_sdk_sunswap_v2.evmHexToTronAddress)(normalized, label);
|
|
525
|
+
}
|
|
526
|
+
function tronAddressToEvmAddress(address, label = "address") {
|
|
527
|
+
return `0x${(0, import_sun_sdk_sunswap_v2.tronAddressToEvmHex)((0, import_sun_sdk_core9.assertTronAddress)(address, label))}`;
|
|
528
|
+
}
|
|
529
|
+
function normalizeProtocolToken(context, token, label, metadata = {}) {
|
|
530
|
+
const wrappedContractAddress = (0, import_sun_sdk_chains2.getWrappedNativeAddress)(context.network);
|
|
531
|
+
const kind = isNativeTrxInput(context, token) ? "native" : "token";
|
|
532
|
+
const contractAddress = kind === "native" ? import_sun_sdk_core9.TRON_ZERO_ADDRESS : (0, import_sun_sdk_core9.assertTronAddress)(token, label);
|
|
533
|
+
const mathAddress = kind === "native" ? wrappedContractAddress : contractAddress;
|
|
534
|
+
const evmAddress = `0x${(0, import_sun_sdk_sunswap_v2.tronAddressToEvmHex)(mathAddress)}`.toLowerCase();
|
|
535
|
+
return {
|
|
536
|
+
kind,
|
|
537
|
+
input: token,
|
|
538
|
+
contractAddress,
|
|
539
|
+
wrappedContractAddress,
|
|
540
|
+
token: new import_sun_sdk_core9.Token(
|
|
541
|
+
context.chain.uniswapChainId,
|
|
542
|
+
evmAddress,
|
|
543
|
+
metadata.decimals ?? 6,
|
|
544
|
+
metadata.symbol,
|
|
545
|
+
metadata.name,
|
|
546
|
+
true
|
|
547
|
+
)
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
function parseLiquidityPercentageInput(value, label) {
|
|
551
|
+
if (value instanceof import_sun_sdk_core9.Percent) return value;
|
|
552
|
+
const match = PERCENT_PATTERN2.exec(value);
|
|
553
|
+
if (!match) {
|
|
554
|
+
throw new import_sun_sdk_core9.SDKError("SDK_INVALID_ARGUMENT", `${label} must be a percentage string`, {
|
|
555
|
+
label,
|
|
556
|
+
percentage: value
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
const whole = match[1] ?? "0";
|
|
560
|
+
const fraction = match[2] ?? "";
|
|
561
|
+
const scale = 10n ** BigInt(fraction.length);
|
|
562
|
+
const percentageUnits = BigInt(whole) * scale + BigInt(fraction || "0");
|
|
563
|
+
return new import_sun_sdk_core9.Percent(percentageUnits.toString(), (100n * scale).toString());
|
|
564
|
+
}
|
|
565
|
+
function normalizeLiquidityPercentage(value, label) {
|
|
566
|
+
const percent = parseLiquidityPercentageInput(value, label);
|
|
567
|
+
if (percent.equalTo(0)) {
|
|
568
|
+
throw new import_sun_sdk_core9.SDKError("SDK_INVALID_ARGUMENT", `${label} must be greater than 0%`, {
|
|
569
|
+
label,
|
|
570
|
+
percentage: percent.toSignificant()
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
if (percent.greaterThan(1)) {
|
|
574
|
+
throw new import_sun_sdk_core9.SDKError("SDK_INVALID_ARGUMENT", `${label} must be at most 100%`, {
|
|
575
|
+
label,
|
|
576
|
+
percentage: percent.toSignificant()
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
return percent;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// src/internal/intents.ts
|
|
583
|
+
function attachApprovalIntent(actions, scope, tokenSymbol) {
|
|
584
|
+
const intent = { kind: "approve", scope, tokenSymbol };
|
|
585
|
+
return actions.map((action) => action.execute === "runtime" ? { ...action, intent } : action);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// src/liquidity/intents.ts
|
|
589
|
+
var SUNSWAP_V2_LIQUIDITY_PROTOCOL_ID = "sunswap-v2";
|
|
590
|
+
var SUNSWAP_V3_LIQUIDITY_PROTOCOL_ID = "sunswap-v3";
|
|
591
|
+
var SUNSWAP_V4_LIQUIDITY_PROTOCOL_ID = "sunswap-v4";
|
|
592
|
+
function liquidityIntentScope(protocol, operation) {
|
|
593
|
+
return { domain: "liquidity", protocol, operation };
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// src/liquidity/v2.ts
|
|
597
|
+
var DEFAULT_SLIPPAGE = "0.5%";
|
|
598
|
+
function parseContractInteger(value, label) {
|
|
599
|
+
const normalized = Array.isArray(value) && value.length === 1 ? value[0] : value;
|
|
600
|
+
if (normalized === null || normalized === void 0 || typeof normalized !== "string" && typeof normalized !== "number" && typeof normalized !== "bigint" && typeof normalized !== "object") {
|
|
601
|
+
throw new import_sun_sdk_core10.SDKError("SDK_CONTRACT_ERROR", `${label} must be an integer-like contract result`, { value });
|
|
602
|
+
}
|
|
603
|
+
try {
|
|
604
|
+
const text = normalized.toString();
|
|
605
|
+
if (!/^\d+$/.test(text)) throw new Error("not an unsigned integer");
|
|
606
|
+
return BigInt(text).toString();
|
|
607
|
+
} catch {
|
|
608
|
+
throw new import_sun_sdk_core10.SDKError("SDK_CONTRACT_ERROR", `${label} must be an integer-like contract result`, { value });
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
function parseContractAddress(value, label) {
|
|
612
|
+
const normalized = Array.isArray(value) && value.length === 1 ? value[0] : value;
|
|
613
|
+
if (typeof normalized !== "string") {
|
|
614
|
+
throw new import_sun_sdk_core10.SDKError("SDK_CONTRACT_ERROR", `${label} must be an address contract result`, { value });
|
|
615
|
+
}
|
|
616
|
+
try {
|
|
617
|
+
return /^0x[0-9a-fA-F]{40}$/.test(normalized) ? evmHexToTronAddress(normalized, label) : (0, import_sun_sdk_core10.assertTronAddress)(normalized, label);
|
|
618
|
+
} catch {
|
|
619
|
+
throw new import_sun_sdk_core10.SDKError("SDK_CONTRACT_ERROR", `${label} must be an address contract result`, { value });
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
function createPairContext(context, tokenAInput, tokenBInput) {
|
|
623
|
+
const tokenA = normalizeProtocolToken(context, tokenAInput, "tokenA");
|
|
624
|
+
const tokenB = normalizeProtocolToken(context, tokenBInput, "tokenB");
|
|
625
|
+
if (tokenA.token.equals(tokenB.token)) {
|
|
626
|
+
throw new import_sun_sdk_core10.SDKError("SDK_INVALID_ARGUMENT", "V2 liquidity tokens must be different", {
|
|
627
|
+
tokenA: tokenAInput,
|
|
628
|
+
tokenB: tokenBInput
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
return {
|
|
632
|
+
tokenA,
|
|
633
|
+
tokenB,
|
|
634
|
+
tokenAContract: tokenA.kind === "native" ? tokenA.wrappedContractAddress : tokenA.contractAddress,
|
|
635
|
+
tokenBContract: tokenB.kind === "native" ? tokenB.wrappedContractAddress : tokenB.contractAddress,
|
|
636
|
+
pair: (0, import_sun_sdk_sunswap_v22.computeV2PairAddress)({
|
|
637
|
+
network: context.network,
|
|
638
|
+
tokenA: tokenA.kind === "native" ? tokenA.wrappedContractAddress : tokenA.contractAddress,
|
|
639
|
+
tokenB: tokenB.kind === "native" ? tokenB.wrappedContractAddress : tokenB.contractAddress
|
|
640
|
+
}),
|
|
641
|
+
tokenASortsBefore: tokenA.token.sortsBefore(tokenB.token)
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
function parsePairReserves(result, tokenASortsBefore) {
|
|
645
|
+
if (!Array.isArray(result) || result.length < 2) {
|
|
646
|
+
throw new import_sun_sdk_core10.SDKError("SDK_CONTRACT_ERROR", "V2 pair reserves result is malformed", { result });
|
|
647
|
+
}
|
|
648
|
+
let normalized;
|
|
649
|
+
try {
|
|
650
|
+
normalized = (0, import_sun_sdk_sunswap_v22.normalizeV2ReserveResult)(result);
|
|
651
|
+
} catch {
|
|
652
|
+
throw new import_sun_sdk_core10.SDKError("SDK_CONTRACT_ERROR", "V2 pair reserves result is malformed", { result });
|
|
653
|
+
}
|
|
654
|
+
const reserve0 = parseContractInteger(normalized.reserve0, "reserve0");
|
|
655
|
+
const reserve1 = parseContractInteger(normalized.reserve1, "reserve1");
|
|
656
|
+
return tokenASortsBefore ? { reserveA: reserve0, reserveB: reserve1 } : { reserveA: reserve1, reserveB: reserve0 };
|
|
657
|
+
}
|
|
658
|
+
function resolveAddAmounts(input) {
|
|
659
|
+
const reserveA = BigInt(input.reserves.reserveA);
|
|
660
|
+
const reserveB = BigInt(input.reserves.reserveB);
|
|
661
|
+
if (reserveA === 0n && reserveB === 0n) {
|
|
662
|
+
return { amountA: input.amountADesired, amountB: input.amountBDesired };
|
|
663
|
+
}
|
|
664
|
+
if (reserveA === 0n || reserveB === 0n) {
|
|
665
|
+
throw new import_sun_sdk_core10.SDKError("SDK_CONTRACT_ERROR", "V2 pair has inconsistent zero reserves", {
|
|
666
|
+
reserveA: input.reserves.reserveA,
|
|
667
|
+
reserveB: input.reserves.reserveB
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
const amountBOptimal = (0, import_sun_sdk_sunswap_v22.quoteV2LiquidityAmount)(
|
|
671
|
+
input.amountADesired,
|
|
672
|
+
input.reserves.reserveA,
|
|
673
|
+
input.reserves.reserveB
|
|
674
|
+
);
|
|
675
|
+
if (BigInt(amountBOptimal) <= BigInt(input.amountBDesired)) {
|
|
676
|
+
return { amountA: input.amountADesired, amountB: amountBOptimal };
|
|
677
|
+
}
|
|
678
|
+
const amountAOptimal = (0, import_sun_sdk_sunswap_v22.quoteV2LiquidityAmount)(
|
|
679
|
+
input.amountBDesired,
|
|
680
|
+
input.reserves.reserveB,
|
|
681
|
+
input.reserves.reserveA
|
|
682
|
+
);
|
|
683
|
+
if (BigInt(amountAOptimal) > BigInt(input.amountADesired)) {
|
|
684
|
+
throw new import_sun_sdk_core10.SDKError("SDK_CONTRACT_ERROR", "V2 pair reserve quote exceeds desired liquidity amounts", {
|
|
685
|
+
amountADesired: input.amountADesired,
|
|
686
|
+
amountBDesired: input.amountBDesired,
|
|
687
|
+
amountAOptimal,
|
|
688
|
+
amountBOptimal
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
return { amountA: amountAOptimal, amountB: input.amountBDesired };
|
|
692
|
+
}
|
|
693
|
+
function createPairEntity(pair, reserves) {
|
|
694
|
+
return new import_sun_sdk_sunswap_v22.Pair(
|
|
695
|
+
import_sun_sdk_core10.CurrencyAmount.fromRawAmount(pair.tokenA.token, reserves.reserveA),
|
|
696
|
+
import_sun_sdk_core10.CurrencyAmount.fromRawAmount(pair.tokenB.token, reserves.reserveB)
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
function validateAddAmounts(pair, reserves, amounts) {
|
|
700
|
+
if (BigInt(amounts.amountA) === 0n || BigInt(amounts.amountB) === 0n) {
|
|
701
|
+
throw new import_sun_sdk_core10.SDKError("SDK_INVALID_ARGUMENT", "V2 add liquidity amounts must remain positive after reserve optimization", {
|
|
702
|
+
...amounts,
|
|
703
|
+
...reserves
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
if (BigInt(reserves.reserveA) !== 0n || BigInt(reserves.reserveB) !== 0n) return;
|
|
707
|
+
const pairEntity = createPairEntity(pair, reserves);
|
|
708
|
+
try {
|
|
709
|
+
pairEntity.getLiquidityMinted(
|
|
710
|
+
import_sun_sdk_core10.CurrencyAmount.fromRawAmount(pairEntity.liquidityToken, "0"),
|
|
711
|
+
import_sun_sdk_core10.CurrencyAmount.fromRawAmount(pair.tokenA.token, amounts.amountA),
|
|
712
|
+
import_sun_sdk_core10.CurrencyAmount.fromRawAmount(pair.tokenB.token, amounts.amountB)
|
|
713
|
+
);
|
|
714
|
+
} catch {
|
|
715
|
+
throw new import_sun_sdk_core10.SDKError("SDK_INVALID_ARGUMENT", "Initial V2 liquidity is too small to exceed MINIMUM_LIQUIDITY", {
|
|
716
|
+
...amounts
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
async function planTokenApproval(input) {
|
|
721
|
+
if (input.token.kind === "native") return [];
|
|
722
|
+
const approval = await planApproval(
|
|
723
|
+
input.context,
|
|
724
|
+
{
|
|
725
|
+
asset: { type: "token", token: input.token.contractAddress },
|
|
726
|
+
owner: input.owner,
|
|
727
|
+
spender: input.router,
|
|
728
|
+
amount: input.amount
|
|
729
|
+
},
|
|
730
|
+
{ mode: input.mode ?? "auto" }
|
|
731
|
+
);
|
|
732
|
+
return attachApprovalIntent(
|
|
733
|
+
approval.actions,
|
|
734
|
+
liquidityIntentScope(SUNSWAP_V2_LIQUIDITY_PROTOCOL_ID, "add-liquidity"),
|
|
735
|
+
input.token.token.symbol ?? input.token.input
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
function buildAddAction(input) {
|
|
739
|
+
const options = {
|
|
740
|
+
network: input.context.network,
|
|
741
|
+
router: input.router,
|
|
742
|
+
recipient: input.recipient,
|
|
743
|
+
deadline: input.deadline,
|
|
744
|
+
id: `${input.planId}-tx`,
|
|
745
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {}
|
|
746
|
+
};
|
|
747
|
+
const action = input.pair.tokenA.kind === "native" ? (0, import_sun_sdk_sunswap_v22.buildV2AddLiquidityEthAction)({
|
|
748
|
+
...options,
|
|
749
|
+
token: input.pair.tokenB.token,
|
|
750
|
+
amountTokenDesired: input.amountB,
|
|
751
|
+
amountEthDesired: input.amountA,
|
|
752
|
+
amountTokenMin: input.amountBMin,
|
|
753
|
+
amountEthMin: input.amountAMin
|
|
754
|
+
}) : input.pair.tokenB.kind === "native" ? (0, import_sun_sdk_sunswap_v22.buildV2AddLiquidityEthAction)({
|
|
755
|
+
...options,
|
|
756
|
+
token: input.pair.tokenA.token,
|
|
757
|
+
amountTokenDesired: input.amountA,
|
|
758
|
+
amountEthDesired: input.amountB,
|
|
759
|
+
amountTokenMin: input.amountAMin,
|
|
760
|
+
amountEthMin: input.amountBMin
|
|
761
|
+
}) : (0, import_sun_sdk_sunswap_v22.buildV2AddLiquidityAction)({
|
|
762
|
+
...options,
|
|
763
|
+
tokenA: input.pair.tokenA.token,
|
|
764
|
+
tokenB: input.pair.tokenB.token,
|
|
765
|
+
amountADesired: input.amountA,
|
|
766
|
+
amountBDesired: input.amountB,
|
|
767
|
+
amountAMin: input.amountAMin,
|
|
768
|
+
amountBMin: input.amountBMin
|
|
769
|
+
});
|
|
770
|
+
const intent = {
|
|
771
|
+
kind: "add-liquidity",
|
|
772
|
+
scope: liquidityIntentScope(SUNSWAP_V2_LIQUIDITY_PROTOCOL_ID, "add-liquidity")
|
|
773
|
+
};
|
|
774
|
+
return {
|
|
775
|
+
...action,
|
|
776
|
+
intent
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
function buildRemoveAction(input) {
|
|
780
|
+
const options = {
|
|
781
|
+
network: input.context.network,
|
|
782
|
+
router: input.router,
|
|
783
|
+
liquidity: input.liquidity,
|
|
784
|
+
recipient: input.recipient,
|
|
785
|
+
deadline: input.deadline,
|
|
786
|
+
id: `${input.planId}-tx`,
|
|
787
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {}
|
|
788
|
+
};
|
|
789
|
+
let action;
|
|
790
|
+
if (input.pair.tokenA.kind === "native") {
|
|
791
|
+
action = (0, import_sun_sdk_sunswap_v22.buildV2RemoveLiquidityEthAction)({
|
|
792
|
+
...options,
|
|
793
|
+
token: input.pair.tokenB.token,
|
|
794
|
+
amountTokenMin: input.amountBMin,
|
|
795
|
+
amountEthMin: input.amountAMin
|
|
796
|
+
});
|
|
797
|
+
} else if (input.pair.tokenB.kind === "native") {
|
|
798
|
+
action = (0, import_sun_sdk_sunswap_v22.buildV2RemoveLiquidityEthAction)({
|
|
799
|
+
...options,
|
|
800
|
+
token: input.pair.tokenA.token,
|
|
801
|
+
amountTokenMin: input.amountAMin,
|
|
802
|
+
amountEthMin: input.amountBMin
|
|
803
|
+
});
|
|
804
|
+
} else {
|
|
805
|
+
action = (0, import_sun_sdk_sunswap_v22.buildV2RemoveLiquidityAction)({
|
|
806
|
+
...options,
|
|
807
|
+
tokenA: input.pair.tokenA.token,
|
|
808
|
+
tokenB: input.pair.tokenB.token,
|
|
809
|
+
amountAMin: input.amountAMin,
|
|
810
|
+
amountBMin: input.amountBMin
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
return {
|
|
814
|
+
...action,
|
|
815
|
+
intent: {
|
|
816
|
+
kind: "remove-liquidity",
|
|
817
|
+
scope: liquidityIntentScope(SUNSWAP_V2_LIQUIDITY_PROTOCOL_ID, "remove-liquidity")
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
function resolveRemoveLiquidity(input, balance) {
|
|
822
|
+
const hasLiquidity = input.liquidity !== void 0;
|
|
823
|
+
const hasPercentage = input.percentage !== void 0;
|
|
824
|
+
if (hasLiquidity === hasPercentage) {
|
|
825
|
+
throw new import_sun_sdk_core10.SDKError("SDK_INVALID_ARGUMENT", "V2 remove requires exactly one of liquidity or percentage");
|
|
826
|
+
}
|
|
827
|
+
const liquidity = hasLiquidity ? BigInt(normalizeBaseUnitAmount(input.liquidity, "liquidity")) : (() => {
|
|
828
|
+
const percentage = normalizeLiquidityPercentage(input.percentage, "percentage");
|
|
829
|
+
const numerator = BigInt(percentage.numerator.toString());
|
|
830
|
+
const denominator = BigInt(percentage.denominator.toString());
|
|
831
|
+
const result = BigInt(balance) * numerator / denominator;
|
|
832
|
+
if (result <= 0n) {
|
|
833
|
+
throw new import_sun_sdk_core10.SDKError("SDK_INVALID_ARGUMENT", "percentage resolves to zero liquidity", {
|
|
834
|
+
balance,
|
|
835
|
+
percentage: percentage.toSignificant()
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
return result;
|
|
839
|
+
})();
|
|
840
|
+
if (liquidity > BigInt(balance)) {
|
|
841
|
+
throw new import_sun_sdk_core10.SDKError("SDK_INVALID_ARGUMENT", "liquidity cannot exceed wallet LP balance", {
|
|
842
|
+
liquidity: liquidity.toString(),
|
|
843
|
+
balance
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
return liquidity.toString();
|
|
847
|
+
}
|
|
848
|
+
async function planV2AddLiquidity(context, input) {
|
|
849
|
+
const router = resolveProtocolContract(context, input.contracts, "sunswapV2Router");
|
|
850
|
+
const planId = nextPlanId(context, "liquidity-v2-add");
|
|
851
|
+
const owner = await requireWalletAddress(context, "V2 add liquidity");
|
|
852
|
+
const recipient = await normalizeRecipient(context, input.recipient, "V2 add liquidity", owner);
|
|
853
|
+
const deadline = normalizeDeadline(context, input);
|
|
854
|
+
const slippageInput = input.slippage ?? DEFAULT_SLIPPAGE;
|
|
855
|
+
const slippage = parseSlippage(slippageInput);
|
|
856
|
+
const pair = createPairContext(context, input.tokenA, input.tokenB);
|
|
857
|
+
const amountADesired = normalizeBaseUnitAmount(input.amountADesired, "amountADesired");
|
|
858
|
+
const amountBDesired = normalizeBaseUnitAmount(input.amountBDesired, "amountBDesired");
|
|
859
|
+
const deployedPair = parseContractAddress(await readRequiredContract(
|
|
860
|
+
context,
|
|
861
|
+
(0, import_sun_sdk_sunswap_v22.buildV2FactoryPairRead)({
|
|
862
|
+
network: context.network,
|
|
863
|
+
tokenA: pair.tokenAContract,
|
|
864
|
+
tokenB: pair.tokenBContract
|
|
865
|
+
}),
|
|
866
|
+
"V2 add liquidity pair lookup"
|
|
867
|
+
), "V2 factory pair");
|
|
868
|
+
if (deployedPair !== import_sun_sdk_core10.TRON_ZERO_ADDRESS && deployedPair !== pair.pair) {
|
|
869
|
+
throw new import_sun_sdk_core10.SDKError("SDK_CONTRACT_ERROR", "V2 factory pair does not match the configured CREATE2 pair", {
|
|
870
|
+
factoryPair: deployedPair,
|
|
871
|
+
computedPair: pair.pair
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
const reserves = deployedPair === import_sun_sdk_core10.TRON_ZERO_ADDRESS ? { reserveA: "0", reserveB: "0" } : parsePairReserves(await readRequiredContract(
|
|
875
|
+
context,
|
|
876
|
+
(0, import_sun_sdk_sunswap_v22.buildV2PairReservesRead)(pair.pair),
|
|
877
|
+
"V2 add liquidity reserves"
|
|
878
|
+
), pair.tokenASortsBefore);
|
|
879
|
+
const amounts = resolveAddAmounts({ amountADesired, amountBDesired, reserves });
|
|
880
|
+
validateAddAmounts(pair, reserves, amounts);
|
|
881
|
+
const slippageMinimums = (0, import_sun_sdk_sunswap_v22.getV2AddLiquidityMinimums)(amounts, slippage);
|
|
882
|
+
const minimums = {
|
|
883
|
+
amountAMin: input.amountAMin === void 0 ? slippageMinimums.amountAMin : normalizeBaseUnitAmount(input.amountAMin, "amountAMin", { allowZero: true }),
|
|
884
|
+
amountBMin: input.amountBMin === void 0 ? slippageMinimums.amountBMin : normalizeBaseUnitAmount(input.amountBMin, "amountBMin", { allowZero: true })
|
|
885
|
+
};
|
|
886
|
+
const approvals = [
|
|
887
|
+
...await planTokenApproval({ context, owner, token: pair.tokenA, amount: amounts.amountA, mode: input.approval, router }),
|
|
888
|
+
...await planTokenApproval({ context, owner, token: pair.tokenB, amount: amounts.amountB, mode: input.approval, router })
|
|
889
|
+
];
|
|
890
|
+
const transaction = buildAddAction({
|
|
891
|
+
context,
|
|
892
|
+
planId,
|
|
893
|
+
pair,
|
|
894
|
+
amountA: amounts.amountA,
|
|
895
|
+
amountB: amounts.amountB,
|
|
896
|
+
amountAMin: minimums.amountAMin,
|
|
897
|
+
amountBMin: minimums.amountBMin,
|
|
898
|
+
recipient,
|
|
899
|
+
deadline,
|
|
900
|
+
router,
|
|
901
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {}
|
|
902
|
+
});
|
|
903
|
+
return {
|
|
904
|
+
id: planId,
|
|
905
|
+
kind: "liquidity",
|
|
906
|
+
network: context.network,
|
|
907
|
+
owner,
|
|
908
|
+
summary: {
|
|
909
|
+
title: "Add SunSwap V2 liquidity",
|
|
910
|
+
lines: [`Pair ${pair.pair}`, `Recipient ${recipient}`]
|
|
911
|
+
},
|
|
912
|
+
approvals,
|
|
913
|
+
transactions: [transaction],
|
|
914
|
+
metadata: {
|
|
915
|
+
protocol: "sunswap-v2",
|
|
916
|
+
operation: "add-liquidity",
|
|
917
|
+
pair: pair.pair,
|
|
918
|
+
tokenA: input.tokenA,
|
|
919
|
+
tokenB: input.tokenB,
|
|
920
|
+
amountADesired,
|
|
921
|
+
amountBDesired,
|
|
922
|
+
amountA: amounts.amountA,
|
|
923
|
+
amountB: amounts.amountB,
|
|
924
|
+
amountAMin: minimums.amountAMin,
|
|
925
|
+
amountBMin: minimums.amountBMin,
|
|
926
|
+
owner,
|
|
927
|
+
recipient,
|
|
928
|
+
deadline,
|
|
929
|
+
slippageBips: slippageToBips(slippageInput),
|
|
930
|
+
approvalMode: input.approval ?? "auto"
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
async function planV2RemoveLiquidity(context, input) {
|
|
935
|
+
const router = resolveProtocolContract(context, input.contracts, "sunswapV2Router");
|
|
936
|
+
const planId = nextPlanId(context, "liquidity-v2-remove");
|
|
937
|
+
const owner = await requireWalletAddress(context, "V2 remove liquidity");
|
|
938
|
+
const recipient = await normalizeRecipient(context, input.recipient, "V2 remove liquidity", owner);
|
|
939
|
+
const deadline = normalizeDeadline(context, input);
|
|
940
|
+
const slippageInput = input.slippage ?? DEFAULT_SLIPPAGE;
|
|
941
|
+
const slippage = parseSlippage(slippageInput);
|
|
942
|
+
const approvalMode = input.approval ?? "auto";
|
|
943
|
+
if (approvalMode === "permit2") {
|
|
944
|
+
throw new import_sun_sdk_core10.SDKError("SDK_WALLET_CAPABILITY_ERROR", "Permit2 cannot authorize a SunSwap V2 LP token", {
|
|
945
|
+
approvalMode
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
const pair = createPairContext(context, input.tokenA, input.tokenB);
|
|
949
|
+
const [reservesResult, totalSupplyResult, balanceResult, feeToResult, kLastResult] = await readMulticallRequired(
|
|
950
|
+
context,
|
|
951
|
+
[
|
|
952
|
+
(0, import_sun_sdk_sunswap_v22.buildV2PairReservesRead)(pair.pair),
|
|
953
|
+
(0, import_sun_sdk_sunswap_v22.buildV2PairTotalSupplyRead)(pair.pair),
|
|
954
|
+
(0, import_sun_sdk_sunswap_v22.buildV2PairBalanceRead)(pair.pair, owner),
|
|
955
|
+
(0, import_sun_sdk_sunswap_v22.buildV2FactoryFeeToRead)({ network: context.network }),
|
|
956
|
+
(0, import_sun_sdk_sunswap_v22.buildV2PairKLastRead)(pair.pair)
|
|
957
|
+
],
|
|
958
|
+
"V2 remove liquidity state"
|
|
959
|
+
);
|
|
960
|
+
const reserves = parsePairReserves(reservesResult, pair.tokenASortsBefore);
|
|
961
|
+
const totalSupply = parseContractInteger(totalSupplyResult, "totalSupply");
|
|
962
|
+
const balance = parseContractInteger(balanceResult, "LP balance");
|
|
963
|
+
const feeTo = parseContractAddress(feeToResult, "V2 factory feeTo");
|
|
964
|
+
const kLast = parseContractInteger(kLastResult, "kLast");
|
|
965
|
+
const feeOn = feeTo !== import_sun_sdk_core10.TRON_ZERO_ADDRESS;
|
|
966
|
+
if (BigInt(totalSupply) === 0n) {
|
|
967
|
+
throw new import_sun_sdk_core10.SDKError("SDK_CONTRACT_ERROR", "V2 pair total supply must be positive", {
|
|
968
|
+
pair: pair.pair,
|
|
969
|
+
totalSupply
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
if (BigInt(reserves.reserveA) === 0n || BigInt(reserves.reserveB) === 0n) {
|
|
973
|
+
throw new import_sun_sdk_core10.SDKError("SDK_CONTRACT_ERROR", "V2 pair reserves must be positive for removal", {
|
|
974
|
+
pair: pair.pair,
|
|
975
|
+
reserveA: reserves.reserveA,
|
|
976
|
+
reserveB: reserves.reserveB
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
const liquidity = resolveRemoveLiquidity(input, balance);
|
|
980
|
+
if (BigInt(liquidity) > BigInt(totalSupply)) {
|
|
981
|
+
throw new import_sun_sdk_core10.SDKError("SDK_CONTRACT_ERROR", "V2 LP balance exceeds pair total supply", {
|
|
982
|
+
liquidity,
|
|
983
|
+
totalSupply
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
const pairEntity = createPairEntity(pair, reserves);
|
|
987
|
+
const totalSupplyAmount = import_sun_sdk_core10.CurrencyAmount.fromRawAmount(pairEntity.liquidityToken, totalSupply);
|
|
988
|
+
const liquidityAmount = import_sun_sdk_core10.CurrencyAmount.fromRawAmount(pairEntity.liquidityToken, liquidity);
|
|
989
|
+
const amountA = pairEntity.getLiquidityValue(
|
|
990
|
+
pair.tokenA.token,
|
|
991
|
+
totalSupplyAmount,
|
|
992
|
+
liquidityAmount,
|
|
993
|
+
feeOn,
|
|
994
|
+
kLast
|
|
995
|
+
).quotient.toString();
|
|
996
|
+
const amountB = pairEntity.getLiquidityValue(
|
|
997
|
+
pair.tokenB.token,
|
|
998
|
+
totalSupplyAmount,
|
|
999
|
+
liquidityAmount,
|
|
1000
|
+
feeOn,
|
|
1001
|
+
kLast
|
|
1002
|
+
).quotient.toString();
|
|
1003
|
+
if (BigInt(amountA) === 0n || BigInt(amountB) === 0n) {
|
|
1004
|
+
throw new import_sun_sdk_core10.SDKError("SDK_INVALID_ARGUMENT", "V2 remove liquidity resolves to a zero token amount", {
|
|
1005
|
+
liquidity,
|
|
1006
|
+
amountA,
|
|
1007
|
+
amountB
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
const minimums = (0, import_sun_sdk_sunswap_v22.getV2RemoveLiquidityMinimums)({ amountA, amountB }, slippage);
|
|
1011
|
+
const approvals = [];
|
|
1012
|
+
if (approvalMode !== "none") {
|
|
1013
|
+
const allowance = parseContractInteger(
|
|
1014
|
+
await readRequiredContract(
|
|
1015
|
+
context,
|
|
1016
|
+
(0, import_sun_sdk_sunswap_v22.buildV2PairAllowanceRead)(pair.pair, owner, router),
|
|
1017
|
+
"V2 LP allowance"
|
|
1018
|
+
),
|
|
1019
|
+
"LP allowance"
|
|
1020
|
+
);
|
|
1021
|
+
if (BigInt(allowance) < BigInt(liquidity)) {
|
|
1022
|
+
approvals.push({
|
|
1023
|
+
...(0, import_sun_sdk_sunswap_v22.buildV2LpApproveAction)({
|
|
1024
|
+
pair: pair.pair,
|
|
1025
|
+
spender: router,
|
|
1026
|
+
amount: liquidity,
|
|
1027
|
+
id: `${planId}-approve`,
|
|
1028
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {}
|
|
1029
|
+
}),
|
|
1030
|
+
intent: {
|
|
1031
|
+
kind: "approve",
|
|
1032
|
+
scope: liquidityIntentScope(SUNSWAP_V2_LIQUIDITY_PROTOCOL_ID, "remove-liquidity"),
|
|
1033
|
+
tokenSymbol: "LP"
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
const transaction = buildRemoveAction({
|
|
1039
|
+
context,
|
|
1040
|
+
planId,
|
|
1041
|
+
pair,
|
|
1042
|
+
liquidity,
|
|
1043
|
+
amountAMin: minimums.amountAMin,
|
|
1044
|
+
amountBMin: minimums.amountBMin,
|
|
1045
|
+
recipient,
|
|
1046
|
+
deadline,
|
|
1047
|
+
router,
|
|
1048
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {}
|
|
1049
|
+
});
|
|
1050
|
+
return {
|
|
1051
|
+
id: planId,
|
|
1052
|
+
kind: "liquidity",
|
|
1053
|
+
network: context.network,
|
|
1054
|
+
owner,
|
|
1055
|
+
summary: {
|
|
1056
|
+
title: "Remove SunSwap V2 liquidity",
|
|
1057
|
+
lines: [`Pair ${pair.pair}`, `LP liquidity ${liquidity}`, `Recipient ${recipient}`]
|
|
1058
|
+
},
|
|
1059
|
+
approvals,
|
|
1060
|
+
transactions: [transaction],
|
|
1061
|
+
metadata: {
|
|
1062
|
+
protocol: "sunswap-v2",
|
|
1063
|
+
operation: "remove-liquidity",
|
|
1064
|
+
pair: pair.pair,
|
|
1065
|
+
tokenA: input.tokenA,
|
|
1066
|
+
tokenB: input.tokenB,
|
|
1067
|
+
liquidity,
|
|
1068
|
+
amountA,
|
|
1069
|
+
amountB,
|
|
1070
|
+
amountAMin: minimums.amountAMin,
|
|
1071
|
+
amountBMin: minimums.amountBMin,
|
|
1072
|
+
owner,
|
|
1073
|
+
recipient,
|
|
1074
|
+
deadline,
|
|
1075
|
+
slippageBips: slippageToBips(slippageInput),
|
|
1076
|
+
approvalMode,
|
|
1077
|
+
feeOn,
|
|
1078
|
+
feeTo,
|
|
1079
|
+
kLast
|
|
1080
|
+
}
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
var V2LiquidityClient = class {
|
|
1084
|
+
constructor(context) {
|
|
1085
|
+
this.context = context;
|
|
1086
|
+
}
|
|
1087
|
+
context;
|
|
1088
|
+
add = {
|
|
1089
|
+
plan: (input) => planV2AddLiquidity(this.context, input),
|
|
1090
|
+
execute: async (input) => executeTransactionPlan(await planV2AddLiquidity(this.context, input), createContextExecutor(this.context))
|
|
1091
|
+
};
|
|
1092
|
+
remove = {
|
|
1093
|
+
plan: (input) => planV2RemoveLiquidity(this.context, input),
|
|
1094
|
+
execute: async (input) => executeTransactionPlan(await planV2RemoveLiquidity(this.context, input), createContextExecutor(this.context))
|
|
1095
|
+
};
|
|
1096
|
+
};
|
|
1097
|
+
function createV2LiquidityClient(context) {
|
|
1098
|
+
return new V2LiquidityClient(context);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// src/liquidity/v3.ts
|
|
1102
|
+
var import_sun_sdk_core12 = require("@sun-protocol/sun-sdk-core");
|
|
1103
|
+
var import_sun_sdk_sunswap_v3 = require("@sun-protocol/sun-sdk-sunswap-v3");
|
|
1104
|
+
|
|
1105
|
+
// src/internal/method-parameters.ts
|
|
1106
|
+
var import_sun_sdk_core11 = require("@sun-protocol/sun-sdk-core");
|
|
1107
|
+
function methodParametersToAction(input) {
|
|
1108
|
+
return (0, import_sun_sdk_core11.createContractCallAction)({
|
|
1109
|
+
id: input.id,
|
|
1110
|
+
target: input.target,
|
|
1111
|
+
functionSelector: input.methodParameters.function_selector,
|
|
1112
|
+
parameters: input.methodParameters.parameter,
|
|
1113
|
+
callValue: BigInt(input.methodParameters.value),
|
|
1114
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {},
|
|
1115
|
+
...input.waitForReceipt !== void 0 ? { waitForReceipt: input.waitForReceipt } : {},
|
|
1116
|
+
...input.metadata !== void 0 ? { metadata: input.metadata } : {},
|
|
1117
|
+
...input.intent !== void 0 ? { intent: input.intent } : {}
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// src/liquidity/v3.ts
|
|
1122
|
+
function integerString(value, label) {
|
|
1123
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "bigint") {
|
|
1124
|
+
return BigInt(value.toString()).toString();
|
|
1125
|
+
}
|
|
1126
|
+
throw new import_sun_sdk_core12.SDKError("SDK_CONTRACT_ERROR", `${label} must be an integer-like contract result`, { value });
|
|
1127
|
+
}
|
|
1128
|
+
function integerNumber(value, label) {
|
|
1129
|
+
const parsed = Number(integerString(value, label));
|
|
1130
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
1131
|
+
throw new import_sun_sdk_core12.SDKError("SDK_CONTRACT_ERROR", `${label} must be a safe integer`, { value });
|
|
1132
|
+
}
|
|
1133
|
+
return parsed;
|
|
1134
|
+
}
|
|
1135
|
+
function positionToken(value, label) {
|
|
1136
|
+
if (typeof value !== "string") {
|
|
1137
|
+
throw new import_sun_sdk_core12.SDKError("SDK_CONTRACT_ERROR", `${label} must be an address string`, { value });
|
|
1138
|
+
}
|
|
1139
|
+
if (value.startsWith("0x")) return evmHexToTronAddress(value, label);
|
|
1140
|
+
if (/^41[0-9a-fA-F]{40}$/.test(value)) {
|
|
1141
|
+
return evmHexToTronAddress(`0x${value.slice(2)}`, label);
|
|
1142
|
+
}
|
|
1143
|
+
return (0, import_sun_sdk_core12.assertTronAddress)(value, label);
|
|
1144
|
+
}
|
|
1145
|
+
function parseV3Slot0(result) {
|
|
1146
|
+
if (Array.isArray(result)) {
|
|
1147
|
+
return {
|
|
1148
|
+
sqrtRatioX96: integerString(result[0], "sqrtRatioX96"),
|
|
1149
|
+
tickCurrent: integerNumber(result[1], "tickCurrent")
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
if (typeof result === "object" && result !== null) {
|
|
1153
|
+
const slot0 = result;
|
|
1154
|
+
return {
|
|
1155
|
+
sqrtRatioX96: integerString(slot0.sqrtPriceX96 ?? slot0.sqrtRatioX96, "sqrtRatioX96"),
|
|
1156
|
+
tickCurrent: integerNumber(slot0.tick, "tickCurrent")
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
throw new import_sun_sdk_core12.SDKError("SDK_CONTRACT_ERROR", "V3 slot0 result is malformed", { result });
|
|
1160
|
+
}
|
|
1161
|
+
async function resolveV3PoolState(context, input) {
|
|
1162
|
+
const tokenA = normalizeProtocolToken(context, input.tokenA, "tokenA");
|
|
1163
|
+
const tokenB = normalizeProtocolToken(context, input.tokenB, "tokenB");
|
|
1164
|
+
const poolAddress = (0, import_sun_sdk_sunswap_v3.computeV3PoolAddress)({
|
|
1165
|
+
network: context.network,
|
|
1166
|
+
tokenA: tokenA.contractAddress,
|
|
1167
|
+
tokenB: tokenB.contractAddress,
|
|
1168
|
+
fee: input.fee,
|
|
1169
|
+
factory: resolveProtocolContract(context, input.contracts, "sunswapV3Factory")
|
|
1170
|
+
});
|
|
1171
|
+
const [slot0Result, liquidityResult, feeResult, tickSpacingResult] = await readMulticallRequired(
|
|
1172
|
+
context,
|
|
1173
|
+
(0, import_sun_sdk_sunswap_v3.buildV3PoolStateReads)({ pool: poolAddress }),
|
|
1174
|
+
"V3 pool state read"
|
|
1175
|
+
);
|
|
1176
|
+
const fee = integerNumber(feeResult, "fee");
|
|
1177
|
+
if (fee !== input.fee) {
|
|
1178
|
+
throw new import_sun_sdk_core12.SDKError("SDK_CONTRACT_ERROR", "V3 pool fee does not match requested fee", {
|
|
1179
|
+
pool: poolAddress,
|
|
1180
|
+
requestedFee: input.fee,
|
|
1181
|
+
poolFee: fee
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
const slot0 = parseV3Slot0(slot0Result);
|
|
1185
|
+
return {
|
|
1186
|
+
sqrtRatioX96: slot0.sqrtRatioX96,
|
|
1187
|
+
liquidity: integerString(liquidityResult, "liquidity"),
|
|
1188
|
+
tickCurrent: slot0.tickCurrent,
|
|
1189
|
+
tickSpacing: integerNumber(tickSpacingResult, "tickSpacing")
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
async function resolveV3PositionContext(context, tokenId, contracts) {
|
|
1193
|
+
const result = await readRequiredContract(
|
|
1194
|
+
context,
|
|
1195
|
+
(0, import_sun_sdk_sunswap_v3.buildV3PositionRead)({
|
|
1196
|
+
network: context.network,
|
|
1197
|
+
tokenId,
|
|
1198
|
+
positionManager: resolveProtocolContract(context, contracts, "sunswapV3PositionManager")
|
|
1199
|
+
}),
|
|
1200
|
+
"V3 position context read"
|
|
1201
|
+
);
|
|
1202
|
+
let token0;
|
|
1203
|
+
let token1;
|
|
1204
|
+
let fee;
|
|
1205
|
+
let tickLower;
|
|
1206
|
+
let tickUpper;
|
|
1207
|
+
if (Array.isArray(result)) {
|
|
1208
|
+
token0 = result[2];
|
|
1209
|
+
token1 = result[3];
|
|
1210
|
+
fee = result[4];
|
|
1211
|
+
tickLower = result[5];
|
|
1212
|
+
tickUpper = result[6];
|
|
1213
|
+
} else if (typeof result === "object" && result !== null) {
|
|
1214
|
+
const position = result;
|
|
1215
|
+
token0 = position.token0;
|
|
1216
|
+
token1 = position.token1;
|
|
1217
|
+
fee = position.fee;
|
|
1218
|
+
tickLower = position.tickLower;
|
|
1219
|
+
tickUpper = position.tickUpper;
|
|
1220
|
+
} else {
|
|
1221
|
+
throw new import_sun_sdk_core12.SDKError("SDK_CONTRACT_ERROR", "V3 position result is malformed", { result });
|
|
1222
|
+
}
|
|
1223
|
+
return {
|
|
1224
|
+
tokenA: positionToken(token0, "token0"),
|
|
1225
|
+
tokenB: positionToken(token1, "token1"),
|
|
1226
|
+
fee: integerNumber(fee, "fee"),
|
|
1227
|
+
tickLower: integerNumber(tickLower, "tickLower"),
|
|
1228
|
+
tickUpper: integerNumber(tickUpper, "tickUpper")
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
function normalizeSingleSideAmounts(input) {
|
|
1232
|
+
if (input.amount0 === void 0 && input.amount1 === void 0) {
|
|
1233
|
+
throw new import_sun_sdk_core12.SDKError("SDK_INVALID_ARGUMENT", "V3 liquidity requires at least one of amount0 or amount1");
|
|
1234
|
+
}
|
|
1235
|
+
return {
|
|
1236
|
+
amount0: input.amount0 ?? "0",
|
|
1237
|
+
amount1: input.amount1 ?? "0"
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
function assertV3PositionContextMatches(context, tokenId, input, position) {
|
|
1241
|
+
const mismatches = {};
|
|
1242
|
+
const suppliedTokenA = input.tokenA === void 0 ? void 0 : normalizeV3ContextToken(context, input.tokenA, "tokenA");
|
|
1243
|
+
const suppliedTokenB = input.tokenB === void 0 ? void 0 : normalizeV3ContextToken(context, input.tokenB, "tokenB");
|
|
1244
|
+
const positionTokens = [position.tokenA, position.tokenB];
|
|
1245
|
+
if (suppliedTokenA !== void 0 && suppliedTokenB !== void 0) {
|
|
1246
|
+
const pairMatches = suppliedTokenA === position.tokenA && suppliedTokenB === position.tokenB || suppliedTokenA === position.tokenB && suppliedTokenB === position.tokenA;
|
|
1247
|
+
if (!pairMatches) {
|
|
1248
|
+
mismatches.tokens = { supplied: [suppliedTokenA, suppliedTokenB], position: positionTokens };
|
|
1249
|
+
}
|
|
1250
|
+
} else {
|
|
1251
|
+
if (suppliedTokenA !== void 0 && !positionTokens.includes(suppliedTokenA)) {
|
|
1252
|
+
mismatches.tokenA = { supplied: suppliedTokenA, position: positionTokens };
|
|
1253
|
+
}
|
|
1254
|
+
if (suppliedTokenB !== void 0 && !positionTokens.includes(suppliedTokenB)) {
|
|
1255
|
+
mismatches.tokenB = { supplied: suppliedTokenB, position: positionTokens };
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
if (input.fee !== void 0 && input.fee !== position.fee) {
|
|
1259
|
+
mismatches.fee = { supplied: input.fee, position: position.fee };
|
|
1260
|
+
}
|
|
1261
|
+
if (input.tickLower !== void 0 && input.tickLower !== position.tickLower) {
|
|
1262
|
+
mismatches.tickLower = { supplied: input.tickLower, position: position.tickLower };
|
|
1263
|
+
}
|
|
1264
|
+
if (input.tickUpper !== void 0 && input.tickUpper !== position.tickUpper) {
|
|
1265
|
+
mismatches.tickUpper = { supplied: input.tickUpper, position: position.tickUpper };
|
|
1266
|
+
}
|
|
1267
|
+
if (Object.keys(mismatches).length > 0) {
|
|
1268
|
+
throw new import_sun_sdk_core12.SDKError("SDK_INVALID_ARGUMENT", "V3 increase context does not match the tokenId position", {
|
|
1269
|
+
tokenId,
|
|
1270
|
+
mismatches
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
function normalizeV3ContextToken(context, token, label) {
|
|
1275
|
+
const normalized = normalizeProtocolToken(context, token, label);
|
|
1276
|
+
return normalized.kind === "native" ? normalized.wrappedContractAddress : normalized.contractAddress;
|
|
1277
|
+
}
|
|
1278
|
+
function resolveTickRange(input) {
|
|
1279
|
+
if (input.tickLower !== void 0 && input.tickUpper !== void 0) {
|
|
1280
|
+
return { tickLower: input.tickLower, tickUpper: input.tickUpper, derived: false };
|
|
1281
|
+
}
|
|
1282
|
+
if (input.tickLower !== void 0 || input.tickUpper !== void 0) {
|
|
1283
|
+
throw new import_sun_sdk_core12.SDKError("SDK_INVALID_ARGUMENT", "tickLower and tickUpper must be provided together");
|
|
1284
|
+
}
|
|
1285
|
+
if (!Number.isInteger(input.tickRangeWidth) || (input.tickRangeWidth ?? 0) <= 0) {
|
|
1286
|
+
throw new import_sun_sdk_core12.SDKError("SDK_INVALID_ARGUMENT", "tickRangeWidth must be a positive integer when ticks are omitted", {
|
|
1287
|
+
tickRangeWidth: input.tickRangeWidth
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
const tickSpacing = input.pool.tickSpacing;
|
|
1291
|
+
if (tickSpacing === void 0) {
|
|
1292
|
+
throw new import_sun_sdk_core12.SDKError("SDK_INVALID_ARGUMENT", "pool tickSpacing is required to derive a tick range");
|
|
1293
|
+
}
|
|
1294
|
+
const width = input.tickRangeWidth;
|
|
1295
|
+
const lowerTarget = Math.max(import_sun_sdk_sunswap_v3.TickMath.MIN_TICK, input.pool.tickCurrent - width * tickSpacing);
|
|
1296
|
+
const upperTarget = Math.min(import_sun_sdk_sunswap_v3.TickMath.MAX_TICK, input.pool.tickCurrent + width * tickSpacing);
|
|
1297
|
+
let tickLower = (0, import_sun_sdk_sunswap_v3.nearestUsableTick)(lowerTarget, tickSpacing);
|
|
1298
|
+
let tickUpper = (0, import_sun_sdk_sunswap_v3.nearestUsableTick)(upperTarget, tickSpacing);
|
|
1299
|
+
if (tickUpper - tickLower < tickSpacing) {
|
|
1300
|
+
const maxUsableTick = (0, import_sun_sdk_sunswap_v3.nearestUsableTick)(import_sun_sdk_sunswap_v3.TickMath.MAX_TICK, tickSpacing);
|
|
1301
|
+
if (tickUpper < maxUsableTick) tickUpper += tickSpacing;
|
|
1302
|
+
else tickLower -= tickSpacing;
|
|
1303
|
+
}
|
|
1304
|
+
return {
|
|
1305
|
+
tickLower,
|
|
1306
|
+
tickUpper,
|
|
1307
|
+
derived: true
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
function createPool(input) {
|
|
1311
|
+
const tokenA = normalizeProtocolToken(input.context, input.tokenA, "tokenA");
|
|
1312
|
+
const tokenB = normalizeProtocolToken(input.context, input.tokenB, "tokenB");
|
|
1313
|
+
return {
|
|
1314
|
+
tokenA,
|
|
1315
|
+
tokenB,
|
|
1316
|
+
pool: new import_sun_sdk_sunswap_v3.Pool({
|
|
1317
|
+
tokenA: tokenA.token,
|
|
1318
|
+
tokenB: tokenB.token,
|
|
1319
|
+
fee: input.fee,
|
|
1320
|
+
sqrtRatioX96: normalizeBaseUnitAmount(input.pool.sqrtRatioX96, "sqrtRatioX96"),
|
|
1321
|
+
liquidity: normalizeBaseUnitAmount(input.pool.liquidity, "pool liquidity", { allowZero: true }),
|
|
1322
|
+
tickCurrent: input.pool.tickCurrent,
|
|
1323
|
+
...input.pool.tickSpacing !== void 0 ? { tickSpacing: input.pool.tickSpacing } : {}
|
|
1324
|
+
})
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
function createPosition(input) {
|
|
1328
|
+
return import_sun_sdk_sunswap_v3.Position.fromAmounts({
|
|
1329
|
+
pool: input.pool,
|
|
1330
|
+
tickLower: input.tickLower,
|
|
1331
|
+
tickUpper: input.tickUpper,
|
|
1332
|
+
amount0: input.amount0,
|
|
1333
|
+
amount1: input.amount1,
|
|
1334
|
+
useFullPrecision: input.useFullPrecision ?? true
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
function isNativePoolToken(poolToken, tokenA, tokenB) {
|
|
1338
|
+
return tokenA.kind === "native" && poolToken.equals(tokenA.token) || tokenB.kind === "native" && poolToken.equals(tokenB.token);
|
|
1339
|
+
}
|
|
1340
|
+
async function planTokenApproval2(input) {
|
|
1341
|
+
if (isNativePoolToken(input.token, input.tokenA, input.tokenB)) return [];
|
|
1342
|
+
if (BigInt(input.amount) === 0n) return [];
|
|
1343
|
+
const approval = await planApproval(
|
|
1344
|
+
input.context,
|
|
1345
|
+
{
|
|
1346
|
+
asset: { type: "token", token: evmHexToTronAddress(input.token.address) },
|
|
1347
|
+
owner: input.owner,
|
|
1348
|
+
spender: input.spender,
|
|
1349
|
+
amount: input.amount
|
|
1350
|
+
},
|
|
1351
|
+
{ mode: input.approval ?? "auto" }
|
|
1352
|
+
);
|
|
1353
|
+
return attachApprovalIntent(
|
|
1354
|
+
approval.actions,
|
|
1355
|
+
liquidityIntentScope(SUNSWAP_V3_LIQUIDITY_PROTOCOL_ID, input.operation),
|
|
1356
|
+
input.token.symbol ?? input.token.address
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
function amountForToken(position, token) {
|
|
1360
|
+
if (position.pool.token0.equals(token)) return position.mintAmounts.amount0.toString();
|
|
1361
|
+
return position.mintAmounts.amount1.toString();
|
|
1362
|
+
}
|
|
1363
|
+
function applyExplicitAddMinimums(methodParameters, amount0Min, amount1Min) {
|
|
1364
|
+
const parameter = methodParameters.parameter[0];
|
|
1365
|
+
const mintSelector = import_sun_sdk_sunswap_v3.NonfungiblePositionManager.INTERFACE.getSighash("mint");
|
|
1366
|
+
const calldatas = parameter.value.map((calldata) => {
|
|
1367
|
+
if (!calldata.startsWith(mintSelector)) return calldata;
|
|
1368
|
+
const [mint] = import_sun_sdk_sunswap_v3.NonfungiblePositionManager.INTERFACE.decodeFunctionData("mint", calldata);
|
|
1369
|
+
return import_sun_sdk_sunswap_v3.NonfungiblePositionManager.INTERFACE.encodeFunctionData("mint", [{
|
|
1370
|
+
token0: mint.token0,
|
|
1371
|
+
token1: mint.token1,
|
|
1372
|
+
fee: mint.fee,
|
|
1373
|
+
tickLower: mint.tickLower,
|
|
1374
|
+
tickUpper: mint.tickUpper,
|
|
1375
|
+
amount0Desired: mint.amount0Desired,
|
|
1376
|
+
amount1Desired: mint.amount1Desired,
|
|
1377
|
+
amount0Min,
|
|
1378
|
+
amount1Min,
|
|
1379
|
+
recipient: mint.recipient,
|
|
1380
|
+
deadline: mint.deadline
|
|
1381
|
+
}]);
|
|
1382
|
+
});
|
|
1383
|
+
return {
|
|
1384
|
+
...methodParameters,
|
|
1385
|
+
calldata: import_sun_sdk_sunswap_v3.Multicall.encodeMulticall(calldatas),
|
|
1386
|
+
parameter: [{ type: "bytes[]", value: calldatas }]
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
var V3LiquidityClient = class {
|
|
1390
|
+
constructor(context) {
|
|
1391
|
+
this.context = context;
|
|
1392
|
+
}
|
|
1393
|
+
context;
|
|
1394
|
+
add = {
|
|
1395
|
+
plan: async (input) => this.planAdd(input),
|
|
1396
|
+
execute: async (input) => executeTransactionPlan(await this.planAdd(input), createContextExecutor(this.context))
|
|
1397
|
+
};
|
|
1398
|
+
increase = {
|
|
1399
|
+
plan: async (input) => this.planIncrease(input),
|
|
1400
|
+
execute: async (input) => executeTransactionPlan(await this.planIncrease(input), createContextExecutor(this.context))
|
|
1401
|
+
};
|
|
1402
|
+
async planAdd(input) {
|
|
1403
|
+
const positionManager = resolveProtocolContract(this.context, input.contracts, "sunswapV3PositionManager");
|
|
1404
|
+
const planId = nextPlanId(this.context, "liquidity-v3-add");
|
|
1405
|
+
const owner = await requireWalletAddress(this.context, "V3 add liquidity");
|
|
1406
|
+
const recipient = await normalizeRecipient(this.context, input.recipient, "V3 add liquidity", owner);
|
|
1407
|
+
const deadline = normalizeDeadline(this.context, input);
|
|
1408
|
+
const amounts = normalizeSingleSideAmounts(input);
|
|
1409
|
+
const amount0 = normalizeBaseUnitAmount(amounts.amount0, "amount0", { allowZero: true });
|
|
1410
|
+
const amount1 = normalizeBaseUnitAmount(amounts.amount1, "amount1", { allowZero: true });
|
|
1411
|
+
const poolDerived = input.pool === void 0;
|
|
1412
|
+
const poolState = input.pool ?? await resolveV3PoolState(this.context, input);
|
|
1413
|
+
const tickRange = resolveTickRange({
|
|
1414
|
+
...input.tickLower !== void 0 ? { tickLower: input.tickLower } : {},
|
|
1415
|
+
...input.tickUpper !== void 0 ? { tickUpper: input.tickUpper } : {},
|
|
1416
|
+
...input.tickRangeWidth !== void 0 ? { tickRangeWidth: input.tickRangeWidth } : {},
|
|
1417
|
+
pool: poolState
|
|
1418
|
+
});
|
|
1419
|
+
const { pool, tokenA, tokenB } = createPool({
|
|
1420
|
+
context: this.context,
|
|
1421
|
+
tokenA: input.tokenA,
|
|
1422
|
+
tokenB: input.tokenB,
|
|
1423
|
+
fee: input.fee,
|
|
1424
|
+
pool: poolState
|
|
1425
|
+
});
|
|
1426
|
+
const position = createPosition({
|
|
1427
|
+
pool,
|
|
1428
|
+
tickLower: tickRange.tickLower,
|
|
1429
|
+
tickUpper: tickRange.tickUpper,
|
|
1430
|
+
amount0,
|
|
1431
|
+
amount1,
|
|
1432
|
+
...input.useFullPrecision !== void 0 ? { useFullPrecision: input.useFullPrecision } : {}
|
|
1433
|
+
});
|
|
1434
|
+
const slippageTolerance = parseSlippage(input.slippage ?? "0.5%");
|
|
1435
|
+
const slippageMinimums = position.mintAmountsWithSlippage(slippageTolerance);
|
|
1436
|
+
const amount0Min = input.amount0Min === void 0 ? slippageMinimums.amount0.toString() : normalizeBaseUnitAmount(input.amount0Min, "amount0Min", { allowZero: true });
|
|
1437
|
+
const amount1Min = input.amount1Min === void 0 ? slippageMinimums.amount1.toString() : normalizeBaseUnitAmount(input.amount1Min, "amount1Min", { allowZero: true });
|
|
1438
|
+
const generatedMethodParameters = import_sun_sdk_sunswap_v3.NonfungiblePositionManager.addCallParameters(position, {
|
|
1439
|
+
recipient: tronAddressToEvmAddress(recipient, "recipient"),
|
|
1440
|
+
...input.createPool !== void 0 ? { createPool: input.createPool } : {},
|
|
1441
|
+
slippageTolerance,
|
|
1442
|
+
deadline,
|
|
1443
|
+
...tokenA.kind === "native" || tokenB.kind === "native" ? { useNative: this.context.chain.nativeCurrency } : {}
|
|
1444
|
+
});
|
|
1445
|
+
const methodParameters = input.amount0Min === void 0 && input.amount1Min === void 0 ? generatedMethodParameters : applyExplicitAddMinimums(generatedMethodParameters, amount0Min, amount1Min);
|
|
1446
|
+
const action = methodParametersToAction({
|
|
1447
|
+
id: `${planId}-tx`,
|
|
1448
|
+
target: positionManager,
|
|
1449
|
+
methodParameters,
|
|
1450
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {},
|
|
1451
|
+
waitForReceipt: "included",
|
|
1452
|
+
intent: {
|
|
1453
|
+
kind: "add-liquidity",
|
|
1454
|
+
scope: liquidityIntentScope(SUNSWAP_V3_LIQUIDITY_PROTOCOL_ID, "add-liquidity")
|
|
1455
|
+
}
|
|
1456
|
+
});
|
|
1457
|
+
const approvals = [
|
|
1458
|
+
...await planTokenApproval2({
|
|
1459
|
+
context: this.context,
|
|
1460
|
+
owner,
|
|
1461
|
+
token: pool.token0,
|
|
1462
|
+
amount: amountForToken(position, pool.token0),
|
|
1463
|
+
spender: positionManager,
|
|
1464
|
+
approval: input.approval,
|
|
1465
|
+
tokenA,
|
|
1466
|
+
tokenB,
|
|
1467
|
+
operation: "add-liquidity"
|
|
1468
|
+
}),
|
|
1469
|
+
...await planTokenApproval2({
|
|
1470
|
+
context: this.context,
|
|
1471
|
+
owner,
|
|
1472
|
+
token: pool.token1,
|
|
1473
|
+
amount: amountForToken(position, pool.token1),
|
|
1474
|
+
spender: positionManager,
|
|
1475
|
+
approval: input.approval,
|
|
1476
|
+
tokenA,
|
|
1477
|
+
tokenB,
|
|
1478
|
+
operation: "add-liquidity"
|
|
1479
|
+
})
|
|
1480
|
+
];
|
|
1481
|
+
return {
|
|
1482
|
+
id: planId,
|
|
1483
|
+
kind: "liquidity",
|
|
1484
|
+
network: this.context.network,
|
|
1485
|
+
owner,
|
|
1486
|
+
summary: { title: "Add SunSwap V3 liquidity", lines: [`Recipient ${recipient}`] },
|
|
1487
|
+
approvals,
|
|
1488
|
+
transactions: [action],
|
|
1489
|
+
metadata: {
|
|
1490
|
+
protocol: "sunswap-v3",
|
|
1491
|
+
operation: "add-liquidity",
|
|
1492
|
+
recipient,
|
|
1493
|
+
deadline,
|
|
1494
|
+
slippageBips: slippageToBips(input.slippage ?? "0.5%"),
|
|
1495
|
+
poolDerived,
|
|
1496
|
+
tickRangeDerived: tickRange.derived,
|
|
1497
|
+
tickLower: tickRange.tickLower,
|
|
1498
|
+
tickUpper: tickRange.tickUpper
|
|
1499
|
+
}
|
|
1500
|
+
};
|
|
1501
|
+
}
|
|
1502
|
+
async planIncrease(input) {
|
|
1503
|
+
const positionManager = resolveProtocolContract(this.context, input.contracts, "sunswapV3PositionManager");
|
|
1504
|
+
const planId = nextPlanId(this.context, "liquidity-v3-increase");
|
|
1505
|
+
const owner = await requireWalletAddress(this.context, "V3 increase liquidity");
|
|
1506
|
+
const deadline = normalizeDeadline(this.context, input);
|
|
1507
|
+
const tokenId = normalizeBaseUnitAmount(input.tokenId, "tokenId");
|
|
1508
|
+
const amounts = normalizeSingleSideAmounts(input);
|
|
1509
|
+
const amount0 = normalizeBaseUnitAmount(amounts.amount0, "amount0", { allowZero: true });
|
|
1510
|
+
const amount1 = normalizeBaseUnitAmount(amounts.amount1, "amount1", { allowZero: true });
|
|
1511
|
+
const positionDerived = input.tokenA === void 0 || input.tokenB === void 0 || input.fee === void 0 || input.tickLower === void 0 || input.tickUpper === void 0;
|
|
1512
|
+
const positionContext = await resolveV3PositionContext(this.context, tokenId, input.contracts);
|
|
1513
|
+
assertV3PositionContextMatches(this.context, tokenId, input, positionContext);
|
|
1514
|
+
const tokenAInput = input.tokenA ?? positionContext.tokenA;
|
|
1515
|
+
const tokenBInput = input.tokenB ?? positionContext.tokenB;
|
|
1516
|
+
const fee = input.fee ?? positionContext.fee;
|
|
1517
|
+
const tickLower = input.tickLower ?? positionContext.tickLower;
|
|
1518
|
+
const tickUpper = input.tickUpper ?? positionContext.tickUpper;
|
|
1519
|
+
if (tokenAInput === void 0 || tokenBInput === void 0 || fee === void 0 || tickLower === void 0 || tickUpper === void 0) {
|
|
1520
|
+
throw new import_sun_sdk_core12.SDKError("SDK_CONTRACT_ERROR", "V3 position context is incomplete", { tokenId });
|
|
1521
|
+
}
|
|
1522
|
+
const poolDerived = input.pool === void 0;
|
|
1523
|
+
const poolState = input.pool ?? await resolveV3PoolState(this.context, {
|
|
1524
|
+
tokenA: tokenAInput,
|
|
1525
|
+
tokenB: tokenBInput,
|
|
1526
|
+
fee,
|
|
1527
|
+
contracts: input.contracts
|
|
1528
|
+
});
|
|
1529
|
+
const { pool, tokenA, tokenB } = createPool({
|
|
1530
|
+
context: this.context,
|
|
1531
|
+
tokenA: tokenAInput,
|
|
1532
|
+
tokenB: tokenBInput,
|
|
1533
|
+
fee,
|
|
1534
|
+
pool: poolState
|
|
1535
|
+
});
|
|
1536
|
+
const position = createPosition({
|
|
1537
|
+
pool,
|
|
1538
|
+
tickLower,
|
|
1539
|
+
tickUpper,
|
|
1540
|
+
amount0,
|
|
1541
|
+
amount1,
|
|
1542
|
+
...input.useFullPrecision !== void 0 ? { useFullPrecision: input.useFullPrecision } : {}
|
|
1543
|
+
});
|
|
1544
|
+
const methodParameters = import_sun_sdk_sunswap_v3.NonfungiblePositionManager.addCallParameters(position, {
|
|
1545
|
+
tokenId,
|
|
1546
|
+
slippageTolerance: parseSlippage(input.slippage ?? "0.5%"),
|
|
1547
|
+
deadline,
|
|
1548
|
+
...tokenA.kind === "native" || tokenB.kind === "native" ? { useNative: this.context.chain.nativeCurrency } : {}
|
|
1549
|
+
});
|
|
1550
|
+
const action = methodParametersToAction({
|
|
1551
|
+
id: `${planId}-tx`,
|
|
1552
|
+
target: positionManager,
|
|
1553
|
+
methodParameters,
|
|
1554
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {},
|
|
1555
|
+
waitForReceipt: "included",
|
|
1556
|
+
intent: {
|
|
1557
|
+
kind: "add-liquidity",
|
|
1558
|
+
scope: liquidityIntentScope(SUNSWAP_V3_LIQUIDITY_PROTOCOL_ID, "increase-liquidity"),
|
|
1559
|
+
positionId: tokenId
|
|
1560
|
+
}
|
|
1561
|
+
});
|
|
1562
|
+
const approvals = [
|
|
1563
|
+
...await planTokenApproval2({
|
|
1564
|
+
context: this.context,
|
|
1565
|
+
owner,
|
|
1566
|
+
token: pool.token0,
|
|
1567
|
+
amount: amountForToken(position, pool.token0),
|
|
1568
|
+
spender: positionManager,
|
|
1569
|
+
approval: input.approval,
|
|
1570
|
+
tokenA,
|
|
1571
|
+
tokenB,
|
|
1572
|
+
operation: "increase-liquidity"
|
|
1573
|
+
}),
|
|
1574
|
+
...await planTokenApproval2({
|
|
1575
|
+
context: this.context,
|
|
1576
|
+
owner,
|
|
1577
|
+
token: pool.token1,
|
|
1578
|
+
amount: amountForToken(position, pool.token1),
|
|
1579
|
+
spender: positionManager,
|
|
1580
|
+
approval: input.approval,
|
|
1581
|
+
tokenA,
|
|
1582
|
+
tokenB,
|
|
1583
|
+
operation: "increase-liquidity"
|
|
1584
|
+
})
|
|
1585
|
+
];
|
|
1586
|
+
return {
|
|
1587
|
+
id: planId,
|
|
1588
|
+
kind: "liquidity",
|
|
1589
|
+
network: this.context.network,
|
|
1590
|
+
owner,
|
|
1591
|
+
summary: { title: "Increase SunSwap V3 liquidity", lines: [`Position ${tokenId}`] },
|
|
1592
|
+
approvals,
|
|
1593
|
+
transactions: [action],
|
|
1594
|
+
metadata: {
|
|
1595
|
+
protocol: "sunswap-v3",
|
|
1596
|
+
operation: "increase-liquidity",
|
|
1597
|
+
tokenId,
|
|
1598
|
+
deadline,
|
|
1599
|
+
slippageBips: slippageToBips(input.slippage ?? "0.5%"),
|
|
1600
|
+
positionDerived,
|
|
1601
|
+
poolDerived
|
|
1602
|
+
}
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
};
|
|
1606
|
+
|
|
1607
|
+
// src/liquidity/v4.ts
|
|
1608
|
+
var import_sun_sdk_core15 = require("@sun-protocol/sun-sdk-core");
|
|
1609
|
+
var import_sun_sdk_sunswap_v32 = require("@sun-protocol/sun-sdk-sunswap-v3");
|
|
1610
|
+
var import_sun_sdk_sunswap_v4 = require("@sun-protocol/sun-sdk-sunswap-v4");
|
|
1611
|
+
|
|
1612
|
+
// src/approval/permit2.ts
|
|
1613
|
+
var import_sun_sdk_core14 = require("@sun-protocol/sun-sdk-core");
|
|
1614
|
+
|
|
1615
|
+
// src/internal/permit2-adapter.ts
|
|
1616
|
+
var import_sun_sdk_core13 = require("@sun-protocol/sun-sdk-core");
|
|
1617
|
+
var import_permit2_sdk = require("@sun-protocol/permit2-sdk");
|
|
1618
|
+
var PERMIT2_TYPED_DATA_CHAIN_IDS = {
|
|
1619
|
+
mainnet: 728126428,
|
|
1620
|
+
nile: 3448148188
|
|
1621
|
+
};
|
|
1622
|
+
function buildPermit2HelperRead(input) {
|
|
1623
|
+
return {
|
|
1624
|
+
target: input.helper,
|
|
1625
|
+
functionSelector: "checkPermit2Allowance(address,address,address,address,uint256)",
|
|
1626
|
+
parameters: [
|
|
1627
|
+
{ type: "address", value: input.permit2 },
|
|
1628
|
+
{ type: "address", value: input.token },
|
|
1629
|
+
{ type: "address", value: input.owner },
|
|
1630
|
+
{ type: "address", value: input.spender },
|
|
1631
|
+
{ type: "uint256", value: input.amount }
|
|
1632
|
+
],
|
|
1633
|
+
outputs: [{ type: "bool", name: "approved" }]
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
function normalizePermit2HelperResult(result) {
|
|
1637
|
+
if (typeof result === "boolean") return result;
|
|
1638
|
+
if (Array.isArray(result) && result.length === 1 && typeof result[0] === "boolean") return result[0];
|
|
1639
|
+
throw new import_sun_sdk_core13.SDKError("SDK_CONTRACT_ERROR", "Invalid Permit2Helper approval result", { result });
|
|
1640
|
+
}
|
|
1641
|
+
function buildPermit2AllowanceRead(input) {
|
|
1642
|
+
return {
|
|
1643
|
+
target: input.permit2,
|
|
1644
|
+
functionSelector: "allowance(address,address,address)",
|
|
1645
|
+
parameters: [
|
|
1646
|
+
{ type: "address", value: input.owner },
|
|
1647
|
+
{ type: "address", value: input.token },
|
|
1648
|
+
{ type: "address", value: input.spender }
|
|
1649
|
+
],
|
|
1650
|
+
outputs: [
|
|
1651
|
+
{ type: "uint160", name: "amount" },
|
|
1652
|
+
{ type: "uint48", name: "expiration" },
|
|
1653
|
+
{ type: "uint48", name: "nonce" }
|
|
1654
|
+
]
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1657
|
+
function bigintFrom(value, label) {
|
|
1658
|
+
if (typeof value === "bigint") return value;
|
|
1659
|
+
if (typeof value === "number" && Number.isSafeInteger(value)) return BigInt(value);
|
|
1660
|
+
if (typeof value === "string" && /^[0-9]+$/.test(value)) return BigInt(value);
|
|
1661
|
+
throw new import_sun_sdk_core13.SDKError("SDK_CONTRACT_ERROR", `Invalid Permit2 allowance ${label}`, { value });
|
|
1662
|
+
}
|
|
1663
|
+
function normalizePermit2Allowance(result) {
|
|
1664
|
+
if (Array.isArray(result)) {
|
|
1665
|
+
return {
|
|
1666
|
+
amount: bigintFrom(result[0], "amount"),
|
|
1667
|
+
expiration: bigintFrom(result[1], "expiration"),
|
|
1668
|
+
nonce: bigintFrom(result[2], "nonce")
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
if (typeof result === "object" && result !== null) {
|
|
1672
|
+
const value = result;
|
|
1673
|
+
return {
|
|
1674
|
+
amount: bigintFrom(value.amount ?? value[0], "amount"),
|
|
1675
|
+
expiration: bigintFrom(value.expiration ?? value[1], "expiration"),
|
|
1676
|
+
nonce: bigintFrom(value.nonce ?? value[2], "nonce")
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
throw new import_sun_sdk_core13.SDKError("SDK_CONTRACT_ERROR", "Invalid Permit2 allowance result", { result });
|
|
1680
|
+
}
|
|
1681
|
+
function buildPermit2TypedData(input) {
|
|
1682
|
+
return {
|
|
1683
|
+
primaryType: "PermitSingle",
|
|
1684
|
+
domain: {
|
|
1685
|
+
name: "Permit2",
|
|
1686
|
+
chainId: PERMIT2_TYPED_DATA_CHAIN_IDS[input.network],
|
|
1687
|
+
verifyingContract: (0, import_permit2_sdk.toEvmHex)(input.permit2)
|
|
1688
|
+
},
|
|
1689
|
+
types: import_permit2_sdk.PERMIT_TYPES,
|
|
1690
|
+
message: {
|
|
1691
|
+
details: {
|
|
1692
|
+
token: (0, import_permit2_sdk.toEvmHex)(input.token),
|
|
1693
|
+
amount: input.amount,
|
|
1694
|
+
expiration: input.expiration,
|
|
1695
|
+
nonce: input.nonce
|
|
1696
|
+
},
|
|
1697
|
+
spender: (0, import_permit2_sdk.toEvmHex)(input.spender),
|
|
1698
|
+
sigDeadline: input.sigDeadline
|
|
1699
|
+
}
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
function buildPermit2ApproveAction(input) {
|
|
1703
|
+
return (0, import_sun_sdk_core13.createContractCallAction)({
|
|
1704
|
+
id: input.id,
|
|
1705
|
+
target: input.permit2,
|
|
1706
|
+
functionSelector: "approve(address,address,uint160,uint48)",
|
|
1707
|
+
waitForReceipt: "included",
|
|
1708
|
+
parameters: [
|
|
1709
|
+
{ type: "address", value: input.token },
|
|
1710
|
+
{ type: "address", value: input.spender },
|
|
1711
|
+
{ type: "uint160", value: input.amount },
|
|
1712
|
+
{ type: "uint48", value: input.expiration }
|
|
1713
|
+
]
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
// src/approval/permit2.ts
|
|
1718
|
+
async function hasTypedDataCapability(context, capabilities) {
|
|
1719
|
+
if (capabilities) return capabilities.signTypedData;
|
|
1720
|
+
return (await context.runtime?.getWallet())?.capabilities.signTypedData === true;
|
|
1721
|
+
}
|
|
1722
|
+
function normalizePlanDeadlineValue(value) {
|
|
1723
|
+
return typeof value === "bigint" ? value.toString() : value;
|
|
1724
|
+
}
|
|
1725
|
+
var MAX_UINT160 = "1461501637330902918203684832716283019655932542975";
|
|
1726
|
+
async function planRouterPermit2Authorization(context, input) {
|
|
1727
|
+
if (input.approval === "none") {
|
|
1728
|
+
return { actions: [], mode: "none" };
|
|
1729
|
+
}
|
|
1730
|
+
const helper = context.chain.contracts.permit2Helper;
|
|
1731
|
+
if (!helper) {
|
|
1732
|
+
throw new import_sun_sdk_core14.SDKError("SDK_INVALID_ARGUMENT", `Permit2Helper is not configured for ${context.network}`, {
|
|
1733
|
+
network: context.network,
|
|
1734
|
+
contract: "permit2Helper"
|
|
1735
|
+
});
|
|
1736
|
+
}
|
|
1737
|
+
const runtime = requireRuntime(context, "Router Permit2 authorization");
|
|
1738
|
+
const actions = [];
|
|
1739
|
+
const token = (0, import_sun_sdk_core14.assertTronAddress)(input.token, "token");
|
|
1740
|
+
const spender = input.spender ? (0, import_sun_sdk_core14.assertTronAddress)(input.spender, "spender") : context.chain.contracts.universalRouter;
|
|
1741
|
+
const tokenApproval = await planApproval(context, {
|
|
1742
|
+
asset: { type: "token", token },
|
|
1743
|
+
owner: input.owner,
|
|
1744
|
+
spender: context.chain.contracts.permit2,
|
|
1745
|
+
amount: input.amount
|
|
1746
|
+
}, {
|
|
1747
|
+
mode: "auto",
|
|
1748
|
+
amount: input.approveAmountType ?? "exact"
|
|
1749
|
+
});
|
|
1750
|
+
actions.push(...tokenApproval.actions);
|
|
1751
|
+
const approved = normalizePermit2HelperResult(await runtime.readContract(buildPermit2HelperRead({
|
|
1752
|
+
helper,
|
|
1753
|
+
permit2: context.chain.contracts.permit2,
|
|
1754
|
+
owner: input.owner,
|
|
1755
|
+
token,
|
|
1756
|
+
spender,
|
|
1757
|
+
amount: input.amount
|
|
1758
|
+
})));
|
|
1759
|
+
const permit2Amount = input.approveAmountType === "max" ? MAX_UINT160 : input.amount;
|
|
1760
|
+
if (approved) {
|
|
1761
|
+
return { actions, mode: "already-approved" };
|
|
1762
|
+
}
|
|
1763
|
+
const expiration = normalizeDeadline(context, {
|
|
1764
|
+
ttlSeconds: input.ttlSeconds ?? 30 * 24 * 60 * 60,
|
|
1765
|
+
...input.deadline !== void 0 ? { deadline: normalizePlanDeadlineValue(input.deadline) } : {}
|
|
1766
|
+
}).toString();
|
|
1767
|
+
const sigDeadline = normalizeDeadline(context, { ttlSeconds: 3600 }).toString();
|
|
1768
|
+
if ((input.approval === "auto" || input.approval === "permit2") && await hasTypedDataCapability(context, input.walletCapabilities)) {
|
|
1769
|
+
const allowance = normalizePermit2Allowance(await runtime.readContract(buildPermit2AllowanceRead({
|
|
1770
|
+
permit2: context.chain.contracts.permit2,
|
|
1771
|
+
owner: input.owner,
|
|
1772
|
+
token,
|
|
1773
|
+
spender
|
|
1774
|
+
})));
|
|
1775
|
+
const typedData = buildPermit2TypedData({
|
|
1776
|
+
network: context.network,
|
|
1777
|
+
permit2: context.chain.contracts.permit2,
|
|
1778
|
+
owner: input.owner,
|
|
1779
|
+
token,
|
|
1780
|
+
spender,
|
|
1781
|
+
amount: permit2Amount,
|
|
1782
|
+
expiration,
|
|
1783
|
+
sigDeadline,
|
|
1784
|
+
nonce: allowance.nonce.toString()
|
|
1785
|
+
});
|
|
1786
|
+
const signatureActionId = `${nextPlanId(context, "permit2")}-signature`;
|
|
1787
|
+
actions.push((0, import_sun_sdk_core14.createTypedDataSignatureAction)({
|
|
1788
|
+
id: signatureActionId,
|
|
1789
|
+
typedData
|
|
1790
|
+
}));
|
|
1791
|
+
return {
|
|
1792
|
+
actions,
|
|
1793
|
+
signatureActionId,
|
|
1794
|
+
permitSingle: typedData.message,
|
|
1795
|
+
mode: "typed-data"
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
if (input.approval === "permit2" || input.approval === "auto" || input.approval === "approve") {
|
|
1799
|
+
actions.push(buildPermit2ApproveAction({
|
|
1800
|
+
id: `${nextPlanId(context, "permit2")}-approve`,
|
|
1801
|
+
permit2: context.chain.contracts.permit2,
|
|
1802
|
+
token,
|
|
1803
|
+
spender,
|
|
1804
|
+
amount: permit2Amount,
|
|
1805
|
+
expiration
|
|
1806
|
+
}));
|
|
1807
|
+
return { actions, mode: "on-chain" };
|
|
1808
|
+
}
|
|
1809
|
+
throw new import_sun_sdk_core14.SDKError(
|
|
1810
|
+
"SDK_INVALID_ARGUMENT",
|
|
1811
|
+
"Unsupported approval mode for Router Permit2 authorization",
|
|
1812
|
+
{ approval: input.approval }
|
|
1813
|
+
);
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
// src/liquidity/v4.ts
|
|
1817
|
+
var Q96 = 1n << 96n;
|
|
1818
|
+
function parseString(value, label) {
|
|
1819
|
+
if (typeof value === "bigint" || typeof value === "number" || typeof value === "string") {
|
|
1820
|
+
return BigInt(value.toString()).toString();
|
|
1821
|
+
}
|
|
1822
|
+
throw new import_sun_sdk_core15.SDKError("SDK_CONTRACT_ERROR", `${label} must be an integer-like contract result`, { value });
|
|
1823
|
+
}
|
|
1824
|
+
function parseNumber(value, label) {
|
|
1825
|
+
const parsed = Number(parseString(value, label));
|
|
1826
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
1827
|
+
throw new import_sun_sdk_core15.SDKError("SDK_CONTRACT_ERROR", `${label} must be a safe integer`, { value });
|
|
1828
|
+
}
|
|
1829
|
+
return parsed;
|
|
1830
|
+
}
|
|
1831
|
+
function unwrapSingle(value) {
|
|
1832
|
+
return Array.isArray(value) && value.length === 1 ? value[0] : value;
|
|
1833
|
+
}
|
|
1834
|
+
function signed24(value) {
|
|
1835
|
+
const masked = value & 0xffffffn;
|
|
1836
|
+
return Number((masked & 0x800000n) === 0n ? masked : masked - 0x1000000n);
|
|
1837
|
+
}
|
|
1838
|
+
function parsePackedPositionTicks(value) {
|
|
1839
|
+
const info = BigInt(String(unwrapSingle(value)));
|
|
1840
|
+
return {
|
|
1841
|
+
tickLower: signed24(info >> 8n),
|
|
1842
|
+
tickUpper: signed24(info >> 32n)
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1845
|
+
function parseV4Address(value, label) {
|
|
1846
|
+
const raw = String(unwrapSingle(value));
|
|
1847
|
+
try {
|
|
1848
|
+
return (0, import_sun_sdk_core15.assertTronAddress)(raw, label);
|
|
1849
|
+
} catch {
|
|
1850
|
+
const evmHex = /^41[0-9a-fA-F]{40}$/.test(raw) ? `0x${raw.slice(2)}` : raw;
|
|
1851
|
+
return evmHexToTronAddress(evmHex, label);
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
function parsePositionPoolKey(value, network) {
|
|
1855
|
+
if (Array.isArray(value) && value.length >= 5) {
|
|
1856
|
+
return (0, import_sun_sdk_sunswap_v4.createV4PoolKey)({
|
|
1857
|
+
network,
|
|
1858
|
+
currencyA: parseV4Address(value[0], "poolKey.currency0"),
|
|
1859
|
+
currencyB: parseV4Address(value[1], "poolKey.currency1"),
|
|
1860
|
+
hooks: parseV4Address(value[2], "poolKey.hooks"),
|
|
1861
|
+
fee: parseNumber(value[3], "poolKey.fee"),
|
|
1862
|
+
parameters: String(value[4])
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
if (typeof value === "object" && value !== null) {
|
|
1866
|
+
const key = value;
|
|
1867
|
+
if (key.currency0 !== void 0 && key.currency1 !== void 0 && key.hooks !== void 0 && key.fee !== void 0 && key.parameters !== void 0) {
|
|
1868
|
+
return (0, import_sun_sdk_sunswap_v4.createV4PoolKey)({
|
|
1869
|
+
network,
|
|
1870
|
+
currencyA: parseV4Address(key.currency0, "poolKey.currency0"),
|
|
1871
|
+
currencyB: parseV4Address(key.currency1, "poolKey.currency1"),
|
|
1872
|
+
hooks: parseV4Address(key.hooks, "poolKey.hooks"),
|
|
1873
|
+
fee: parseNumber(key.fee, "poolKey.fee"),
|
|
1874
|
+
parameters: String(key.parameters)
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
throw new import_sun_sdk_core15.SDKError("SDK_CONTRACT_ERROR", "V4 position context is missing poolKey", { value });
|
|
1879
|
+
}
|
|
1880
|
+
function parseSlot0(result) {
|
|
1881
|
+
if (Array.isArray(result)) {
|
|
1882
|
+
return {
|
|
1883
|
+
sqrtPriceX96: parseString(result[0], "sqrtPriceX96"),
|
|
1884
|
+
tick: parseNumber(result[1], "tick")
|
|
1885
|
+
};
|
|
1886
|
+
}
|
|
1887
|
+
if (typeof result === "object" && result !== null) {
|
|
1888
|
+
const slot0 = result;
|
|
1889
|
+
return {
|
|
1890
|
+
sqrtPriceX96: parseString(slot0.sqrtPriceX96, "sqrtPriceX96"),
|
|
1891
|
+
tick: parseNumber(slot0.tick, "tick")
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
throw new import_sun_sdk_core15.SDKError("SDK_CONTRACT_ERROR", "V4 slot0 result is malformed", { result });
|
|
1895
|
+
}
|
|
1896
|
+
async function resolveV4PoolKey(context, input) {
|
|
1897
|
+
if (input.poolKey !== void 0) {
|
|
1898
|
+
return { poolKey: input.poolKey, derived: false, amountsSwapped: false };
|
|
1899
|
+
}
|
|
1900
|
+
if (input.token0 === void 0 || input.token1 === void 0 || input.fee === void 0 || input.tickSpacing === void 0) {
|
|
1901
|
+
throw new import_sun_sdk_core15.SDKError("SDK_INVALID_ARGUMENT", "V4 poolKey or complete token pool inputs are required");
|
|
1902
|
+
}
|
|
1903
|
+
const token0 = normalizeProtocolToken(context, input.token0, "token0").contractAddress;
|
|
1904
|
+
const token1 = normalizeProtocolToken(context, input.token1, "token1").contractAddress;
|
|
1905
|
+
const hooks = (0, import_sun_sdk_core15.assertTronAddress)(input.hooks ?? import_sun_sdk_core15.TRON_ZERO_ADDRESS, "hooks");
|
|
1906
|
+
if (input.dynamicFee && hooks === import_sun_sdk_core15.TRON_ZERO_ADDRESS) {
|
|
1907
|
+
throw new import_sun_sdk_core15.SDKError("SDK_INVALID_ARGUMENT", "Dynamic V4 fees require a non-zero hooks contract");
|
|
1908
|
+
}
|
|
1909
|
+
const hooksRegistration = input.hooksRegistration ?? (hooks === import_sun_sdk_core15.TRON_ZERO_ADDRESS ? 0 : parseNumber(
|
|
1910
|
+
unwrapSingle(await readRequiredContract(
|
|
1911
|
+
context,
|
|
1912
|
+
(0, import_sun_sdk_sunswap_v4.buildV4HookRegistrationBitmapRead)({ network: context.network, hooks }),
|
|
1913
|
+
"V4 hook registration read"
|
|
1914
|
+
)),
|
|
1915
|
+
"hooksRegistration"
|
|
1916
|
+
));
|
|
1917
|
+
const poolKey = (0, import_sun_sdk_sunswap_v4.createV4PoolKey)({
|
|
1918
|
+
network: context.network,
|
|
1919
|
+
currencyA: token0,
|
|
1920
|
+
currencyB: token1,
|
|
1921
|
+
hooks,
|
|
1922
|
+
fee: input.dynamicFee ? 8388608 : input.fee,
|
|
1923
|
+
parameters: (0, import_sun_sdk_sunswap_v4.encodeV4PoolParameters)({
|
|
1924
|
+
tickSpacing: input.tickSpacing,
|
|
1925
|
+
hooksRegistration: (0, import_sun_sdk_sunswap_v4.decodeV4HooksRegistration)(hooksRegistration)
|
|
1926
|
+
})
|
|
1927
|
+
});
|
|
1928
|
+
return {
|
|
1929
|
+
poolKey,
|
|
1930
|
+
derived: true,
|
|
1931
|
+
amountsSwapped: poolKey.currency0 !== token0
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
function parseV4PositionContext(result, network) {
|
|
1935
|
+
let poolKey;
|
|
1936
|
+
let tickLower;
|
|
1937
|
+
let tickUpper;
|
|
1938
|
+
if (Array.isArray(result)) {
|
|
1939
|
+
poolKey = result[0];
|
|
1940
|
+
if (result[2] === void 0 && result[1] !== void 0) {
|
|
1941
|
+
const ticks = parsePackedPositionTicks(result[1]);
|
|
1942
|
+
tickLower = ticks.tickLower;
|
|
1943
|
+
tickUpper = ticks.tickUpper;
|
|
1944
|
+
} else {
|
|
1945
|
+
tickLower = result[1];
|
|
1946
|
+
tickUpper = result[2];
|
|
1947
|
+
}
|
|
1948
|
+
} else if (typeof result === "object" && result !== null) {
|
|
1949
|
+
const info = result;
|
|
1950
|
+
poolKey = info.poolKey;
|
|
1951
|
+
tickLower = info.tickLower;
|
|
1952
|
+
tickUpper = info.tickUpper;
|
|
1953
|
+
} else {
|
|
1954
|
+
throw new import_sun_sdk_core15.SDKError("SDK_CONTRACT_ERROR", "V4 position context result is malformed", { result });
|
|
1955
|
+
}
|
|
1956
|
+
return {
|
|
1957
|
+
poolKey: parsePositionPoolKey(poolKey, network),
|
|
1958
|
+
tickLower: parseNumber(tickLower, "tickLower"),
|
|
1959
|
+
tickUpper: parseNumber(tickUpper, "tickUpper")
|
|
1960
|
+
};
|
|
1961
|
+
}
|
|
1962
|
+
function assertV4PoolKeyMatches(tokenId, supplied, position) {
|
|
1963
|
+
const suppliedPoolId = (0, import_sun_sdk_sunswap_v4.computeV4PoolId)(supplied);
|
|
1964
|
+
const positionPoolId = (0, import_sun_sdk_sunswap_v4.computeV4PoolId)(position);
|
|
1965
|
+
if (suppliedPoolId !== positionPoolId) {
|
|
1966
|
+
throw new import_sun_sdk_core15.SDKError("SDK_INVALID_ARGUMENT", "V4 pool context does not match the tokenId position", {
|
|
1967
|
+
tokenId,
|
|
1968
|
+
suppliedPoolId,
|
|
1969
|
+
positionPoolId
|
|
1970
|
+
});
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
async function resolveV4PositionContext(context, tokenId, contracts) {
|
|
1974
|
+
const result = await readRequiredContract(
|
|
1975
|
+
context,
|
|
1976
|
+
(0, import_sun_sdk_sunswap_v4.buildV4PoolAndPositionInfoRead)({
|
|
1977
|
+
network: context.network,
|
|
1978
|
+
tokenId,
|
|
1979
|
+
positionManager: resolveProtocolContract(context, contracts, "sunswapV4PositionManager")
|
|
1980
|
+
}),
|
|
1981
|
+
"V4 position context read"
|
|
1982
|
+
);
|
|
1983
|
+
return parseV4PositionContext(result, context.network);
|
|
1984
|
+
}
|
|
1985
|
+
async function shouldInitializeV4Pool(context, poolKey, input) {
|
|
1986
|
+
if (!input.createPool) return { shouldInitialize: false };
|
|
1987
|
+
if (input.poolInitializationMode === "trust-input") return { shouldInitialize: true };
|
|
1988
|
+
try {
|
|
1989
|
+
const slot0Result = await readRequiredContract(
|
|
1990
|
+
context,
|
|
1991
|
+
(0, import_sun_sdk_sunswap_v4.buildV4PoolSlot0Read)({
|
|
1992
|
+
network: context.network,
|
|
1993
|
+
poolId: (0, import_sun_sdk_sunswap_v4.computeV4PoolId)(poolKey),
|
|
1994
|
+
poolManager: resolveProtocolContract(context, input.contracts, "sunswapV4PoolManager")
|
|
1995
|
+
}),
|
|
1996
|
+
"V4 pool initialization check"
|
|
1997
|
+
);
|
|
1998
|
+
const sqrtPriceX96 = parseSlot0(slot0Result).sqrtPriceX96;
|
|
1999
|
+
return BigInt(sqrtPriceX96) === 0n ? { shouldInitialize: true } : { shouldInitialize: false, sqrtPriceX96 };
|
|
2000
|
+
} catch {
|
|
2001
|
+
return { shouldInitialize: true };
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
function maxV4LiquidityForAmounts(input) {
|
|
2005
|
+
const sqrtPrice = BigInt(input.sqrtPriceX96);
|
|
2006
|
+
let sqrtA = BigInt(import_sun_sdk_sunswap_v32.TickMath.getSqrtRatioAtTick(input.tickLower).toString());
|
|
2007
|
+
let sqrtB = BigInt(import_sun_sdk_sunswap_v32.TickMath.getSqrtRatioAtTick(input.tickUpper).toString());
|
|
2008
|
+
if (sqrtA > sqrtB) [sqrtA, sqrtB] = [sqrtB, sqrtA];
|
|
2009
|
+
const amount0 = BigInt(input.amount0);
|
|
2010
|
+
const amount1 = BigInt(input.amount1);
|
|
2011
|
+
const difference = sqrtB - sqrtA;
|
|
2012
|
+
let liquidity;
|
|
2013
|
+
if (sqrtPrice <= sqrtA) {
|
|
2014
|
+
liquidity = amount0 * sqrtA * sqrtB / (Q96 * difference);
|
|
2015
|
+
} else if (sqrtPrice < sqrtB) {
|
|
2016
|
+
const liquidity0 = amount0 * sqrtPrice * sqrtB / (Q96 * (sqrtB - sqrtPrice));
|
|
2017
|
+
const liquidity1 = amount1 * Q96 / (sqrtPrice - sqrtA);
|
|
2018
|
+
liquidity = liquidity0 < liquidity1 ? liquidity0 : liquidity1;
|
|
2019
|
+
} else {
|
|
2020
|
+
liquidity = amount1 * Q96 / difference;
|
|
2021
|
+
}
|
|
2022
|
+
return liquidity.toString();
|
|
2023
|
+
}
|
|
2024
|
+
async function resolveV4SqrtPrice(context, poolKey, input) {
|
|
2025
|
+
if (input.createPool) {
|
|
2026
|
+
if (input.sqrtPriceX96 === void 0) {
|
|
2027
|
+
throw new import_sun_sdk_core15.SDKError("SDK_INVALID_ARGUMENT", "sqrtPriceX96 is required when createPool is true");
|
|
2028
|
+
}
|
|
2029
|
+
return normalizeBaseUnitAmount(input.sqrtPriceX96, "sqrtPriceX96");
|
|
2030
|
+
}
|
|
2031
|
+
const slot0Result = await readRequiredContract(
|
|
2032
|
+
context,
|
|
2033
|
+
(0, import_sun_sdk_sunswap_v4.buildV4PoolSlot0Read)({
|
|
2034
|
+
network: context.network,
|
|
2035
|
+
poolId: (0, import_sun_sdk_sunswap_v4.computeV4PoolId)(poolKey),
|
|
2036
|
+
poolManager: resolveProtocolContract(context, input.contracts, "sunswapV4PoolManager")
|
|
2037
|
+
}),
|
|
2038
|
+
"V4 pool state read"
|
|
2039
|
+
);
|
|
2040
|
+
return parseSlot0(slot0Result).sqrtPriceX96;
|
|
2041
|
+
}
|
|
2042
|
+
async function resolveV4LiquidityAmounts(context, input) {
|
|
2043
|
+
const hasResolvedAmount = input.liquidity !== void 0 || input.amount0Max !== void 0 || input.amount1Max !== void 0;
|
|
2044
|
+
if (hasResolvedAmount) {
|
|
2045
|
+
if (input.liquidity === void 0 || input.amount0Max === void 0 || input.amount1Max === void 0) {
|
|
2046
|
+
throw new import_sun_sdk_core15.SDKError("SDK_INVALID_ARGUMENT", "liquidity, amount0Max, and amount1Max must be provided together");
|
|
2047
|
+
}
|
|
2048
|
+
return {
|
|
2049
|
+
liquidity: normalizeBaseUnitAmount(input.liquidity, "liquidity"),
|
|
2050
|
+
amount0Max: normalizeBaseUnitAmount(input.amount0Max, "amount0Max", { allowZero: true }),
|
|
2051
|
+
amount1Max: normalizeBaseUnitAmount(input.amount1Max, "amount1Max", { allowZero: true }),
|
|
2052
|
+
amount0Desired: normalizeBaseUnitAmount(input.amount0Max, "amount0Max", { allowZero: true }),
|
|
2053
|
+
amount1Desired: normalizeBaseUnitAmount(input.amount1Max, "amount1Max", { allowZero: true }),
|
|
2054
|
+
derived: false
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2057
|
+
if (input.amount0 === void 0 && input.amount1 === void 0) {
|
|
2058
|
+
throw new import_sun_sdk_core15.SDKError("SDK_INVALID_ARGUMENT", "V4 liquidity requires at least one of amount0 or amount1");
|
|
2059
|
+
}
|
|
2060
|
+
if (input.tickLower >= input.tickUpper) {
|
|
2061
|
+
throw new import_sun_sdk_core15.SDKError("SDK_INVALID_ARGUMENT", "tickLower must be less than tickUpper", {
|
|
2062
|
+
tickLower: input.tickLower,
|
|
2063
|
+
tickUpper: input.tickUpper
|
|
2064
|
+
});
|
|
2065
|
+
}
|
|
2066
|
+
const userAmount0 = normalizeBaseUnitAmount(input.amount0 ?? "0", "amount0", { allowZero: true });
|
|
2067
|
+
const userAmount1 = normalizeBaseUnitAmount(input.amount1 ?? "0", "amount1", { allowZero: true });
|
|
2068
|
+
const amount0 = input.amountsSwapped ? userAmount1 : userAmount0;
|
|
2069
|
+
const amount1 = input.amountsSwapped ? userAmount0 : userAmount1;
|
|
2070
|
+
const liquidity = maxV4LiquidityForAmounts({
|
|
2071
|
+
sqrtPriceX96: await resolveV4SqrtPrice(context, input.poolKey, input),
|
|
2072
|
+
tickLower: input.tickLower,
|
|
2073
|
+
tickUpper: input.tickUpper,
|
|
2074
|
+
amount0,
|
|
2075
|
+
amount1
|
|
2076
|
+
});
|
|
2077
|
+
if (BigInt(liquidity) <= 0n) {
|
|
2078
|
+
throw new import_sun_sdk_core15.SDKError("SDK_INVALID_ARGUMENT", "V4 amount inputs resolve to zero liquidity", {
|
|
2079
|
+
amount0,
|
|
2080
|
+
amount1,
|
|
2081
|
+
tickLower: input.tickLower,
|
|
2082
|
+
tickUpper: input.tickUpper
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2085
|
+
const slippageBips = slippageToBips(input.slippage ?? "0.5%");
|
|
2086
|
+
const halfUp = (amount, bips) => ((amount * BigInt(1e4 + bips) + 5000n) / 10000n).toString();
|
|
2087
|
+
return {
|
|
2088
|
+
liquidity,
|
|
2089
|
+
amount0Max: input.roundingMode === "half-up" ? halfUp(BigInt(amount0), slippageBips) : applySlippageMax(BigInt(amount0), slippageBips).toString(),
|
|
2090
|
+
amount1Max: input.roundingMode === "half-up" ? halfUp(BigInt(amount1), slippageBips) : applySlippageMax(BigInt(amount1), slippageBips).toString(),
|
|
2091
|
+
amount0Desired: amount0,
|
|
2092
|
+
amount1Desired: amount1,
|
|
2093
|
+
derived: true
|
|
2094
|
+
};
|
|
2095
|
+
}
|
|
2096
|
+
function nativeCallValue(poolKey, amounts, mode) {
|
|
2097
|
+
if (mode !== "desired") return void 0;
|
|
2098
|
+
let value = 0n;
|
|
2099
|
+
if (poolKey.currency0 === import_sun_sdk_core15.TRON_ZERO_ADDRESS) value += BigInt(amounts.amount0Desired);
|
|
2100
|
+
if (poolKey.currency1 === import_sun_sdk_core15.TRON_ZERO_ADDRESS) value += BigInt(amounts.amount1Desired);
|
|
2101
|
+
return value;
|
|
2102
|
+
}
|
|
2103
|
+
function planMetadata(input) {
|
|
2104
|
+
return {
|
|
2105
|
+
protocol: "sunswap-v4",
|
|
2106
|
+
operation: input.operation,
|
|
2107
|
+
...input.poolId !== void 0 ? { poolId: input.poolId } : {},
|
|
2108
|
+
...input.tokenId !== void 0 ? { tokenId: input.tokenId } : {},
|
|
2109
|
+
...input.owner !== void 0 ? { owner: input.owner } : {},
|
|
2110
|
+
...input.sweepRecipient !== void 0 ? { sweepRecipient: input.sweepRecipient } : {},
|
|
2111
|
+
...input.deadline !== void 0 ? { deadline: input.deadline } : {},
|
|
2112
|
+
...input.poolKeyDerived !== void 0 ? { poolKeyDerived: input.poolKeyDerived } : {},
|
|
2113
|
+
...input.amountsDerived !== void 0 ? { amountsDerived: input.amountsDerived } : {},
|
|
2114
|
+
...input.createPool !== void 0 ? { createPool: input.createPool } : {}
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2117
|
+
async function approvalsFromV4Action(context, action, owner, mode, scope) {
|
|
2118
|
+
const requirements = action.tokenRequirements ?? [];
|
|
2119
|
+
const approvals = [];
|
|
2120
|
+
for (const requirement of requirements) {
|
|
2121
|
+
if (BigInt(requirement.amount) === 0n) continue;
|
|
2122
|
+
if (mode === "none") continue;
|
|
2123
|
+
const authorization = await planRouterPermit2Authorization(context, {
|
|
2124
|
+
owner,
|
|
2125
|
+
token: requirement.token,
|
|
2126
|
+
spender: requirement.spender,
|
|
2127
|
+
amount: requirement.amount,
|
|
2128
|
+
approval: "approve"
|
|
2129
|
+
});
|
|
2130
|
+
for (const approval of authorization.actions) {
|
|
2131
|
+
if (approval.type !== "approve" && approval.type !== "contract-call") {
|
|
2132
|
+
throw new import_sun_sdk_core15.SDKError(
|
|
2133
|
+
"SDK_RUNTIME_ERROR",
|
|
2134
|
+
"V4 Permit2 authorization produced a non-contract action",
|
|
2135
|
+
{ actionType: approval.type }
|
|
2136
|
+
);
|
|
2137
|
+
}
|
|
2138
|
+
approvals.push(...attachApprovalIntent(
|
|
2139
|
+
[approval],
|
|
2140
|
+
scope,
|
|
2141
|
+
requirement.token
|
|
2142
|
+
));
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
return approvals;
|
|
2146
|
+
}
|
|
2147
|
+
var V4LiquidityClient = class {
|
|
2148
|
+
constructor(context) {
|
|
2149
|
+
this.context = context;
|
|
2150
|
+
}
|
|
2151
|
+
context;
|
|
2152
|
+
async read(input) {
|
|
2153
|
+
const poolId = (0, import_sun_sdk_sunswap_v4.assertV4PoolId)(input.poolId);
|
|
2154
|
+
const [slot0Result, liquidityResult] = await readMulticallRequired(
|
|
2155
|
+
this.context,
|
|
2156
|
+
(0, import_sun_sdk_sunswap_v4.buildV4PoolStateReads)({
|
|
2157
|
+
network: this.context.network,
|
|
2158
|
+
poolId,
|
|
2159
|
+
poolManager: resolveProtocolContract(this.context, input.contracts, "sunswapV4PoolManager")
|
|
2160
|
+
}),
|
|
2161
|
+
"V4 pool state read"
|
|
2162
|
+
);
|
|
2163
|
+
const slot0 = parseSlot0(slot0Result);
|
|
2164
|
+
return {
|
|
2165
|
+
protocol: "sunswap-v4",
|
|
2166
|
+
poolId,
|
|
2167
|
+
sqrtPriceX96: slot0.sqrtPriceX96,
|
|
2168
|
+
tick: slot0.tick,
|
|
2169
|
+
liquidity: parseString(liquidityResult, "liquidity")
|
|
2170
|
+
};
|
|
2171
|
+
}
|
|
2172
|
+
initialize = {
|
|
2173
|
+
plan: async (input) => this.planInitialize(input),
|
|
2174
|
+
execute: async (input) => executeTransactionPlan(await this.planInitialize(input), createContextExecutor(this.context))
|
|
2175
|
+
};
|
|
2176
|
+
add = {
|
|
2177
|
+
plan: async (input) => this.planMint(input),
|
|
2178
|
+
execute: async (input) => executeTransactionPlan(await this.planMint(input), createContextExecutor(this.context))
|
|
2179
|
+
};
|
|
2180
|
+
increase = {
|
|
2181
|
+
plan: async (input) => this.planIncrease(input),
|
|
2182
|
+
execute: async (input) => executeTransactionPlan(await this.planIncrease(input), createContextExecutor(this.context))
|
|
2183
|
+
};
|
|
2184
|
+
async planInitialize(input) {
|
|
2185
|
+
const { address: owner } = await requireWalletSnapshot(this.context, "V4 initialize pool");
|
|
2186
|
+
const positionManager = resolveProtocolContract(this.context, input.contracts, "sunswapV4PositionManager");
|
|
2187
|
+
const planId = nextPlanId(this.context, "liquidity-v4-initialize");
|
|
2188
|
+
const poolKey = await resolveV4PoolKey(this.context, input);
|
|
2189
|
+
const action = (0, import_sun_sdk_sunswap_v4.buildV4InitializePoolAction)({
|
|
2190
|
+
network: this.context.network,
|
|
2191
|
+
positionManager,
|
|
2192
|
+
poolKey: poolKey.poolKey,
|
|
2193
|
+
sqrtPriceX96: normalizeBaseUnitAmount(input.sqrtPriceX96, "sqrtPriceX96"),
|
|
2194
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {},
|
|
2195
|
+
id: `${planId}-tx`
|
|
2196
|
+
});
|
|
2197
|
+
const poolId = (0, import_sun_sdk_sunswap_v4.computeV4PoolId)(poolKey.poolKey);
|
|
2198
|
+
const transaction = {
|
|
2199
|
+
...action,
|
|
2200
|
+
intent: {
|
|
2201
|
+
kind: "create-pool",
|
|
2202
|
+
scope: liquidityIntentScope(SUNSWAP_V4_LIQUIDITY_PROTOCOL_ID, "initialize-pool"),
|
|
2203
|
+
poolId
|
|
2204
|
+
}
|
|
2205
|
+
};
|
|
2206
|
+
return {
|
|
2207
|
+
id: planId,
|
|
2208
|
+
kind: "liquidity",
|
|
2209
|
+
network: this.context.network,
|
|
2210
|
+
owner,
|
|
2211
|
+
summary: {
|
|
2212
|
+
title: "Initialize SunSwap V4 pool",
|
|
2213
|
+
lines: [`Pool ${poolId}`]
|
|
2214
|
+
},
|
|
2215
|
+
approvals: [],
|
|
2216
|
+
transactions: [transaction],
|
|
2217
|
+
metadata: planMetadata({ operation: "initialize-pool", poolId, poolKeyDerived: poolKey.derived })
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2220
|
+
async planMint(input) {
|
|
2221
|
+
const positionManager = resolveProtocolContract(this.context, input.contracts, "sunswapV4PositionManager");
|
|
2222
|
+
const planId = nextPlanId(this.context, "liquidity-v4-mint");
|
|
2223
|
+
const { address: walletAddress } = await requireWalletSnapshot(this.context, "V4 mint position");
|
|
2224
|
+
const owner = input.owner !== void 0 ? (0, import_sun_sdk_core15.assertTronAddress)(input.owner, "owner") : walletAddress;
|
|
2225
|
+
if (owner !== walletAddress) {
|
|
2226
|
+
throw new import_sun_sdk_core15.SDKError("SDK_WALLET_ACCOUNT_MISMATCH", "V4 mint owner does not match the connected wallet", {
|
|
2227
|
+
expectedAddress: owner,
|
|
2228
|
+
actualAddress: walletAddress
|
|
2229
|
+
});
|
|
2230
|
+
}
|
|
2231
|
+
const deadline = normalizeDeadline(this.context, input);
|
|
2232
|
+
const poolKey = await resolveV4PoolKey(this.context, input);
|
|
2233
|
+
const initialization = await shouldInitializeV4Pool(this.context, poolKey.poolKey, input);
|
|
2234
|
+
const amounts = await resolveV4LiquidityAmounts(this.context, {
|
|
2235
|
+
...input,
|
|
2236
|
+
poolKey: poolKey.poolKey,
|
|
2237
|
+
amountsSwapped: poolKey.amountsSwapped,
|
|
2238
|
+
...initialization.sqrtPriceX96 !== void 0 ? { sqrtPriceX96: initialization.sqrtPriceX96 } : {},
|
|
2239
|
+
...initialization.shouldInitialize ? { createPool: true } : { createPool: false }
|
|
2240
|
+
});
|
|
2241
|
+
if (input.createPool && input.sqrtPriceX96 === void 0) {
|
|
2242
|
+
throw new import_sun_sdk_core15.SDKError("SDK_INVALID_ARGUMENT", "sqrtPriceX96 is required when createPool is true");
|
|
2243
|
+
}
|
|
2244
|
+
const hasNativeCurrency = poolKey.poolKey.currency0 === import_sun_sdk_core15.TRON_ZERO_ADDRESS || poolKey.poolKey.currency1 === import_sun_sdk_core15.TRON_ZERO_ADDRESS;
|
|
2245
|
+
const sweepRecipient = hasNativeCurrency ? input.sweepRecipient !== void 0 ? (0, import_sun_sdk_core15.assertTronAddress)(input.sweepRecipient, "sweepRecipient") : owner : void 0;
|
|
2246
|
+
const desiredNativeCallValue = nativeCallValue(poolKey.poolKey, amounts, input.nativeCallValueMode);
|
|
2247
|
+
const action = (0, import_sun_sdk_sunswap_v4.buildV4MintPositionAction)({
|
|
2248
|
+
network: this.context.network,
|
|
2249
|
+
positionManager,
|
|
2250
|
+
positionConfig: { poolKey: poolKey.poolKey, tickLower: input.tickLower, tickUpper: input.tickUpper },
|
|
2251
|
+
liquidity: amounts.liquidity,
|
|
2252
|
+
amount0Max: amounts.amount0Max,
|
|
2253
|
+
amount1Max: amounts.amount1Max,
|
|
2254
|
+
owner,
|
|
2255
|
+
deadline,
|
|
2256
|
+
...desiredNativeCallValue !== void 0 ? { callValue: desiredNativeCallValue } : {},
|
|
2257
|
+
...sweepRecipient !== void 0 ? { sweepRecipient } : {},
|
|
2258
|
+
...input.hookData !== void 0 ? { hookData: input.hookData } : {},
|
|
2259
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {},
|
|
2260
|
+
id: `${planId}-tx`
|
|
2261
|
+
});
|
|
2262
|
+
const scope = liquidityIntentScope(
|
|
2263
|
+
SUNSWAP_V4_LIQUIDITY_PROTOCOL_ID,
|
|
2264
|
+
initialization.shouldInitialize ? "initialize-and-mint" : "mint-position"
|
|
2265
|
+
);
|
|
2266
|
+
const approvals = await approvalsFromV4Action(this.context, action, owner, input.approval, scope);
|
|
2267
|
+
const poolId = (0, import_sun_sdk_sunswap_v4.computeV4PoolId)(poolKey.poolKey);
|
|
2268
|
+
const initializeAction = initialization.shouldInitialize ? (0, import_sun_sdk_sunswap_v4.buildV4InitializePoolAction)({
|
|
2269
|
+
network: this.context.network,
|
|
2270
|
+
positionManager,
|
|
2271
|
+
poolKey: poolKey.poolKey,
|
|
2272
|
+
sqrtPriceX96: normalizeBaseUnitAmount(input.sqrtPriceX96, "sqrtPriceX96"),
|
|
2273
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {},
|
|
2274
|
+
id: `${planId}-initialize`
|
|
2275
|
+
}) : void 0;
|
|
2276
|
+
const calls = [
|
|
2277
|
+
...(input.permit2Signatures ?? []).map((permit) => (0, import_sun_sdk_sunswap_v4.encodeV4Permit2ForwardCall)({ owner, permit })),
|
|
2278
|
+
...initializeAction ? [(0, import_sun_sdk_sunswap_v4.encodeV4ContractCallAction)(initializeAction)] : [],
|
|
2279
|
+
(0, import_sun_sdk_sunswap_v4.encodeV4ContractCallAction)(action)
|
|
2280
|
+
];
|
|
2281
|
+
const transactionAction = initializeAction || input.transactionMode === "multicall" || (input.permit2Signatures?.length ?? 0) > 0 ? (0, import_sun_sdk_sunswap_v4.buildV4MulticallAction)({
|
|
2282
|
+
network: this.context.network,
|
|
2283
|
+
positionManager,
|
|
2284
|
+
calls,
|
|
2285
|
+
...action.callValue !== void 0 ? { callValue: action.callValue } : {},
|
|
2286
|
+
...action.tokenRequirements !== void 0 ? { tokenRequirements: action.tokenRequirements } : {},
|
|
2287
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {},
|
|
2288
|
+
id: `${planId}-tx`
|
|
2289
|
+
}) : action;
|
|
2290
|
+
const transaction = {
|
|
2291
|
+
...transactionAction,
|
|
2292
|
+
waitForReceipt: "included",
|
|
2293
|
+
intent: {
|
|
2294
|
+
kind: "add-liquidity",
|
|
2295
|
+
scope
|
|
2296
|
+
}
|
|
2297
|
+
};
|
|
2298
|
+
return {
|
|
2299
|
+
id: planId,
|
|
2300
|
+
kind: "liquidity",
|
|
2301
|
+
network: this.context.network,
|
|
2302
|
+
owner,
|
|
2303
|
+
summary: {
|
|
2304
|
+
title: "Mint SunSwap V4 position",
|
|
2305
|
+
lines: [`Pool ${poolId}`, `Owner ${owner}`]
|
|
2306
|
+
},
|
|
2307
|
+
approvals,
|
|
2308
|
+
transactions: [transaction],
|
|
2309
|
+
metadata: planMetadata({
|
|
2310
|
+
operation: "mint-position",
|
|
2311
|
+
poolId,
|
|
2312
|
+
owner,
|
|
2313
|
+
...sweepRecipient !== void 0 ? { sweepRecipient } : {},
|
|
2314
|
+
deadline,
|
|
2315
|
+
poolKeyDerived: poolKey.derived,
|
|
2316
|
+
amountsDerived: amounts.derived,
|
|
2317
|
+
createPool: initialization.shouldInitialize
|
|
2318
|
+
})
|
|
2319
|
+
};
|
|
2320
|
+
}
|
|
2321
|
+
async planIncrease(input) {
|
|
2322
|
+
const positionManager = resolveProtocolContract(this.context, input.contracts, "sunswapV4PositionManager");
|
|
2323
|
+
const planId = nextPlanId(this.context, "liquidity-v4-increase");
|
|
2324
|
+
const owner = await requireWalletAddress(this.context, "V4 increase liquidity");
|
|
2325
|
+
const deadline = normalizeDeadline(this.context, input);
|
|
2326
|
+
const tokenId = normalizeBaseUnitAmount(input.tokenId, "tokenId");
|
|
2327
|
+
const poolKeyNeedsPosition = input.poolKey === void 0 && input.token0 === void 0;
|
|
2328
|
+
const position = await resolveV4PositionContext(this.context, tokenId, input.contracts);
|
|
2329
|
+
const poolKey = poolKeyNeedsPosition ? { poolKey: position.poolKey, derived: true, amountsSwapped: false } : await resolveV4PoolKey(this.context, input);
|
|
2330
|
+
assertV4PoolKeyMatches(tokenId, poolKey.poolKey, position.poolKey);
|
|
2331
|
+
const amounts = await resolveV4LiquidityAmounts(this.context, {
|
|
2332
|
+
...input,
|
|
2333
|
+
poolKey: poolKey.poolKey,
|
|
2334
|
+
tickLower: position.tickLower,
|
|
2335
|
+
tickUpper: position.tickUpper,
|
|
2336
|
+
amountsSwapped: poolKey.amountsSwapped
|
|
2337
|
+
});
|
|
2338
|
+
const hasNativeCurrency = poolKey.poolKey.currency0 === import_sun_sdk_core15.TRON_ZERO_ADDRESS || poolKey.poolKey.currency1 === import_sun_sdk_core15.TRON_ZERO_ADDRESS;
|
|
2339
|
+
const sweepRecipient = hasNativeCurrency ? input.sweepRecipient !== void 0 ? (0, import_sun_sdk_core15.assertTronAddress)(input.sweepRecipient, "sweepRecipient") : owner : void 0;
|
|
2340
|
+
const desiredNativeCallValue = nativeCallValue(poolKey.poolKey, amounts, input.nativeCallValueMode);
|
|
2341
|
+
const action = (0, import_sun_sdk_sunswap_v4.buildV4IncreaseLiquidityAction)({
|
|
2342
|
+
network: this.context.network,
|
|
2343
|
+
positionManager,
|
|
2344
|
+
poolKey: poolKey.poolKey,
|
|
2345
|
+
tokenId,
|
|
2346
|
+
liquidity: amounts.liquidity,
|
|
2347
|
+
amount0Max: amounts.amount0Max,
|
|
2348
|
+
amount1Max: amounts.amount1Max,
|
|
2349
|
+
deadline,
|
|
2350
|
+
...desiredNativeCallValue !== void 0 ? { callValue: desiredNativeCallValue } : {},
|
|
2351
|
+
...sweepRecipient !== void 0 ? { sweepRecipient } : {},
|
|
2352
|
+
...input.hookData !== void 0 ? { hookData: input.hookData } : {},
|
|
2353
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {},
|
|
2354
|
+
id: `${planId}-tx`
|
|
2355
|
+
});
|
|
2356
|
+
const scope = liquidityIntentScope(SUNSWAP_V4_LIQUIDITY_PROTOCOL_ID, "increase-liquidity");
|
|
2357
|
+
const approvals = await approvalsFromV4Action(this.context, action, owner, input.approval, scope);
|
|
2358
|
+
const poolId = (0, import_sun_sdk_sunswap_v4.computeV4PoolId)(poolKey.poolKey);
|
|
2359
|
+
const permitCalls = (input.permit2Signatures ?? []).map((permit) => (0, import_sun_sdk_sunswap_v4.encodeV4Permit2ForwardCall)({ owner, permit }));
|
|
2360
|
+
const transactionAction = input.transactionMode === "multicall" || permitCalls.length > 0 ? (0, import_sun_sdk_sunswap_v4.buildV4MulticallAction)({
|
|
2361
|
+
network: this.context.network,
|
|
2362
|
+
positionManager,
|
|
2363
|
+
calls: [...permitCalls, (0, import_sun_sdk_sunswap_v4.encodeV4ContractCallAction)(action)],
|
|
2364
|
+
...action.callValue !== void 0 ? { callValue: action.callValue } : {},
|
|
2365
|
+
...action.tokenRequirements !== void 0 ? { tokenRequirements: action.tokenRequirements } : {},
|
|
2366
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {},
|
|
2367
|
+
id: `${planId}-tx`
|
|
2368
|
+
}) : action;
|
|
2369
|
+
const transaction = {
|
|
2370
|
+
...transactionAction,
|
|
2371
|
+
waitForReceipt: "included",
|
|
2372
|
+
intent: {
|
|
2373
|
+
kind: "add-liquidity",
|
|
2374
|
+
scope,
|
|
2375
|
+
positionId: tokenId
|
|
2376
|
+
}
|
|
2377
|
+
};
|
|
2378
|
+
return {
|
|
2379
|
+
id: planId,
|
|
2380
|
+
kind: "liquidity",
|
|
2381
|
+
network: this.context.network,
|
|
2382
|
+
owner,
|
|
2383
|
+
summary: {
|
|
2384
|
+
title: "Increase SunSwap V4 liquidity",
|
|
2385
|
+
lines: [`Position ${tokenId}`, `Pool ${poolId}`]
|
|
2386
|
+
},
|
|
2387
|
+
approvals,
|
|
2388
|
+
transactions: [transaction],
|
|
2389
|
+
metadata: planMetadata({
|
|
2390
|
+
operation: "increase-liquidity",
|
|
2391
|
+
poolId,
|
|
2392
|
+
tokenId,
|
|
2393
|
+
owner,
|
|
2394
|
+
...sweepRecipient !== void 0 ? { sweepRecipient } : {},
|
|
2395
|
+
deadline,
|
|
2396
|
+
poolKeyDerived: poolKey.derived,
|
|
2397
|
+
amountsDerived: amounts.derived
|
|
2398
|
+
})
|
|
2399
|
+
};
|
|
2400
|
+
}
|
|
2401
|
+
};
|
|
2402
|
+
|
|
2403
|
+
// src/liquidity/client.ts
|
|
2404
|
+
var LiquidityClient = class {
|
|
2405
|
+
v4;
|
|
2406
|
+
v3;
|
|
2407
|
+
v2;
|
|
2408
|
+
constructor(context) {
|
|
2409
|
+
this.v2 = new V2LiquidityClient(context);
|
|
2410
|
+
const v3Client = new V3LiquidityClient(context);
|
|
2411
|
+
const v4Client = new V4LiquidityClient(context);
|
|
2412
|
+
this.v3 = {
|
|
2413
|
+
add: v3Client.add,
|
|
2414
|
+
increase: v3Client.increase
|
|
2415
|
+
};
|
|
2416
|
+
this.v4 = {
|
|
2417
|
+
initialize: v4Client.initialize,
|
|
2418
|
+
read: (input) => v4Client.read(input),
|
|
2419
|
+
add: v4Client.add,
|
|
2420
|
+
increase: v4Client.increase
|
|
2421
|
+
};
|
|
2422
|
+
}
|
|
2423
|
+
};
|
|
2424
|
+
function createLiquidityClient(context) {
|
|
2425
|
+
return new LiquidityClient(context);
|
|
2426
|
+
}
|
|
2427
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2428
|
+
0 && (module.exports = {
|
|
2429
|
+
LiquidityClient,
|
|
2430
|
+
SUNSWAP_V2_LIQUIDITY_PROTOCOL_ID,
|
|
2431
|
+
SUNSWAP_V3_LIQUIDITY_PROTOCOL_ID,
|
|
2432
|
+
SUNSWAP_V4_LIQUIDITY_PROTOCOL_ID,
|
|
2433
|
+
V2LiquidityClient,
|
|
2434
|
+
V3LiquidityClient,
|
|
2435
|
+
V4LiquidityClient,
|
|
2436
|
+
createLiquidityClient,
|
|
2437
|
+
createV2LiquidityClient,
|
|
2438
|
+
planV2AddLiquidity,
|
|
2439
|
+
planV2RemoveLiquidity
|
|
2440
|
+
});
|
|
2441
|
+
//# sourceMappingURL=index.cjs.map
|