@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,1209 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SUNSWAP_V2_LIQUIDITY_PROTOCOL_ID,
|
|
3
|
+
SUNSWAP_V4_LIQUIDITY_PROTOCOL_ID,
|
|
4
|
+
V3LiquidityClient,
|
|
5
|
+
liquidityIntentScope
|
|
6
|
+
} from "./chunk-L5TZCDPH.js";
|
|
7
|
+
import {
|
|
8
|
+
planRouterPermit2Authorization
|
|
9
|
+
} from "./chunk-OM4OW4KB.js";
|
|
10
|
+
import {
|
|
11
|
+
evmHexToTronAddress,
|
|
12
|
+
normalizeLiquidityPercentage,
|
|
13
|
+
normalizeProtocolToken,
|
|
14
|
+
planApproval,
|
|
15
|
+
readMulticallRequired,
|
|
16
|
+
readRequiredContract
|
|
17
|
+
} from "./chunk-C2YCVBPI.js";
|
|
18
|
+
import {
|
|
19
|
+
applySlippageMax,
|
|
20
|
+
attachApprovalIntent,
|
|
21
|
+
createContextExecutor,
|
|
22
|
+
executeTransactionPlan,
|
|
23
|
+
nextPlanId,
|
|
24
|
+
normalizeBaseUnitAmount,
|
|
25
|
+
normalizeDeadline,
|
|
26
|
+
normalizeRecipient,
|
|
27
|
+
parseSlippage,
|
|
28
|
+
requireWalletAddress,
|
|
29
|
+
requireWalletSnapshot,
|
|
30
|
+
resolveProtocolContract,
|
|
31
|
+
slippageToBips
|
|
32
|
+
} from "./chunk-VFVKMC4H.js";
|
|
33
|
+
|
|
34
|
+
// src/liquidity/v2.ts
|
|
35
|
+
import {
|
|
36
|
+
CurrencyAmount,
|
|
37
|
+
SDKError,
|
|
38
|
+
TRON_ZERO_ADDRESS,
|
|
39
|
+
assertTronAddress
|
|
40
|
+
} from "@sun-protocol/sun-sdk-core";
|
|
41
|
+
import {
|
|
42
|
+
Pair,
|
|
43
|
+
buildV2AddLiquidityAction,
|
|
44
|
+
buildV2AddLiquidityEthAction,
|
|
45
|
+
buildV2FactoryFeeToRead,
|
|
46
|
+
buildV2FactoryPairRead,
|
|
47
|
+
buildV2LpApproveAction,
|
|
48
|
+
buildV2PairAllowanceRead,
|
|
49
|
+
buildV2PairBalanceRead,
|
|
50
|
+
buildV2PairKLastRead,
|
|
51
|
+
buildV2PairReservesRead,
|
|
52
|
+
buildV2PairTotalSupplyRead,
|
|
53
|
+
buildV2RemoveLiquidityAction,
|
|
54
|
+
buildV2RemoveLiquidityEthAction,
|
|
55
|
+
computeV2PairAddress,
|
|
56
|
+
getV2AddLiquidityMinimums,
|
|
57
|
+
getV2RemoveLiquidityMinimums,
|
|
58
|
+
normalizeV2ReserveResult,
|
|
59
|
+
quoteV2LiquidityAmount
|
|
60
|
+
} from "@sun-protocol/sun-sdk-sunswap-v2";
|
|
61
|
+
var DEFAULT_SLIPPAGE = "0.5%";
|
|
62
|
+
function parseContractInteger(value, label) {
|
|
63
|
+
const normalized = Array.isArray(value) && value.length === 1 ? value[0] : value;
|
|
64
|
+
if (normalized === null || normalized === void 0 || typeof normalized !== "string" && typeof normalized !== "number" && typeof normalized !== "bigint" && typeof normalized !== "object") {
|
|
65
|
+
throw new SDKError("SDK_CONTRACT_ERROR", `${label} must be an integer-like contract result`, { value });
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const text = normalized.toString();
|
|
69
|
+
if (!/^\d+$/.test(text)) throw new Error("not an unsigned integer");
|
|
70
|
+
return BigInt(text).toString();
|
|
71
|
+
} catch {
|
|
72
|
+
throw new SDKError("SDK_CONTRACT_ERROR", `${label} must be an integer-like contract result`, { value });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function parseContractAddress(value, label) {
|
|
76
|
+
const normalized = Array.isArray(value) && value.length === 1 ? value[0] : value;
|
|
77
|
+
if (typeof normalized !== "string") {
|
|
78
|
+
throw new SDKError("SDK_CONTRACT_ERROR", `${label} must be an address contract result`, { value });
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
return /^0x[0-9a-fA-F]{40}$/.test(normalized) ? evmHexToTronAddress(normalized, label) : assertTronAddress(normalized, label);
|
|
82
|
+
} catch {
|
|
83
|
+
throw new SDKError("SDK_CONTRACT_ERROR", `${label} must be an address contract result`, { value });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function createPairContext(context, tokenAInput, tokenBInput) {
|
|
87
|
+
const tokenA = normalizeProtocolToken(context, tokenAInput, "tokenA");
|
|
88
|
+
const tokenB = normalizeProtocolToken(context, tokenBInput, "tokenB");
|
|
89
|
+
if (tokenA.token.equals(tokenB.token)) {
|
|
90
|
+
throw new SDKError("SDK_INVALID_ARGUMENT", "V2 liquidity tokens must be different", {
|
|
91
|
+
tokenA: tokenAInput,
|
|
92
|
+
tokenB: tokenBInput
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
tokenA,
|
|
97
|
+
tokenB,
|
|
98
|
+
tokenAContract: tokenA.kind === "native" ? tokenA.wrappedContractAddress : tokenA.contractAddress,
|
|
99
|
+
tokenBContract: tokenB.kind === "native" ? tokenB.wrappedContractAddress : tokenB.contractAddress,
|
|
100
|
+
pair: computeV2PairAddress({
|
|
101
|
+
network: context.network,
|
|
102
|
+
tokenA: tokenA.kind === "native" ? tokenA.wrappedContractAddress : tokenA.contractAddress,
|
|
103
|
+
tokenB: tokenB.kind === "native" ? tokenB.wrappedContractAddress : tokenB.contractAddress
|
|
104
|
+
}),
|
|
105
|
+
tokenASortsBefore: tokenA.token.sortsBefore(tokenB.token)
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function parsePairReserves(result, tokenASortsBefore) {
|
|
109
|
+
if (!Array.isArray(result) || result.length < 2) {
|
|
110
|
+
throw new SDKError("SDK_CONTRACT_ERROR", "V2 pair reserves result is malformed", { result });
|
|
111
|
+
}
|
|
112
|
+
let normalized;
|
|
113
|
+
try {
|
|
114
|
+
normalized = normalizeV2ReserveResult(result);
|
|
115
|
+
} catch {
|
|
116
|
+
throw new SDKError("SDK_CONTRACT_ERROR", "V2 pair reserves result is malformed", { result });
|
|
117
|
+
}
|
|
118
|
+
const reserve0 = parseContractInteger(normalized.reserve0, "reserve0");
|
|
119
|
+
const reserve1 = parseContractInteger(normalized.reserve1, "reserve1");
|
|
120
|
+
return tokenASortsBefore ? { reserveA: reserve0, reserveB: reserve1 } : { reserveA: reserve1, reserveB: reserve0 };
|
|
121
|
+
}
|
|
122
|
+
function resolveAddAmounts(input) {
|
|
123
|
+
const reserveA = BigInt(input.reserves.reserveA);
|
|
124
|
+
const reserveB = BigInt(input.reserves.reserveB);
|
|
125
|
+
if (reserveA === 0n && reserveB === 0n) {
|
|
126
|
+
return { amountA: input.amountADesired, amountB: input.amountBDesired };
|
|
127
|
+
}
|
|
128
|
+
if (reserveA === 0n || reserveB === 0n) {
|
|
129
|
+
throw new SDKError("SDK_CONTRACT_ERROR", "V2 pair has inconsistent zero reserves", {
|
|
130
|
+
reserveA: input.reserves.reserveA,
|
|
131
|
+
reserveB: input.reserves.reserveB
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
const amountBOptimal = quoteV2LiquidityAmount(
|
|
135
|
+
input.amountADesired,
|
|
136
|
+
input.reserves.reserveA,
|
|
137
|
+
input.reserves.reserveB
|
|
138
|
+
);
|
|
139
|
+
if (BigInt(amountBOptimal) <= BigInt(input.amountBDesired)) {
|
|
140
|
+
return { amountA: input.amountADesired, amountB: amountBOptimal };
|
|
141
|
+
}
|
|
142
|
+
const amountAOptimal = quoteV2LiquidityAmount(
|
|
143
|
+
input.amountBDesired,
|
|
144
|
+
input.reserves.reserveB,
|
|
145
|
+
input.reserves.reserveA
|
|
146
|
+
);
|
|
147
|
+
if (BigInt(amountAOptimal) > BigInt(input.amountADesired)) {
|
|
148
|
+
throw new SDKError("SDK_CONTRACT_ERROR", "V2 pair reserve quote exceeds desired liquidity amounts", {
|
|
149
|
+
amountADesired: input.amountADesired,
|
|
150
|
+
amountBDesired: input.amountBDesired,
|
|
151
|
+
amountAOptimal,
|
|
152
|
+
amountBOptimal
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return { amountA: amountAOptimal, amountB: input.amountBDesired };
|
|
156
|
+
}
|
|
157
|
+
function createPairEntity(pair, reserves) {
|
|
158
|
+
return new Pair(
|
|
159
|
+
CurrencyAmount.fromRawAmount(pair.tokenA.token, reserves.reserveA),
|
|
160
|
+
CurrencyAmount.fromRawAmount(pair.tokenB.token, reserves.reserveB)
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
function validateAddAmounts(pair, reserves, amounts) {
|
|
164
|
+
if (BigInt(amounts.amountA) === 0n || BigInt(amounts.amountB) === 0n) {
|
|
165
|
+
throw new SDKError("SDK_INVALID_ARGUMENT", "V2 add liquidity amounts must remain positive after reserve optimization", {
|
|
166
|
+
...amounts,
|
|
167
|
+
...reserves
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
if (BigInt(reserves.reserveA) !== 0n || BigInt(reserves.reserveB) !== 0n) return;
|
|
171
|
+
const pairEntity = createPairEntity(pair, reserves);
|
|
172
|
+
try {
|
|
173
|
+
pairEntity.getLiquidityMinted(
|
|
174
|
+
CurrencyAmount.fromRawAmount(pairEntity.liquidityToken, "0"),
|
|
175
|
+
CurrencyAmount.fromRawAmount(pair.tokenA.token, amounts.amountA),
|
|
176
|
+
CurrencyAmount.fromRawAmount(pair.tokenB.token, amounts.amountB)
|
|
177
|
+
);
|
|
178
|
+
} catch {
|
|
179
|
+
throw new SDKError("SDK_INVALID_ARGUMENT", "Initial V2 liquidity is too small to exceed MINIMUM_LIQUIDITY", {
|
|
180
|
+
...amounts
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async function planTokenApproval(input) {
|
|
185
|
+
if (input.token.kind === "native") return [];
|
|
186
|
+
const approval = await planApproval(
|
|
187
|
+
input.context,
|
|
188
|
+
{
|
|
189
|
+
asset: { type: "token", token: input.token.contractAddress },
|
|
190
|
+
owner: input.owner,
|
|
191
|
+
spender: input.router,
|
|
192
|
+
amount: input.amount
|
|
193
|
+
},
|
|
194
|
+
{ mode: input.mode ?? "auto" }
|
|
195
|
+
);
|
|
196
|
+
return attachApprovalIntent(
|
|
197
|
+
approval.actions,
|
|
198
|
+
liquidityIntentScope(SUNSWAP_V2_LIQUIDITY_PROTOCOL_ID, "add-liquidity"),
|
|
199
|
+
input.token.token.symbol ?? input.token.input
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
function buildAddAction(input) {
|
|
203
|
+
const options = {
|
|
204
|
+
network: input.context.network,
|
|
205
|
+
router: input.router,
|
|
206
|
+
recipient: input.recipient,
|
|
207
|
+
deadline: input.deadline,
|
|
208
|
+
id: `${input.planId}-tx`,
|
|
209
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {}
|
|
210
|
+
};
|
|
211
|
+
const action = input.pair.tokenA.kind === "native" ? buildV2AddLiquidityEthAction({
|
|
212
|
+
...options,
|
|
213
|
+
token: input.pair.tokenB.token,
|
|
214
|
+
amountTokenDesired: input.amountB,
|
|
215
|
+
amountEthDesired: input.amountA,
|
|
216
|
+
amountTokenMin: input.amountBMin,
|
|
217
|
+
amountEthMin: input.amountAMin
|
|
218
|
+
}) : input.pair.tokenB.kind === "native" ? buildV2AddLiquidityEthAction({
|
|
219
|
+
...options,
|
|
220
|
+
token: input.pair.tokenA.token,
|
|
221
|
+
amountTokenDesired: input.amountA,
|
|
222
|
+
amountEthDesired: input.amountB,
|
|
223
|
+
amountTokenMin: input.amountAMin,
|
|
224
|
+
amountEthMin: input.amountBMin
|
|
225
|
+
}) : buildV2AddLiquidityAction({
|
|
226
|
+
...options,
|
|
227
|
+
tokenA: input.pair.tokenA.token,
|
|
228
|
+
tokenB: input.pair.tokenB.token,
|
|
229
|
+
amountADesired: input.amountA,
|
|
230
|
+
amountBDesired: input.amountB,
|
|
231
|
+
amountAMin: input.amountAMin,
|
|
232
|
+
amountBMin: input.amountBMin
|
|
233
|
+
});
|
|
234
|
+
const intent = {
|
|
235
|
+
kind: "add-liquidity",
|
|
236
|
+
scope: liquidityIntentScope(SUNSWAP_V2_LIQUIDITY_PROTOCOL_ID, "add-liquidity")
|
|
237
|
+
};
|
|
238
|
+
return {
|
|
239
|
+
...action,
|
|
240
|
+
intent
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function buildRemoveAction(input) {
|
|
244
|
+
const options = {
|
|
245
|
+
network: input.context.network,
|
|
246
|
+
router: input.router,
|
|
247
|
+
liquidity: input.liquidity,
|
|
248
|
+
recipient: input.recipient,
|
|
249
|
+
deadline: input.deadline,
|
|
250
|
+
id: `${input.planId}-tx`,
|
|
251
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {}
|
|
252
|
+
};
|
|
253
|
+
let action;
|
|
254
|
+
if (input.pair.tokenA.kind === "native") {
|
|
255
|
+
action = buildV2RemoveLiquidityEthAction({
|
|
256
|
+
...options,
|
|
257
|
+
token: input.pair.tokenB.token,
|
|
258
|
+
amountTokenMin: input.amountBMin,
|
|
259
|
+
amountEthMin: input.amountAMin
|
|
260
|
+
});
|
|
261
|
+
} else if (input.pair.tokenB.kind === "native") {
|
|
262
|
+
action = buildV2RemoveLiquidityEthAction({
|
|
263
|
+
...options,
|
|
264
|
+
token: input.pair.tokenA.token,
|
|
265
|
+
amountTokenMin: input.amountAMin,
|
|
266
|
+
amountEthMin: input.amountBMin
|
|
267
|
+
});
|
|
268
|
+
} else {
|
|
269
|
+
action = buildV2RemoveLiquidityAction({
|
|
270
|
+
...options,
|
|
271
|
+
tokenA: input.pair.tokenA.token,
|
|
272
|
+
tokenB: input.pair.tokenB.token,
|
|
273
|
+
amountAMin: input.amountAMin,
|
|
274
|
+
amountBMin: input.amountBMin
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
...action,
|
|
279
|
+
intent: {
|
|
280
|
+
kind: "remove-liquidity",
|
|
281
|
+
scope: liquidityIntentScope(SUNSWAP_V2_LIQUIDITY_PROTOCOL_ID, "remove-liquidity")
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function resolveRemoveLiquidity(input, balance) {
|
|
286
|
+
const hasLiquidity = input.liquidity !== void 0;
|
|
287
|
+
const hasPercentage = input.percentage !== void 0;
|
|
288
|
+
if (hasLiquidity === hasPercentage) {
|
|
289
|
+
throw new SDKError("SDK_INVALID_ARGUMENT", "V2 remove requires exactly one of liquidity or percentage");
|
|
290
|
+
}
|
|
291
|
+
const liquidity = hasLiquidity ? BigInt(normalizeBaseUnitAmount(input.liquidity, "liquidity")) : (() => {
|
|
292
|
+
const percentage = normalizeLiquidityPercentage(input.percentage, "percentage");
|
|
293
|
+
const numerator = BigInt(percentage.numerator.toString());
|
|
294
|
+
const denominator = BigInt(percentage.denominator.toString());
|
|
295
|
+
const result = BigInt(balance) * numerator / denominator;
|
|
296
|
+
if (result <= 0n) {
|
|
297
|
+
throw new SDKError("SDK_INVALID_ARGUMENT", "percentage resolves to zero liquidity", {
|
|
298
|
+
balance,
|
|
299
|
+
percentage: percentage.toSignificant()
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
return result;
|
|
303
|
+
})();
|
|
304
|
+
if (liquidity > BigInt(balance)) {
|
|
305
|
+
throw new SDKError("SDK_INVALID_ARGUMENT", "liquidity cannot exceed wallet LP balance", {
|
|
306
|
+
liquidity: liquidity.toString(),
|
|
307
|
+
balance
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
return liquidity.toString();
|
|
311
|
+
}
|
|
312
|
+
async function planV2AddLiquidity(context, input) {
|
|
313
|
+
const router = resolveProtocolContract(context, input.contracts, "sunswapV2Router");
|
|
314
|
+
const planId = nextPlanId(context, "liquidity-v2-add");
|
|
315
|
+
const owner = await requireWalletAddress(context, "V2 add liquidity");
|
|
316
|
+
const recipient = await normalizeRecipient(context, input.recipient, "V2 add liquidity", owner);
|
|
317
|
+
const deadline = normalizeDeadline(context, input);
|
|
318
|
+
const slippageInput = input.slippage ?? DEFAULT_SLIPPAGE;
|
|
319
|
+
const slippage = parseSlippage(slippageInput);
|
|
320
|
+
const pair = createPairContext(context, input.tokenA, input.tokenB);
|
|
321
|
+
const amountADesired = normalizeBaseUnitAmount(input.amountADesired, "amountADesired");
|
|
322
|
+
const amountBDesired = normalizeBaseUnitAmount(input.amountBDesired, "amountBDesired");
|
|
323
|
+
const deployedPair = parseContractAddress(await readRequiredContract(
|
|
324
|
+
context,
|
|
325
|
+
buildV2FactoryPairRead({
|
|
326
|
+
network: context.network,
|
|
327
|
+
tokenA: pair.tokenAContract,
|
|
328
|
+
tokenB: pair.tokenBContract
|
|
329
|
+
}),
|
|
330
|
+
"V2 add liquidity pair lookup"
|
|
331
|
+
), "V2 factory pair");
|
|
332
|
+
if (deployedPair !== TRON_ZERO_ADDRESS && deployedPair !== pair.pair) {
|
|
333
|
+
throw new SDKError("SDK_CONTRACT_ERROR", "V2 factory pair does not match the configured CREATE2 pair", {
|
|
334
|
+
factoryPair: deployedPair,
|
|
335
|
+
computedPair: pair.pair
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
const reserves = deployedPair === TRON_ZERO_ADDRESS ? { reserveA: "0", reserveB: "0" } : parsePairReserves(await readRequiredContract(
|
|
339
|
+
context,
|
|
340
|
+
buildV2PairReservesRead(pair.pair),
|
|
341
|
+
"V2 add liquidity reserves"
|
|
342
|
+
), pair.tokenASortsBefore);
|
|
343
|
+
const amounts = resolveAddAmounts({ amountADesired, amountBDesired, reserves });
|
|
344
|
+
validateAddAmounts(pair, reserves, amounts);
|
|
345
|
+
const slippageMinimums = getV2AddLiquidityMinimums(amounts, slippage);
|
|
346
|
+
const minimums = {
|
|
347
|
+
amountAMin: input.amountAMin === void 0 ? slippageMinimums.amountAMin : normalizeBaseUnitAmount(input.amountAMin, "amountAMin", { allowZero: true }),
|
|
348
|
+
amountBMin: input.amountBMin === void 0 ? slippageMinimums.amountBMin : normalizeBaseUnitAmount(input.amountBMin, "amountBMin", { allowZero: true })
|
|
349
|
+
};
|
|
350
|
+
const approvals = [
|
|
351
|
+
...await planTokenApproval({ context, owner, token: pair.tokenA, amount: amounts.amountA, mode: input.approval, router }),
|
|
352
|
+
...await planTokenApproval({ context, owner, token: pair.tokenB, amount: amounts.amountB, mode: input.approval, router })
|
|
353
|
+
];
|
|
354
|
+
const transaction = buildAddAction({
|
|
355
|
+
context,
|
|
356
|
+
planId,
|
|
357
|
+
pair,
|
|
358
|
+
amountA: amounts.amountA,
|
|
359
|
+
amountB: amounts.amountB,
|
|
360
|
+
amountAMin: minimums.amountAMin,
|
|
361
|
+
amountBMin: minimums.amountBMin,
|
|
362
|
+
recipient,
|
|
363
|
+
deadline,
|
|
364
|
+
router,
|
|
365
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {}
|
|
366
|
+
});
|
|
367
|
+
return {
|
|
368
|
+
id: planId,
|
|
369
|
+
kind: "liquidity",
|
|
370
|
+
network: context.network,
|
|
371
|
+
owner,
|
|
372
|
+
summary: {
|
|
373
|
+
title: "Add SunSwap V2 liquidity",
|
|
374
|
+
lines: [`Pair ${pair.pair}`, `Recipient ${recipient}`]
|
|
375
|
+
},
|
|
376
|
+
approvals,
|
|
377
|
+
transactions: [transaction],
|
|
378
|
+
metadata: {
|
|
379
|
+
protocol: "sunswap-v2",
|
|
380
|
+
operation: "add-liquidity",
|
|
381
|
+
pair: pair.pair,
|
|
382
|
+
tokenA: input.tokenA,
|
|
383
|
+
tokenB: input.tokenB,
|
|
384
|
+
amountADesired,
|
|
385
|
+
amountBDesired,
|
|
386
|
+
amountA: amounts.amountA,
|
|
387
|
+
amountB: amounts.amountB,
|
|
388
|
+
amountAMin: minimums.amountAMin,
|
|
389
|
+
amountBMin: minimums.amountBMin,
|
|
390
|
+
owner,
|
|
391
|
+
recipient,
|
|
392
|
+
deadline,
|
|
393
|
+
slippageBips: slippageToBips(slippageInput),
|
|
394
|
+
approvalMode: input.approval ?? "auto"
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
async function planV2RemoveLiquidity(context, input) {
|
|
399
|
+
const router = resolveProtocolContract(context, input.contracts, "sunswapV2Router");
|
|
400
|
+
const planId = nextPlanId(context, "liquidity-v2-remove");
|
|
401
|
+
const owner = await requireWalletAddress(context, "V2 remove liquidity");
|
|
402
|
+
const recipient = await normalizeRecipient(context, input.recipient, "V2 remove liquidity", owner);
|
|
403
|
+
const deadline = normalizeDeadline(context, input);
|
|
404
|
+
const slippageInput = input.slippage ?? DEFAULT_SLIPPAGE;
|
|
405
|
+
const slippage = parseSlippage(slippageInput);
|
|
406
|
+
const approvalMode = input.approval ?? "auto";
|
|
407
|
+
if (approvalMode === "permit2") {
|
|
408
|
+
throw new SDKError("SDK_WALLET_CAPABILITY_ERROR", "Permit2 cannot authorize a SunSwap V2 LP token", {
|
|
409
|
+
approvalMode
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
const pair = createPairContext(context, input.tokenA, input.tokenB);
|
|
413
|
+
const [reservesResult, totalSupplyResult, balanceResult, feeToResult, kLastResult] = await readMulticallRequired(
|
|
414
|
+
context,
|
|
415
|
+
[
|
|
416
|
+
buildV2PairReservesRead(pair.pair),
|
|
417
|
+
buildV2PairTotalSupplyRead(pair.pair),
|
|
418
|
+
buildV2PairBalanceRead(pair.pair, owner),
|
|
419
|
+
buildV2FactoryFeeToRead({ network: context.network }),
|
|
420
|
+
buildV2PairKLastRead(pair.pair)
|
|
421
|
+
],
|
|
422
|
+
"V2 remove liquidity state"
|
|
423
|
+
);
|
|
424
|
+
const reserves = parsePairReserves(reservesResult, pair.tokenASortsBefore);
|
|
425
|
+
const totalSupply = parseContractInteger(totalSupplyResult, "totalSupply");
|
|
426
|
+
const balance = parseContractInteger(balanceResult, "LP balance");
|
|
427
|
+
const feeTo = parseContractAddress(feeToResult, "V2 factory feeTo");
|
|
428
|
+
const kLast = parseContractInteger(kLastResult, "kLast");
|
|
429
|
+
const feeOn = feeTo !== TRON_ZERO_ADDRESS;
|
|
430
|
+
if (BigInt(totalSupply) === 0n) {
|
|
431
|
+
throw new SDKError("SDK_CONTRACT_ERROR", "V2 pair total supply must be positive", {
|
|
432
|
+
pair: pair.pair,
|
|
433
|
+
totalSupply
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
if (BigInt(reserves.reserveA) === 0n || BigInt(reserves.reserveB) === 0n) {
|
|
437
|
+
throw new SDKError("SDK_CONTRACT_ERROR", "V2 pair reserves must be positive for removal", {
|
|
438
|
+
pair: pair.pair,
|
|
439
|
+
reserveA: reserves.reserveA,
|
|
440
|
+
reserveB: reserves.reserveB
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
const liquidity = resolveRemoveLiquidity(input, balance);
|
|
444
|
+
if (BigInt(liquidity) > BigInt(totalSupply)) {
|
|
445
|
+
throw new SDKError("SDK_CONTRACT_ERROR", "V2 LP balance exceeds pair total supply", {
|
|
446
|
+
liquidity,
|
|
447
|
+
totalSupply
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
const pairEntity = createPairEntity(pair, reserves);
|
|
451
|
+
const totalSupplyAmount = CurrencyAmount.fromRawAmount(pairEntity.liquidityToken, totalSupply);
|
|
452
|
+
const liquidityAmount = CurrencyAmount.fromRawAmount(pairEntity.liquidityToken, liquidity);
|
|
453
|
+
const amountA = pairEntity.getLiquidityValue(
|
|
454
|
+
pair.tokenA.token,
|
|
455
|
+
totalSupplyAmount,
|
|
456
|
+
liquidityAmount,
|
|
457
|
+
feeOn,
|
|
458
|
+
kLast
|
|
459
|
+
).quotient.toString();
|
|
460
|
+
const amountB = pairEntity.getLiquidityValue(
|
|
461
|
+
pair.tokenB.token,
|
|
462
|
+
totalSupplyAmount,
|
|
463
|
+
liquidityAmount,
|
|
464
|
+
feeOn,
|
|
465
|
+
kLast
|
|
466
|
+
).quotient.toString();
|
|
467
|
+
if (BigInt(amountA) === 0n || BigInt(amountB) === 0n) {
|
|
468
|
+
throw new SDKError("SDK_INVALID_ARGUMENT", "V2 remove liquidity resolves to a zero token amount", {
|
|
469
|
+
liquidity,
|
|
470
|
+
amountA,
|
|
471
|
+
amountB
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
const minimums = getV2RemoveLiquidityMinimums({ amountA, amountB }, slippage);
|
|
475
|
+
const approvals = [];
|
|
476
|
+
if (approvalMode !== "none") {
|
|
477
|
+
const allowance = parseContractInteger(
|
|
478
|
+
await readRequiredContract(
|
|
479
|
+
context,
|
|
480
|
+
buildV2PairAllowanceRead(pair.pair, owner, router),
|
|
481
|
+
"V2 LP allowance"
|
|
482
|
+
),
|
|
483
|
+
"LP allowance"
|
|
484
|
+
);
|
|
485
|
+
if (BigInt(allowance) < BigInt(liquidity)) {
|
|
486
|
+
approvals.push({
|
|
487
|
+
...buildV2LpApproveAction({
|
|
488
|
+
pair: pair.pair,
|
|
489
|
+
spender: router,
|
|
490
|
+
amount: liquidity,
|
|
491
|
+
id: `${planId}-approve`,
|
|
492
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {}
|
|
493
|
+
}),
|
|
494
|
+
intent: {
|
|
495
|
+
kind: "approve",
|
|
496
|
+
scope: liquidityIntentScope(SUNSWAP_V2_LIQUIDITY_PROTOCOL_ID, "remove-liquidity"),
|
|
497
|
+
tokenSymbol: "LP"
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
const transaction = buildRemoveAction({
|
|
503
|
+
context,
|
|
504
|
+
planId,
|
|
505
|
+
pair,
|
|
506
|
+
liquidity,
|
|
507
|
+
amountAMin: minimums.amountAMin,
|
|
508
|
+
amountBMin: minimums.amountBMin,
|
|
509
|
+
recipient,
|
|
510
|
+
deadline,
|
|
511
|
+
router,
|
|
512
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {}
|
|
513
|
+
});
|
|
514
|
+
return {
|
|
515
|
+
id: planId,
|
|
516
|
+
kind: "liquidity",
|
|
517
|
+
network: context.network,
|
|
518
|
+
owner,
|
|
519
|
+
summary: {
|
|
520
|
+
title: "Remove SunSwap V2 liquidity",
|
|
521
|
+
lines: [`Pair ${pair.pair}`, `LP liquidity ${liquidity}`, `Recipient ${recipient}`]
|
|
522
|
+
},
|
|
523
|
+
approvals,
|
|
524
|
+
transactions: [transaction],
|
|
525
|
+
metadata: {
|
|
526
|
+
protocol: "sunswap-v2",
|
|
527
|
+
operation: "remove-liquidity",
|
|
528
|
+
pair: pair.pair,
|
|
529
|
+
tokenA: input.tokenA,
|
|
530
|
+
tokenB: input.tokenB,
|
|
531
|
+
liquidity,
|
|
532
|
+
amountA,
|
|
533
|
+
amountB,
|
|
534
|
+
amountAMin: minimums.amountAMin,
|
|
535
|
+
amountBMin: minimums.amountBMin,
|
|
536
|
+
owner,
|
|
537
|
+
recipient,
|
|
538
|
+
deadline,
|
|
539
|
+
slippageBips: slippageToBips(slippageInput),
|
|
540
|
+
approvalMode,
|
|
541
|
+
feeOn,
|
|
542
|
+
feeTo,
|
|
543
|
+
kLast
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
var V2LiquidityClient = class {
|
|
548
|
+
constructor(context) {
|
|
549
|
+
this.context = context;
|
|
550
|
+
}
|
|
551
|
+
context;
|
|
552
|
+
add = {
|
|
553
|
+
plan: (input) => planV2AddLiquidity(this.context, input),
|
|
554
|
+
execute: async (input) => executeTransactionPlan(await planV2AddLiquidity(this.context, input), createContextExecutor(this.context))
|
|
555
|
+
};
|
|
556
|
+
remove = {
|
|
557
|
+
plan: (input) => planV2RemoveLiquidity(this.context, input),
|
|
558
|
+
execute: async (input) => executeTransactionPlan(await planV2RemoveLiquidity(this.context, input), createContextExecutor(this.context))
|
|
559
|
+
};
|
|
560
|
+
};
|
|
561
|
+
function createV2LiquidityClient(context) {
|
|
562
|
+
return new V2LiquidityClient(context);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// src/liquidity/v4.ts
|
|
566
|
+
import {
|
|
567
|
+
SDKError as SDKError2,
|
|
568
|
+
TRON_ZERO_ADDRESS as TRON_ZERO_ADDRESS2,
|
|
569
|
+
assertTronAddress as assertTronAddress2
|
|
570
|
+
} from "@sun-protocol/sun-sdk-core";
|
|
571
|
+
import { TickMath } from "@sun-protocol/sun-sdk-sunswap-v3";
|
|
572
|
+
import {
|
|
573
|
+
assertV4PoolId,
|
|
574
|
+
buildV4IncreaseLiquidityAction,
|
|
575
|
+
buildV4HookRegistrationBitmapRead,
|
|
576
|
+
buildV4InitializePoolAction,
|
|
577
|
+
buildV4MintPositionAction,
|
|
578
|
+
buildV4MulticallAction,
|
|
579
|
+
buildV4PoolAndPositionInfoRead,
|
|
580
|
+
buildV4PoolSlot0Read,
|
|
581
|
+
buildV4PoolStateReads,
|
|
582
|
+
computeV4PoolId,
|
|
583
|
+
createV4PoolKey,
|
|
584
|
+
decodeV4HooksRegistration,
|
|
585
|
+
encodeV4PoolParameters,
|
|
586
|
+
encodeV4ContractCallAction,
|
|
587
|
+
encodeV4Permit2ForwardCall
|
|
588
|
+
} from "@sun-protocol/sun-sdk-sunswap-v4";
|
|
589
|
+
var Q96 = 1n << 96n;
|
|
590
|
+
function parseString(value, label) {
|
|
591
|
+
if (typeof value === "bigint" || typeof value === "number" || typeof value === "string") {
|
|
592
|
+
return BigInt(value.toString()).toString();
|
|
593
|
+
}
|
|
594
|
+
throw new SDKError2("SDK_CONTRACT_ERROR", `${label} must be an integer-like contract result`, { value });
|
|
595
|
+
}
|
|
596
|
+
function parseNumber(value, label) {
|
|
597
|
+
const parsed = Number(parseString(value, label));
|
|
598
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
599
|
+
throw new SDKError2("SDK_CONTRACT_ERROR", `${label} must be a safe integer`, { value });
|
|
600
|
+
}
|
|
601
|
+
return parsed;
|
|
602
|
+
}
|
|
603
|
+
function unwrapSingle(value) {
|
|
604
|
+
return Array.isArray(value) && value.length === 1 ? value[0] : value;
|
|
605
|
+
}
|
|
606
|
+
function signed24(value) {
|
|
607
|
+
const masked = value & 0xffffffn;
|
|
608
|
+
return Number((masked & 0x800000n) === 0n ? masked : masked - 0x1000000n);
|
|
609
|
+
}
|
|
610
|
+
function parsePackedPositionTicks(value) {
|
|
611
|
+
const info = BigInt(String(unwrapSingle(value)));
|
|
612
|
+
return {
|
|
613
|
+
tickLower: signed24(info >> 8n),
|
|
614
|
+
tickUpper: signed24(info >> 32n)
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
function parseV4Address(value, label) {
|
|
618
|
+
const raw = String(unwrapSingle(value));
|
|
619
|
+
try {
|
|
620
|
+
return assertTronAddress2(raw, label);
|
|
621
|
+
} catch {
|
|
622
|
+
const evmHex = /^41[0-9a-fA-F]{40}$/.test(raw) ? `0x${raw.slice(2)}` : raw;
|
|
623
|
+
return evmHexToTronAddress(evmHex, label);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
function parsePositionPoolKey(value, network) {
|
|
627
|
+
if (Array.isArray(value) && value.length >= 5) {
|
|
628
|
+
return createV4PoolKey({
|
|
629
|
+
network,
|
|
630
|
+
currencyA: parseV4Address(value[0], "poolKey.currency0"),
|
|
631
|
+
currencyB: parseV4Address(value[1], "poolKey.currency1"),
|
|
632
|
+
hooks: parseV4Address(value[2], "poolKey.hooks"),
|
|
633
|
+
fee: parseNumber(value[3], "poolKey.fee"),
|
|
634
|
+
parameters: String(value[4])
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
if (typeof value === "object" && value !== null) {
|
|
638
|
+
const key = value;
|
|
639
|
+
if (key.currency0 !== void 0 && key.currency1 !== void 0 && key.hooks !== void 0 && key.fee !== void 0 && key.parameters !== void 0) {
|
|
640
|
+
return createV4PoolKey({
|
|
641
|
+
network,
|
|
642
|
+
currencyA: parseV4Address(key.currency0, "poolKey.currency0"),
|
|
643
|
+
currencyB: parseV4Address(key.currency1, "poolKey.currency1"),
|
|
644
|
+
hooks: parseV4Address(key.hooks, "poolKey.hooks"),
|
|
645
|
+
fee: parseNumber(key.fee, "poolKey.fee"),
|
|
646
|
+
parameters: String(key.parameters)
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
throw new SDKError2("SDK_CONTRACT_ERROR", "V4 position context is missing poolKey", { value });
|
|
651
|
+
}
|
|
652
|
+
function parseSlot0(result) {
|
|
653
|
+
if (Array.isArray(result)) {
|
|
654
|
+
return {
|
|
655
|
+
sqrtPriceX96: parseString(result[0], "sqrtPriceX96"),
|
|
656
|
+
tick: parseNumber(result[1], "tick")
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
if (typeof result === "object" && result !== null) {
|
|
660
|
+
const slot0 = result;
|
|
661
|
+
return {
|
|
662
|
+
sqrtPriceX96: parseString(slot0.sqrtPriceX96, "sqrtPriceX96"),
|
|
663
|
+
tick: parseNumber(slot0.tick, "tick")
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
throw new SDKError2("SDK_CONTRACT_ERROR", "V4 slot0 result is malformed", { result });
|
|
667
|
+
}
|
|
668
|
+
async function resolveV4PoolKey(context, input) {
|
|
669
|
+
if (input.poolKey !== void 0) {
|
|
670
|
+
return { poolKey: input.poolKey, derived: false, amountsSwapped: false };
|
|
671
|
+
}
|
|
672
|
+
if (input.token0 === void 0 || input.token1 === void 0 || input.fee === void 0 || input.tickSpacing === void 0) {
|
|
673
|
+
throw new SDKError2("SDK_INVALID_ARGUMENT", "V4 poolKey or complete token pool inputs are required");
|
|
674
|
+
}
|
|
675
|
+
const token0 = normalizeProtocolToken(context, input.token0, "token0").contractAddress;
|
|
676
|
+
const token1 = normalizeProtocolToken(context, input.token1, "token1").contractAddress;
|
|
677
|
+
const hooks = assertTronAddress2(input.hooks ?? TRON_ZERO_ADDRESS2, "hooks");
|
|
678
|
+
if (input.dynamicFee && hooks === TRON_ZERO_ADDRESS2) {
|
|
679
|
+
throw new SDKError2("SDK_INVALID_ARGUMENT", "Dynamic V4 fees require a non-zero hooks contract");
|
|
680
|
+
}
|
|
681
|
+
const hooksRegistration = input.hooksRegistration ?? (hooks === TRON_ZERO_ADDRESS2 ? 0 : parseNumber(
|
|
682
|
+
unwrapSingle(await readRequiredContract(
|
|
683
|
+
context,
|
|
684
|
+
buildV4HookRegistrationBitmapRead({ network: context.network, hooks }),
|
|
685
|
+
"V4 hook registration read"
|
|
686
|
+
)),
|
|
687
|
+
"hooksRegistration"
|
|
688
|
+
));
|
|
689
|
+
const poolKey = createV4PoolKey({
|
|
690
|
+
network: context.network,
|
|
691
|
+
currencyA: token0,
|
|
692
|
+
currencyB: token1,
|
|
693
|
+
hooks,
|
|
694
|
+
fee: input.dynamicFee ? 8388608 : input.fee,
|
|
695
|
+
parameters: encodeV4PoolParameters({
|
|
696
|
+
tickSpacing: input.tickSpacing,
|
|
697
|
+
hooksRegistration: decodeV4HooksRegistration(hooksRegistration)
|
|
698
|
+
})
|
|
699
|
+
});
|
|
700
|
+
return {
|
|
701
|
+
poolKey,
|
|
702
|
+
derived: true,
|
|
703
|
+
amountsSwapped: poolKey.currency0 !== token0
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
function parseV4PositionContext(result, network) {
|
|
707
|
+
let poolKey;
|
|
708
|
+
let tickLower;
|
|
709
|
+
let tickUpper;
|
|
710
|
+
if (Array.isArray(result)) {
|
|
711
|
+
poolKey = result[0];
|
|
712
|
+
if (result[2] === void 0 && result[1] !== void 0) {
|
|
713
|
+
const ticks = parsePackedPositionTicks(result[1]);
|
|
714
|
+
tickLower = ticks.tickLower;
|
|
715
|
+
tickUpper = ticks.tickUpper;
|
|
716
|
+
} else {
|
|
717
|
+
tickLower = result[1];
|
|
718
|
+
tickUpper = result[2];
|
|
719
|
+
}
|
|
720
|
+
} else if (typeof result === "object" && result !== null) {
|
|
721
|
+
const info = result;
|
|
722
|
+
poolKey = info.poolKey;
|
|
723
|
+
tickLower = info.tickLower;
|
|
724
|
+
tickUpper = info.tickUpper;
|
|
725
|
+
} else {
|
|
726
|
+
throw new SDKError2("SDK_CONTRACT_ERROR", "V4 position context result is malformed", { result });
|
|
727
|
+
}
|
|
728
|
+
return {
|
|
729
|
+
poolKey: parsePositionPoolKey(poolKey, network),
|
|
730
|
+
tickLower: parseNumber(tickLower, "tickLower"),
|
|
731
|
+
tickUpper: parseNumber(tickUpper, "tickUpper")
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
function assertV4PoolKeyMatches(tokenId, supplied, position) {
|
|
735
|
+
const suppliedPoolId = computeV4PoolId(supplied);
|
|
736
|
+
const positionPoolId = computeV4PoolId(position);
|
|
737
|
+
if (suppliedPoolId !== positionPoolId) {
|
|
738
|
+
throw new SDKError2("SDK_INVALID_ARGUMENT", "V4 pool context does not match the tokenId position", {
|
|
739
|
+
tokenId,
|
|
740
|
+
suppliedPoolId,
|
|
741
|
+
positionPoolId
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
async function resolveV4PositionContext(context, tokenId, contracts) {
|
|
746
|
+
const result = await readRequiredContract(
|
|
747
|
+
context,
|
|
748
|
+
buildV4PoolAndPositionInfoRead({
|
|
749
|
+
network: context.network,
|
|
750
|
+
tokenId,
|
|
751
|
+
positionManager: resolveProtocolContract(context, contracts, "sunswapV4PositionManager")
|
|
752
|
+
}),
|
|
753
|
+
"V4 position context read"
|
|
754
|
+
);
|
|
755
|
+
return parseV4PositionContext(result, context.network);
|
|
756
|
+
}
|
|
757
|
+
async function shouldInitializeV4Pool(context, poolKey, input) {
|
|
758
|
+
if (!input.createPool) return { shouldInitialize: false };
|
|
759
|
+
if (input.poolInitializationMode === "trust-input") return { shouldInitialize: true };
|
|
760
|
+
try {
|
|
761
|
+
const slot0Result = await readRequiredContract(
|
|
762
|
+
context,
|
|
763
|
+
buildV4PoolSlot0Read({
|
|
764
|
+
network: context.network,
|
|
765
|
+
poolId: computeV4PoolId(poolKey),
|
|
766
|
+
poolManager: resolveProtocolContract(context, input.contracts, "sunswapV4PoolManager")
|
|
767
|
+
}),
|
|
768
|
+
"V4 pool initialization check"
|
|
769
|
+
);
|
|
770
|
+
const sqrtPriceX96 = parseSlot0(slot0Result).sqrtPriceX96;
|
|
771
|
+
return BigInt(sqrtPriceX96) === 0n ? { shouldInitialize: true } : { shouldInitialize: false, sqrtPriceX96 };
|
|
772
|
+
} catch {
|
|
773
|
+
return { shouldInitialize: true };
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
function maxV4LiquidityForAmounts(input) {
|
|
777
|
+
const sqrtPrice = BigInt(input.sqrtPriceX96);
|
|
778
|
+
let sqrtA = BigInt(TickMath.getSqrtRatioAtTick(input.tickLower).toString());
|
|
779
|
+
let sqrtB = BigInt(TickMath.getSqrtRatioAtTick(input.tickUpper).toString());
|
|
780
|
+
if (sqrtA > sqrtB) [sqrtA, sqrtB] = [sqrtB, sqrtA];
|
|
781
|
+
const amount0 = BigInt(input.amount0);
|
|
782
|
+
const amount1 = BigInt(input.amount1);
|
|
783
|
+
const difference = sqrtB - sqrtA;
|
|
784
|
+
let liquidity;
|
|
785
|
+
if (sqrtPrice <= sqrtA) {
|
|
786
|
+
liquidity = amount0 * sqrtA * sqrtB / (Q96 * difference);
|
|
787
|
+
} else if (sqrtPrice < sqrtB) {
|
|
788
|
+
const liquidity0 = amount0 * sqrtPrice * sqrtB / (Q96 * (sqrtB - sqrtPrice));
|
|
789
|
+
const liquidity1 = amount1 * Q96 / (sqrtPrice - sqrtA);
|
|
790
|
+
liquidity = liquidity0 < liquidity1 ? liquidity0 : liquidity1;
|
|
791
|
+
} else {
|
|
792
|
+
liquidity = amount1 * Q96 / difference;
|
|
793
|
+
}
|
|
794
|
+
return liquidity.toString();
|
|
795
|
+
}
|
|
796
|
+
async function resolveV4SqrtPrice(context, poolKey, input) {
|
|
797
|
+
if (input.createPool) {
|
|
798
|
+
if (input.sqrtPriceX96 === void 0) {
|
|
799
|
+
throw new SDKError2("SDK_INVALID_ARGUMENT", "sqrtPriceX96 is required when createPool is true");
|
|
800
|
+
}
|
|
801
|
+
return normalizeBaseUnitAmount(input.sqrtPriceX96, "sqrtPriceX96");
|
|
802
|
+
}
|
|
803
|
+
const slot0Result = await readRequiredContract(
|
|
804
|
+
context,
|
|
805
|
+
buildV4PoolSlot0Read({
|
|
806
|
+
network: context.network,
|
|
807
|
+
poolId: computeV4PoolId(poolKey),
|
|
808
|
+
poolManager: resolveProtocolContract(context, input.contracts, "sunswapV4PoolManager")
|
|
809
|
+
}),
|
|
810
|
+
"V4 pool state read"
|
|
811
|
+
);
|
|
812
|
+
return parseSlot0(slot0Result).sqrtPriceX96;
|
|
813
|
+
}
|
|
814
|
+
async function resolveV4LiquidityAmounts(context, input) {
|
|
815
|
+
const hasResolvedAmount = input.liquidity !== void 0 || input.amount0Max !== void 0 || input.amount1Max !== void 0;
|
|
816
|
+
if (hasResolvedAmount) {
|
|
817
|
+
if (input.liquidity === void 0 || input.amount0Max === void 0 || input.amount1Max === void 0) {
|
|
818
|
+
throw new SDKError2("SDK_INVALID_ARGUMENT", "liquidity, amount0Max, and amount1Max must be provided together");
|
|
819
|
+
}
|
|
820
|
+
return {
|
|
821
|
+
liquidity: normalizeBaseUnitAmount(input.liquidity, "liquidity"),
|
|
822
|
+
amount0Max: normalizeBaseUnitAmount(input.amount0Max, "amount0Max", { allowZero: true }),
|
|
823
|
+
amount1Max: normalizeBaseUnitAmount(input.amount1Max, "amount1Max", { allowZero: true }),
|
|
824
|
+
amount0Desired: normalizeBaseUnitAmount(input.amount0Max, "amount0Max", { allowZero: true }),
|
|
825
|
+
amount1Desired: normalizeBaseUnitAmount(input.amount1Max, "amount1Max", { allowZero: true }),
|
|
826
|
+
derived: false
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
if (input.amount0 === void 0 && input.amount1 === void 0) {
|
|
830
|
+
throw new SDKError2("SDK_INVALID_ARGUMENT", "V4 liquidity requires at least one of amount0 or amount1");
|
|
831
|
+
}
|
|
832
|
+
if (input.tickLower >= input.tickUpper) {
|
|
833
|
+
throw new SDKError2("SDK_INVALID_ARGUMENT", "tickLower must be less than tickUpper", {
|
|
834
|
+
tickLower: input.tickLower,
|
|
835
|
+
tickUpper: input.tickUpper
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
const userAmount0 = normalizeBaseUnitAmount(input.amount0 ?? "0", "amount0", { allowZero: true });
|
|
839
|
+
const userAmount1 = normalizeBaseUnitAmount(input.amount1 ?? "0", "amount1", { allowZero: true });
|
|
840
|
+
const amount0 = input.amountsSwapped ? userAmount1 : userAmount0;
|
|
841
|
+
const amount1 = input.amountsSwapped ? userAmount0 : userAmount1;
|
|
842
|
+
const liquidity = maxV4LiquidityForAmounts({
|
|
843
|
+
sqrtPriceX96: await resolveV4SqrtPrice(context, input.poolKey, input),
|
|
844
|
+
tickLower: input.tickLower,
|
|
845
|
+
tickUpper: input.tickUpper,
|
|
846
|
+
amount0,
|
|
847
|
+
amount1
|
|
848
|
+
});
|
|
849
|
+
if (BigInt(liquidity) <= 0n) {
|
|
850
|
+
throw new SDKError2("SDK_INVALID_ARGUMENT", "V4 amount inputs resolve to zero liquidity", {
|
|
851
|
+
amount0,
|
|
852
|
+
amount1,
|
|
853
|
+
tickLower: input.tickLower,
|
|
854
|
+
tickUpper: input.tickUpper
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
const slippageBips = slippageToBips(input.slippage ?? "0.5%");
|
|
858
|
+
const halfUp = (amount, bips) => ((amount * BigInt(1e4 + bips) + 5000n) / 10000n).toString();
|
|
859
|
+
return {
|
|
860
|
+
liquidity,
|
|
861
|
+
amount0Max: input.roundingMode === "half-up" ? halfUp(BigInt(amount0), slippageBips) : applySlippageMax(BigInt(amount0), slippageBips).toString(),
|
|
862
|
+
amount1Max: input.roundingMode === "half-up" ? halfUp(BigInt(amount1), slippageBips) : applySlippageMax(BigInt(amount1), slippageBips).toString(),
|
|
863
|
+
amount0Desired: amount0,
|
|
864
|
+
amount1Desired: amount1,
|
|
865
|
+
derived: true
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
function nativeCallValue(poolKey, amounts, mode) {
|
|
869
|
+
if (mode !== "desired") return void 0;
|
|
870
|
+
let value = 0n;
|
|
871
|
+
if (poolKey.currency0 === TRON_ZERO_ADDRESS2) value += BigInt(amounts.amount0Desired);
|
|
872
|
+
if (poolKey.currency1 === TRON_ZERO_ADDRESS2) value += BigInt(amounts.amount1Desired);
|
|
873
|
+
return value;
|
|
874
|
+
}
|
|
875
|
+
function planMetadata(input) {
|
|
876
|
+
return {
|
|
877
|
+
protocol: "sunswap-v4",
|
|
878
|
+
operation: input.operation,
|
|
879
|
+
...input.poolId !== void 0 ? { poolId: input.poolId } : {},
|
|
880
|
+
...input.tokenId !== void 0 ? { tokenId: input.tokenId } : {},
|
|
881
|
+
...input.owner !== void 0 ? { owner: input.owner } : {},
|
|
882
|
+
...input.sweepRecipient !== void 0 ? { sweepRecipient: input.sweepRecipient } : {},
|
|
883
|
+
...input.deadline !== void 0 ? { deadline: input.deadline } : {},
|
|
884
|
+
...input.poolKeyDerived !== void 0 ? { poolKeyDerived: input.poolKeyDerived } : {},
|
|
885
|
+
...input.amountsDerived !== void 0 ? { amountsDerived: input.amountsDerived } : {},
|
|
886
|
+
...input.createPool !== void 0 ? { createPool: input.createPool } : {}
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
async function approvalsFromV4Action(context, action, owner, mode, scope) {
|
|
890
|
+
const requirements = action.tokenRequirements ?? [];
|
|
891
|
+
const approvals = [];
|
|
892
|
+
for (const requirement of requirements) {
|
|
893
|
+
if (BigInt(requirement.amount) === 0n) continue;
|
|
894
|
+
if (mode === "none") continue;
|
|
895
|
+
const authorization = await planRouterPermit2Authorization(context, {
|
|
896
|
+
owner,
|
|
897
|
+
token: requirement.token,
|
|
898
|
+
spender: requirement.spender,
|
|
899
|
+
amount: requirement.amount,
|
|
900
|
+
approval: "approve"
|
|
901
|
+
});
|
|
902
|
+
for (const approval of authorization.actions) {
|
|
903
|
+
if (approval.type !== "approve" && approval.type !== "contract-call") {
|
|
904
|
+
throw new SDKError2(
|
|
905
|
+
"SDK_RUNTIME_ERROR",
|
|
906
|
+
"V4 Permit2 authorization produced a non-contract action",
|
|
907
|
+
{ actionType: approval.type }
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
approvals.push(...attachApprovalIntent(
|
|
911
|
+
[approval],
|
|
912
|
+
scope,
|
|
913
|
+
requirement.token
|
|
914
|
+
));
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return approvals;
|
|
918
|
+
}
|
|
919
|
+
var V4LiquidityClient = class {
|
|
920
|
+
constructor(context) {
|
|
921
|
+
this.context = context;
|
|
922
|
+
}
|
|
923
|
+
context;
|
|
924
|
+
async read(input) {
|
|
925
|
+
const poolId = assertV4PoolId(input.poolId);
|
|
926
|
+
const [slot0Result, liquidityResult] = await readMulticallRequired(
|
|
927
|
+
this.context,
|
|
928
|
+
buildV4PoolStateReads({
|
|
929
|
+
network: this.context.network,
|
|
930
|
+
poolId,
|
|
931
|
+
poolManager: resolveProtocolContract(this.context, input.contracts, "sunswapV4PoolManager")
|
|
932
|
+
}),
|
|
933
|
+
"V4 pool state read"
|
|
934
|
+
);
|
|
935
|
+
const slot0 = parseSlot0(slot0Result);
|
|
936
|
+
return {
|
|
937
|
+
protocol: "sunswap-v4",
|
|
938
|
+
poolId,
|
|
939
|
+
sqrtPriceX96: slot0.sqrtPriceX96,
|
|
940
|
+
tick: slot0.tick,
|
|
941
|
+
liquidity: parseString(liquidityResult, "liquidity")
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
initialize = {
|
|
945
|
+
plan: async (input) => this.planInitialize(input),
|
|
946
|
+
execute: async (input) => executeTransactionPlan(await this.planInitialize(input), createContextExecutor(this.context))
|
|
947
|
+
};
|
|
948
|
+
add = {
|
|
949
|
+
plan: async (input) => this.planMint(input),
|
|
950
|
+
execute: async (input) => executeTransactionPlan(await this.planMint(input), createContextExecutor(this.context))
|
|
951
|
+
};
|
|
952
|
+
increase = {
|
|
953
|
+
plan: async (input) => this.planIncrease(input),
|
|
954
|
+
execute: async (input) => executeTransactionPlan(await this.planIncrease(input), createContextExecutor(this.context))
|
|
955
|
+
};
|
|
956
|
+
async planInitialize(input) {
|
|
957
|
+
const { address: owner } = await requireWalletSnapshot(this.context, "V4 initialize pool");
|
|
958
|
+
const positionManager = resolveProtocolContract(this.context, input.contracts, "sunswapV4PositionManager");
|
|
959
|
+
const planId = nextPlanId(this.context, "liquidity-v4-initialize");
|
|
960
|
+
const poolKey = await resolveV4PoolKey(this.context, input);
|
|
961
|
+
const action = buildV4InitializePoolAction({
|
|
962
|
+
network: this.context.network,
|
|
963
|
+
positionManager,
|
|
964
|
+
poolKey: poolKey.poolKey,
|
|
965
|
+
sqrtPriceX96: normalizeBaseUnitAmount(input.sqrtPriceX96, "sqrtPriceX96"),
|
|
966
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {},
|
|
967
|
+
id: `${planId}-tx`
|
|
968
|
+
});
|
|
969
|
+
const poolId = computeV4PoolId(poolKey.poolKey);
|
|
970
|
+
const transaction = {
|
|
971
|
+
...action,
|
|
972
|
+
intent: {
|
|
973
|
+
kind: "create-pool",
|
|
974
|
+
scope: liquidityIntentScope(SUNSWAP_V4_LIQUIDITY_PROTOCOL_ID, "initialize-pool"),
|
|
975
|
+
poolId
|
|
976
|
+
}
|
|
977
|
+
};
|
|
978
|
+
return {
|
|
979
|
+
id: planId,
|
|
980
|
+
kind: "liquidity",
|
|
981
|
+
network: this.context.network,
|
|
982
|
+
owner,
|
|
983
|
+
summary: {
|
|
984
|
+
title: "Initialize SunSwap V4 pool",
|
|
985
|
+
lines: [`Pool ${poolId}`]
|
|
986
|
+
},
|
|
987
|
+
approvals: [],
|
|
988
|
+
transactions: [transaction],
|
|
989
|
+
metadata: planMetadata({ operation: "initialize-pool", poolId, poolKeyDerived: poolKey.derived })
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
async planMint(input) {
|
|
993
|
+
const positionManager = resolveProtocolContract(this.context, input.contracts, "sunswapV4PositionManager");
|
|
994
|
+
const planId = nextPlanId(this.context, "liquidity-v4-mint");
|
|
995
|
+
const { address: walletAddress } = await requireWalletSnapshot(this.context, "V4 mint position");
|
|
996
|
+
const owner = input.owner !== void 0 ? assertTronAddress2(input.owner, "owner") : walletAddress;
|
|
997
|
+
if (owner !== walletAddress) {
|
|
998
|
+
throw new SDKError2("SDK_WALLET_ACCOUNT_MISMATCH", "V4 mint owner does not match the connected wallet", {
|
|
999
|
+
expectedAddress: owner,
|
|
1000
|
+
actualAddress: walletAddress
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
const deadline = normalizeDeadline(this.context, input);
|
|
1004
|
+
const poolKey = await resolveV4PoolKey(this.context, input);
|
|
1005
|
+
const initialization = await shouldInitializeV4Pool(this.context, poolKey.poolKey, input);
|
|
1006
|
+
const amounts = await resolveV4LiquidityAmounts(this.context, {
|
|
1007
|
+
...input,
|
|
1008
|
+
poolKey: poolKey.poolKey,
|
|
1009
|
+
amountsSwapped: poolKey.amountsSwapped,
|
|
1010
|
+
...initialization.sqrtPriceX96 !== void 0 ? { sqrtPriceX96: initialization.sqrtPriceX96 } : {},
|
|
1011
|
+
...initialization.shouldInitialize ? { createPool: true } : { createPool: false }
|
|
1012
|
+
});
|
|
1013
|
+
if (input.createPool && input.sqrtPriceX96 === void 0) {
|
|
1014
|
+
throw new SDKError2("SDK_INVALID_ARGUMENT", "sqrtPriceX96 is required when createPool is true");
|
|
1015
|
+
}
|
|
1016
|
+
const hasNativeCurrency = poolKey.poolKey.currency0 === TRON_ZERO_ADDRESS2 || poolKey.poolKey.currency1 === TRON_ZERO_ADDRESS2;
|
|
1017
|
+
const sweepRecipient = hasNativeCurrency ? input.sweepRecipient !== void 0 ? assertTronAddress2(input.sweepRecipient, "sweepRecipient") : owner : void 0;
|
|
1018
|
+
const desiredNativeCallValue = nativeCallValue(poolKey.poolKey, amounts, input.nativeCallValueMode);
|
|
1019
|
+
const action = buildV4MintPositionAction({
|
|
1020
|
+
network: this.context.network,
|
|
1021
|
+
positionManager,
|
|
1022
|
+
positionConfig: { poolKey: poolKey.poolKey, tickLower: input.tickLower, tickUpper: input.tickUpper },
|
|
1023
|
+
liquidity: amounts.liquidity,
|
|
1024
|
+
amount0Max: amounts.amount0Max,
|
|
1025
|
+
amount1Max: amounts.amount1Max,
|
|
1026
|
+
owner,
|
|
1027
|
+
deadline,
|
|
1028
|
+
...desiredNativeCallValue !== void 0 ? { callValue: desiredNativeCallValue } : {},
|
|
1029
|
+
...sweepRecipient !== void 0 ? { sweepRecipient } : {},
|
|
1030
|
+
...input.hookData !== void 0 ? { hookData: input.hookData } : {},
|
|
1031
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {},
|
|
1032
|
+
id: `${planId}-tx`
|
|
1033
|
+
});
|
|
1034
|
+
const scope = liquidityIntentScope(
|
|
1035
|
+
SUNSWAP_V4_LIQUIDITY_PROTOCOL_ID,
|
|
1036
|
+
initialization.shouldInitialize ? "initialize-and-mint" : "mint-position"
|
|
1037
|
+
);
|
|
1038
|
+
const approvals = await approvalsFromV4Action(this.context, action, owner, input.approval, scope);
|
|
1039
|
+
const poolId = computeV4PoolId(poolKey.poolKey);
|
|
1040
|
+
const initializeAction = initialization.shouldInitialize ? buildV4InitializePoolAction({
|
|
1041
|
+
network: this.context.network,
|
|
1042
|
+
positionManager,
|
|
1043
|
+
poolKey: poolKey.poolKey,
|
|
1044
|
+
sqrtPriceX96: normalizeBaseUnitAmount(input.sqrtPriceX96, "sqrtPriceX96"),
|
|
1045
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {},
|
|
1046
|
+
id: `${planId}-initialize`
|
|
1047
|
+
}) : void 0;
|
|
1048
|
+
const calls = [
|
|
1049
|
+
...(input.permit2Signatures ?? []).map((permit) => encodeV4Permit2ForwardCall({ owner, permit })),
|
|
1050
|
+
...initializeAction ? [encodeV4ContractCallAction(initializeAction)] : [],
|
|
1051
|
+
encodeV4ContractCallAction(action)
|
|
1052
|
+
];
|
|
1053
|
+
const transactionAction = initializeAction || input.transactionMode === "multicall" || (input.permit2Signatures?.length ?? 0) > 0 ? buildV4MulticallAction({
|
|
1054
|
+
network: this.context.network,
|
|
1055
|
+
positionManager,
|
|
1056
|
+
calls,
|
|
1057
|
+
...action.callValue !== void 0 ? { callValue: action.callValue } : {},
|
|
1058
|
+
...action.tokenRequirements !== void 0 ? { tokenRequirements: action.tokenRequirements } : {},
|
|
1059
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {},
|
|
1060
|
+
id: `${planId}-tx`
|
|
1061
|
+
}) : action;
|
|
1062
|
+
const transaction = {
|
|
1063
|
+
...transactionAction,
|
|
1064
|
+
waitForReceipt: "included",
|
|
1065
|
+
intent: {
|
|
1066
|
+
kind: "add-liquidity",
|
|
1067
|
+
scope
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1070
|
+
return {
|
|
1071
|
+
id: planId,
|
|
1072
|
+
kind: "liquidity",
|
|
1073
|
+
network: this.context.network,
|
|
1074
|
+
owner,
|
|
1075
|
+
summary: {
|
|
1076
|
+
title: "Mint SunSwap V4 position",
|
|
1077
|
+
lines: [`Pool ${poolId}`, `Owner ${owner}`]
|
|
1078
|
+
},
|
|
1079
|
+
approvals,
|
|
1080
|
+
transactions: [transaction],
|
|
1081
|
+
metadata: planMetadata({
|
|
1082
|
+
operation: "mint-position",
|
|
1083
|
+
poolId,
|
|
1084
|
+
owner,
|
|
1085
|
+
...sweepRecipient !== void 0 ? { sweepRecipient } : {},
|
|
1086
|
+
deadline,
|
|
1087
|
+
poolKeyDerived: poolKey.derived,
|
|
1088
|
+
amountsDerived: amounts.derived,
|
|
1089
|
+
createPool: initialization.shouldInitialize
|
|
1090
|
+
})
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
async planIncrease(input) {
|
|
1094
|
+
const positionManager = resolveProtocolContract(this.context, input.contracts, "sunswapV4PositionManager");
|
|
1095
|
+
const planId = nextPlanId(this.context, "liquidity-v4-increase");
|
|
1096
|
+
const owner = await requireWalletAddress(this.context, "V4 increase liquidity");
|
|
1097
|
+
const deadline = normalizeDeadline(this.context, input);
|
|
1098
|
+
const tokenId = normalizeBaseUnitAmount(input.tokenId, "tokenId");
|
|
1099
|
+
const poolKeyNeedsPosition = input.poolKey === void 0 && input.token0 === void 0;
|
|
1100
|
+
const position = await resolveV4PositionContext(this.context, tokenId, input.contracts);
|
|
1101
|
+
const poolKey = poolKeyNeedsPosition ? { poolKey: position.poolKey, derived: true, amountsSwapped: false } : await resolveV4PoolKey(this.context, input);
|
|
1102
|
+
assertV4PoolKeyMatches(tokenId, poolKey.poolKey, position.poolKey);
|
|
1103
|
+
const amounts = await resolveV4LiquidityAmounts(this.context, {
|
|
1104
|
+
...input,
|
|
1105
|
+
poolKey: poolKey.poolKey,
|
|
1106
|
+
tickLower: position.tickLower,
|
|
1107
|
+
tickUpper: position.tickUpper,
|
|
1108
|
+
amountsSwapped: poolKey.amountsSwapped
|
|
1109
|
+
});
|
|
1110
|
+
const hasNativeCurrency = poolKey.poolKey.currency0 === TRON_ZERO_ADDRESS2 || poolKey.poolKey.currency1 === TRON_ZERO_ADDRESS2;
|
|
1111
|
+
const sweepRecipient = hasNativeCurrency ? input.sweepRecipient !== void 0 ? assertTronAddress2(input.sweepRecipient, "sweepRecipient") : owner : void 0;
|
|
1112
|
+
const desiredNativeCallValue = nativeCallValue(poolKey.poolKey, amounts, input.nativeCallValueMode);
|
|
1113
|
+
const action = buildV4IncreaseLiquidityAction({
|
|
1114
|
+
network: this.context.network,
|
|
1115
|
+
positionManager,
|
|
1116
|
+
poolKey: poolKey.poolKey,
|
|
1117
|
+
tokenId,
|
|
1118
|
+
liquidity: amounts.liquidity,
|
|
1119
|
+
amount0Max: amounts.amount0Max,
|
|
1120
|
+
amount1Max: amounts.amount1Max,
|
|
1121
|
+
deadline,
|
|
1122
|
+
...desiredNativeCallValue !== void 0 ? { callValue: desiredNativeCallValue } : {},
|
|
1123
|
+
...sweepRecipient !== void 0 ? { sweepRecipient } : {},
|
|
1124
|
+
...input.hookData !== void 0 ? { hookData: input.hookData } : {},
|
|
1125
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {},
|
|
1126
|
+
id: `${planId}-tx`
|
|
1127
|
+
});
|
|
1128
|
+
const scope = liquidityIntentScope(SUNSWAP_V4_LIQUIDITY_PROTOCOL_ID, "increase-liquidity");
|
|
1129
|
+
const approvals = await approvalsFromV4Action(this.context, action, owner, input.approval, scope);
|
|
1130
|
+
const poolId = computeV4PoolId(poolKey.poolKey);
|
|
1131
|
+
const permitCalls = (input.permit2Signatures ?? []).map((permit) => encodeV4Permit2ForwardCall({ owner, permit }));
|
|
1132
|
+
const transactionAction = input.transactionMode === "multicall" || permitCalls.length > 0 ? buildV4MulticallAction({
|
|
1133
|
+
network: this.context.network,
|
|
1134
|
+
positionManager,
|
|
1135
|
+
calls: [...permitCalls, encodeV4ContractCallAction(action)],
|
|
1136
|
+
...action.callValue !== void 0 ? { callValue: action.callValue } : {},
|
|
1137
|
+
...action.tokenRequirements !== void 0 ? { tokenRequirements: action.tokenRequirements } : {},
|
|
1138
|
+
...input.feeLimit !== void 0 ? { feeLimit: input.feeLimit } : {},
|
|
1139
|
+
id: `${planId}-tx`
|
|
1140
|
+
}) : action;
|
|
1141
|
+
const transaction = {
|
|
1142
|
+
...transactionAction,
|
|
1143
|
+
waitForReceipt: "included",
|
|
1144
|
+
intent: {
|
|
1145
|
+
kind: "add-liquidity",
|
|
1146
|
+
scope,
|
|
1147
|
+
positionId: tokenId
|
|
1148
|
+
}
|
|
1149
|
+
};
|
|
1150
|
+
return {
|
|
1151
|
+
id: planId,
|
|
1152
|
+
kind: "liquidity",
|
|
1153
|
+
network: this.context.network,
|
|
1154
|
+
owner,
|
|
1155
|
+
summary: {
|
|
1156
|
+
title: "Increase SunSwap V4 liquidity",
|
|
1157
|
+
lines: [`Position ${tokenId}`, `Pool ${poolId}`]
|
|
1158
|
+
},
|
|
1159
|
+
approvals,
|
|
1160
|
+
transactions: [transaction],
|
|
1161
|
+
metadata: planMetadata({
|
|
1162
|
+
operation: "increase-liquidity",
|
|
1163
|
+
poolId,
|
|
1164
|
+
tokenId,
|
|
1165
|
+
owner,
|
|
1166
|
+
...sweepRecipient !== void 0 ? { sweepRecipient } : {},
|
|
1167
|
+
deadline,
|
|
1168
|
+
poolKeyDerived: poolKey.derived,
|
|
1169
|
+
amountsDerived: amounts.derived
|
|
1170
|
+
})
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
};
|
|
1174
|
+
|
|
1175
|
+
// src/liquidity/client.ts
|
|
1176
|
+
var LiquidityClient = class {
|
|
1177
|
+
v4;
|
|
1178
|
+
v3;
|
|
1179
|
+
v2;
|
|
1180
|
+
constructor(context) {
|
|
1181
|
+
this.v2 = new V2LiquidityClient(context);
|
|
1182
|
+
const v3Client = new V3LiquidityClient(context);
|
|
1183
|
+
const v4Client = new V4LiquidityClient(context);
|
|
1184
|
+
this.v3 = {
|
|
1185
|
+
add: v3Client.add,
|
|
1186
|
+
increase: v3Client.increase
|
|
1187
|
+
};
|
|
1188
|
+
this.v4 = {
|
|
1189
|
+
initialize: v4Client.initialize,
|
|
1190
|
+
read: (input) => v4Client.read(input),
|
|
1191
|
+
add: v4Client.add,
|
|
1192
|
+
increase: v4Client.increase
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
};
|
|
1196
|
+
function createLiquidityClient(context) {
|
|
1197
|
+
return new LiquidityClient(context);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
export {
|
|
1201
|
+
planV2AddLiquidity,
|
|
1202
|
+
planV2RemoveLiquidity,
|
|
1203
|
+
V2LiquidityClient,
|
|
1204
|
+
createV2LiquidityClient,
|
|
1205
|
+
V4LiquidityClient,
|
|
1206
|
+
LiquidityClient,
|
|
1207
|
+
createLiquidityClient
|
|
1208
|
+
};
|
|
1209
|
+
//# sourceMappingURL=chunk-YMGZDXI4.js.map
|