@aspan/sdk 0.3.1 → 0.4.1
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 +310 -512
- package/dist/index.d.mts +303 -323
- package/dist/index.d.ts +303 -323
- package/dist/index.js +298 -381
- package/dist/index.mjs +297 -381
- package/package.json +1 -1
- package/src/__tests__/fork.test.ts +2 -2
- package/src/__tests__/router.test.ts +145 -89
- package/src/abi/diamond.ts +74 -0
- package/src/abi/router.ts +60 -191
- package/src/abi/sApUSD.ts +100 -0
- package/src/bot/config.ts +51 -1
- package/src/bot/index.ts +69 -1
- package/src/bot/services/fee-manager.ts +303 -0
- package/src/bot/services/risk-keeper.ts +519 -0
- package/src/client.ts +109 -0
- package/src/index.ts +22 -5
- package/src/router.ts +58 -216
- package/src/types.ts +60 -43
package/package.json
CHANGED
|
@@ -27,8 +27,8 @@ const TOKENS = {
|
|
|
27
27
|
WBNB: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c" as Address,
|
|
28
28
|
USDT: "0x55d398326f99059fF775485246999027B3197955" as Address,
|
|
29
29
|
slisBNB: "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B" as Address,
|
|
30
|
-
apUSD: "
|
|
31
|
-
xBNB: "
|
|
30
|
+
apUSD: "0x4570047eeB5aDb4081c5d470494EB5134e34A287" as Address,
|
|
31
|
+
xBNB: "0x0A0c9CD826e747D99F90D63e780B3727Da4D0d43" as Address,
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
const ERC20_ABI = [
|
|
@@ -18,23 +18,24 @@ import {
|
|
|
18
18
|
encodeV3Path,
|
|
19
19
|
parseAmount,
|
|
20
20
|
formatAmount,
|
|
21
|
+
BSC_ADDRESSES,
|
|
21
22
|
} from "../index";
|
|
22
23
|
|
|
23
24
|
// ============ Configuration ============
|
|
24
25
|
|
|
25
|
-
const ROUTER =
|
|
26
|
+
const ROUTER = BSC_ADDRESSES.router;
|
|
26
27
|
|
|
27
28
|
const TOKENS = {
|
|
28
|
-
WBNB:
|
|
29
|
-
USDT:
|
|
30
|
-
slisBNB:
|
|
31
|
-
apUSD:
|
|
32
|
-
xBNB:
|
|
29
|
+
WBNB: BSC_ADDRESSES.WBNB,
|
|
30
|
+
USDT: BSC_ADDRESSES.USDT,
|
|
31
|
+
slisBNB: BSC_ADDRESSES.slisBNB,
|
|
32
|
+
apUSD: BSC_ADDRESSES.apUSD,
|
|
33
|
+
xBNB: BSC_ADDRESSES.xBNB,
|
|
33
34
|
};
|
|
34
35
|
|
|
35
|
-
//
|
|
36
|
-
const BNB_AMOUNT = parseEther("0.
|
|
37
|
-
const USDT_AMOUNT = parseAmount("
|
|
36
|
+
// Test amounts - must be above Lista minimum unstake (~0.001 BNB worth)
|
|
37
|
+
const BNB_AMOUNT = parseEther("0.002"); // ~$1.20
|
|
38
|
+
const USDT_AMOUNT = parseAmount("2"); // $2.00
|
|
38
39
|
|
|
39
40
|
const ERC20_ABI = [
|
|
40
41
|
{ name: "balanceOf", type: "function", inputs: [{ type: "address" }], outputs: [{ type: "uint256" }], stateMutability: "view" },
|
|
@@ -121,10 +122,12 @@ describe("AspanRouter SDK", () => {
|
|
|
121
122
|
|
|
122
123
|
it("should preview mint/redeem", async () => {
|
|
123
124
|
const lstAmount = parseAmount("1");
|
|
124
|
-
|
|
125
|
+
// Use new unified previewMint(lst, amount, mintXBNB)
|
|
126
|
+
const apUSDOut = await readClient.previewMint(TOKENS.slisBNB, lstAmount, false);
|
|
125
127
|
expect(apUSDOut).toBeGreaterThan(parseAmount("400"));
|
|
126
128
|
|
|
127
|
-
|
|
129
|
+
// Use new unified previewRedeem(lst, redeemXBNB, amount)
|
|
130
|
+
const lstBack = await readClient.previewRedeem(TOKENS.slisBNB, false, apUSDOut);
|
|
128
131
|
const ratio = Number(lstBack) / Number(lstAmount);
|
|
129
132
|
expect(ratio).toBeGreaterThan(0.95);
|
|
130
133
|
console.log(`Preview: 1 slisBNB → ${formatAmount(apUSDOut)} apUSD (${(ratio * 100).toFixed(1)}% round-trip)`);
|
|
@@ -149,11 +152,17 @@ describe("AspanRouter SDK", () => {
|
|
|
149
152
|
console.log(` Staking ${formatAmount(BNB_AMOUNT)} BNB...`);
|
|
150
153
|
const apUSDBefore = await getBalance(TOKENS.apUSD);
|
|
151
154
|
|
|
152
|
-
const mintHash = await writeClient.
|
|
155
|
+
const mintHash = await writeClient.stakeAndMint({
|
|
156
|
+
targetLST: TOKENS.slisBNB,
|
|
157
|
+
isXBNB: false,
|
|
158
|
+
minMintOut: 0n,
|
|
159
|
+
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
|
|
160
|
+
value: BNB_AMOUNT,
|
|
161
|
+
});
|
|
153
162
|
const mintReceipt = await publicClient.waitForTransactionReceipt({ hash: mintHash });
|
|
154
163
|
await sleep(2000);
|
|
155
164
|
expect(mintReceipt.status).toBe("success");
|
|
156
|
-
await sleep(2000);
|
|
165
|
+
await sleep(2000);
|
|
157
166
|
|
|
158
167
|
const apUSDAfter = await getBalance(TOKENS.apUSD);
|
|
159
168
|
const apUSDMinted = apUSDAfter - apUSDBefore;
|
|
@@ -166,9 +175,10 @@ describe("AspanRouter SDK", () => {
|
|
|
166
175
|
|
|
167
176
|
const slisBNBBefore = await getBalance(TOKENS.slisBNB);
|
|
168
177
|
|
|
169
|
-
const redeemHash = await writeClient.
|
|
178
|
+
const redeemHash = await writeClient.redeem({
|
|
170
179
|
lst: TOKENS.slisBNB,
|
|
171
|
-
|
|
180
|
+
redeemXBNB: false,
|
|
181
|
+
amount: apUSDMinted,
|
|
172
182
|
minOut: 0n,
|
|
173
183
|
});
|
|
174
184
|
const redeemReceipt = await publicClient.waitForTransactionReceipt({ hash: redeemHash });
|
|
@@ -185,9 +195,10 @@ describe("AspanRouter SDK", () => {
|
|
|
185
195
|
await approve(TOKENS.slisBNB, slisBNBReceived);
|
|
186
196
|
|
|
187
197
|
const apUSDBefore2 = await getBalance(TOKENS.apUSD);
|
|
188
|
-
const directMintHash = await writeClient.
|
|
198
|
+
const directMintHash = await writeClient.mint({
|
|
189
199
|
lst: TOKENS.slisBNB,
|
|
190
200
|
lstAmount: slisBNBReceived,
|
|
201
|
+
mintXBNB: false,
|
|
191
202
|
minOut: 0n,
|
|
192
203
|
});
|
|
193
204
|
await publicClient.waitForTransactionReceipt({ hash: directMintHash });
|
|
@@ -203,7 +214,10 @@ describe("AspanRouter SDK", () => {
|
|
|
203
214
|
await approve(TOKENS.apUSD, directMinted);
|
|
204
215
|
|
|
205
216
|
const indicesBefore = await readClient.getUserWithdrawalIndices(account.address);
|
|
206
|
-
const unstakeHash = await writeClient.
|
|
217
|
+
const unstakeHash = await writeClient.redeemAndRequestUnstake({
|
|
218
|
+
redeemXBNB: false,
|
|
219
|
+
amount: directMinted,
|
|
220
|
+
});
|
|
207
221
|
await publicClient.waitForTransactionReceipt({ hash: unstakeHash });
|
|
208
222
|
await sleep(2000);
|
|
209
223
|
|
|
@@ -220,7 +234,13 @@ describe("AspanRouter SDK", () => {
|
|
|
220
234
|
console.log(` Staking ${formatAmount(BNB_AMOUNT)} BNB...`);
|
|
221
235
|
const xBNBBefore = await getBalance(TOKENS.xBNB);
|
|
222
236
|
|
|
223
|
-
const mintHash = await writeClient.
|
|
237
|
+
const mintHash = await writeClient.stakeAndMint({
|
|
238
|
+
targetLST: TOKENS.slisBNB,
|
|
239
|
+
isXBNB: true,
|
|
240
|
+
minMintOut: 0n,
|
|
241
|
+
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
|
|
242
|
+
value: BNB_AMOUNT,
|
|
243
|
+
});
|
|
224
244
|
const mintReceipt = await publicClient.waitForTransactionReceipt({ hash: mintHash });
|
|
225
245
|
await sleep(2000);
|
|
226
246
|
expect(mintReceipt.status).toBe("success");
|
|
@@ -236,9 +256,10 @@ describe("AspanRouter SDK", () => {
|
|
|
236
256
|
|
|
237
257
|
const slisBNBBefore = await getBalance(TOKENS.slisBNB);
|
|
238
258
|
|
|
239
|
-
const redeemHash = await writeClient.
|
|
259
|
+
const redeemHash = await writeClient.redeem({
|
|
240
260
|
lst: TOKENS.slisBNB,
|
|
241
|
-
|
|
261
|
+
redeemXBNB: true,
|
|
262
|
+
amount: xBNBMinted,
|
|
242
263
|
minOut: 0n,
|
|
243
264
|
});
|
|
244
265
|
const redeemReceipt = await publicClient.waitForTransactionReceipt({ hash: redeemHash });
|
|
@@ -255,9 +276,10 @@ describe("AspanRouter SDK", () => {
|
|
|
255
276
|
await approve(TOKENS.slisBNB, slisBNBReceived2);
|
|
256
277
|
|
|
257
278
|
const xBNBBefore2 = await getBalance(TOKENS.xBNB);
|
|
258
|
-
const directMintHash = await writeClient.
|
|
279
|
+
const directMintHash = await writeClient.mint({
|
|
259
280
|
lst: TOKENS.slisBNB,
|
|
260
281
|
lstAmount: slisBNBReceived2,
|
|
282
|
+
mintXBNB: true,
|
|
261
283
|
minOut: 0n,
|
|
262
284
|
});
|
|
263
285
|
await publicClient.waitForTransactionReceipt({ hash: directMintHash });
|
|
@@ -285,7 +307,7 @@ describe("AspanRouter SDK", () => {
|
|
|
285
307
|
|
|
286
308
|
const apUSDBefore = await getBalance(TOKENS.apUSD);
|
|
287
309
|
|
|
288
|
-
const mintHash = await writeClient.
|
|
310
|
+
const mintHash = await writeClient.swapAndMint({
|
|
289
311
|
swapParams: {
|
|
290
312
|
inputToken: TOKENS.USDT,
|
|
291
313
|
inputAmount: USDT_AMOUNT,
|
|
@@ -294,6 +316,7 @@ describe("AspanRouter SDK", () => {
|
|
|
294
316
|
poolFee: 2500,
|
|
295
317
|
},
|
|
296
318
|
mintParams: {
|
|
319
|
+
mintXBNB: false,
|
|
297
320
|
minMintOut: 0n,
|
|
298
321
|
recipient: zeroAddress,
|
|
299
322
|
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
|
|
@@ -315,12 +338,14 @@ describe("AspanRouter SDK", () => {
|
|
|
315
338
|
const usdtBefore = await getBalance(TOKENS.USDT);
|
|
316
339
|
const path = encodeV3Path([TOKENS.slisBNB, TOKENS.WBNB, TOKENS.USDT], [500, 500]);
|
|
317
340
|
|
|
318
|
-
const redeemHash = await writeClient.
|
|
341
|
+
const redeemHash = await writeClient.redeemAndSwap({
|
|
319
342
|
lst: TOKENS.slisBNB,
|
|
343
|
+
redeemXBNB: false,
|
|
320
344
|
amount: apUSDMinted,
|
|
321
345
|
path,
|
|
322
346
|
minOut: 0n,
|
|
323
347
|
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
|
|
348
|
+
unwrapBNB: false,
|
|
324
349
|
});
|
|
325
350
|
const redeemReceipt = await publicClient.waitForTransactionReceipt({ hash: redeemHash });
|
|
326
351
|
await sleep(2000);
|
|
@@ -348,7 +373,7 @@ describe("AspanRouter SDK", () => {
|
|
|
348
373
|
|
|
349
374
|
const xBNBBefore = await getBalance(TOKENS.xBNB);
|
|
350
375
|
|
|
351
|
-
const mintHash = await writeClient.
|
|
376
|
+
const mintHash = await writeClient.swapAndMint({
|
|
352
377
|
swapParams: {
|
|
353
378
|
inputToken: TOKENS.USDT,
|
|
354
379
|
inputAmount: USDT_AMOUNT,
|
|
@@ -357,6 +382,7 @@ describe("AspanRouter SDK", () => {
|
|
|
357
382
|
poolFee: 2500,
|
|
358
383
|
},
|
|
359
384
|
mintParams: {
|
|
385
|
+
mintXBNB: true,
|
|
360
386
|
minMintOut: 0n,
|
|
361
387
|
recipient: zeroAddress,
|
|
362
388
|
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
|
|
@@ -377,9 +403,10 @@ describe("AspanRouter SDK", () => {
|
|
|
377
403
|
|
|
378
404
|
const slisBNBBefore = await getBalance(TOKENS.slisBNB);
|
|
379
405
|
|
|
380
|
-
const redeemHash = await writeClient.
|
|
406
|
+
const redeemHash = await writeClient.redeem({
|
|
381
407
|
lst: TOKENS.slisBNB,
|
|
382
|
-
|
|
408
|
+
redeemXBNB: true,
|
|
409
|
+
amount: xBNBMinted,
|
|
383
410
|
minOut: 0n,
|
|
384
411
|
});
|
|
385
412
|
const redeemReceipt = await publicClient.waitForTransactionReceipt({ hash: redeemHash });
|
|
@@ -391,9 +418,9 @@ describe("AspanRouter SDK", () => {
|
|
|
391
418
|
expect(slisBNBAfter).toBeGreaterThan(slisBNBBefore);
|
|
392
419
|
}, 180000);
|
|
393
420
|
|
|
394
|
-
// Test 5:
|
|
395
|
-
it.skipIf(!HAS_PRIVATE_KEY)("
|
|
396
|
-
console.log(`\n[E2E 5]
|
|
421
|
+
// Test 5: swapAndMintDefault apUSD (USDT → apUSD with default LST)
|
|
422
|
+
it.skipIf(!HAS_PRIVATE_KEY)("swapAndMintDefault: USDT → apUSD", async () => {
|
|
423
|
+
console.log(`\n[E2E 5] swapAndMintDefault: USDT → apUSD`);
|
|
397
424
|
|
|
398
425
|
const usdtBalance = await getBalance(TOKENS.USDT);
|
|
399
426
|
if (usdtBalance < USDT_AMOUNT) {
|
|
@@ -407,9 +434,10 @@ describe("AspanRouter SDK", () => {
|
|
|
407
434
|
const apUSDBefore = await getBalance(TOKENS.apUSD);
|
|
408
435
|
|
|
409
436
|
const deadline = BigInt(Math.floor(Date.now() / 1000) + 600);
|
|
410
|
-
const mintHash = await writeClient.
|
|
437
|
+
const mintHash = await writeClient.swapAndMintDefault({
|
|
411
438
|
inputToken: TOKENS.USDT,
|
|
412
439
|
inputAmount: USDT_AMOUNT,
|
|
440
|
+
mintXBNB: false,
|
|
413
441
|
minMintOut: 0n,
|
|
414
442
|
deadline,
|
|
415
443
|
});
|
|
@@ -423,9 +451,9 @@ describe("AspanRouter SDK", () => {
|
|
|
423
451
|
expect(apUSDMinted).toBeGreaterThan(0n);
|
|
424
452
|
}, 180000);
|
|
425
453
|
|
|
426
|
-
// Test 6:
|
|
427
|
-
it.skipIf(!HAS_PRIVATE_KEY)("
|
|
428
|
-
console.log(`\n[E2E 6]
|
|
454
|
+
// Test 6: swapAndMintDefault xBNB (USDT → xBNB with default LST)
|
|
455
|
+
it.skipIf(!HAS_PRIVATE_KEY)("swapAndMintDefault: USDT → xBNB", async () => {
|
|
456
|
+
console.log(`\n[E2E 6] swapAndMintDefault: USDT → xBNB`);
|
|
429
457
|
|
|
430
458
|
const usdtBalance = await getBalance(TOKENS.USDT);
|
|
431
459
|
if (usdtBalance < USDT_AMOUNT) {
|
|
@@ -439,9 +467,10 @@ describe("AspanRouter SDK", () => {
|
|
|
439
467
|
const xBNBBefore = await getBalance(TOKENS.xBNB);
|
|
440
468
|
|
|
441
469
|
const deadline = BigInt(Math.floor(Date.now() / 1000) + 600);
|
|
442
|
-
const mintHash = await writeClient.
|
|
470
|
+
const mintHash = await writeClient.swapAndMintDefault({
|
|
443
471
|
inputToken: TOKENS.USDT,
|
|
444
472
|
inputAmount: USDT_AMOUNT,
|
|
473
|
+
mintXBNB: true,
|
|
445
474
|
minMintOut: 0n,
|
|
446
475
|
deadline,
|
|
447
476
|
});
|
|
@@ -455,9 +484,9 @@ describe("AspanRouter SDK", () => {
|
|
|
455
484
|
expect(xBNBMinted).toBeGreaterThan(0n);
|
|
456
485
|
}, 180000);
|
|
457
486
|
|
|
458
|
-
// Test 7:
|
|
459
|
-
it.skipIf(!HAS_PRIVATE_KEY)("
|
|
460
|
-
console.log(`\n[E2E 7]
|
|
487
|
+
// Test 7: redeemAndSwap xBNB → USDT via V3
|
|
488
|
+
it.skipIf(!HAS_PRIVATE_KEY)("redeemAndSwap: xBNB → USDT", async () => {
|
|
489
|
+
console.log(`\n[E2E 7] redeemAndSwap: xBNB → USDT`);
|
|
461
490
|
|
|
462
491
|
const xBNBBalance = await getBalance(TOKENS.xBNB);
|
|
463
492
|
if (xBNBBalance === 0n) {
|
|
@@ -472,12 +501,14 @@ describe("AspanRouter SDK", () => {
|
|
|
472
501
|
const usdtBefore = await getBalance(TOKENS.USDT);
|
|
473
502
|
const path = encodeV3Path([TOKENS.slisBNB, TOKENS.WBNB, TOKENS.USDT], [500, 500]);
|
|
474
503
|
|
|
475
|
-
const redeemHash = await writeClient.
|
|
504
|
+
const redeemHash = await writeClient.redeemAndSwap({
|
|
476
505
|
lst: TOKENS.slisBNB,
|
|
506
|
+
redeemXBNB: true,
|
|
477
507
|
amount: redeemAmount,
|
|
478
508
|
path,
|
|
479
509
|
minOut: 0n,
|
|
480
510
|
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
|
|
511
|
+
unwrapBNB: false,
|
|
481
512
|
});
|
|
482
513
|
const redeemReceipt = await publicClient.waitForTransactionReceipt({ hash: redeemHash });
|
|
483
514
|
await sleep(2000);
|
|
@@ -488,22 +519,32 @@ describe("AspanRouter SDK", () => {
|
|
|
488
519
|
expect(usdtAfter).toBeGreaterThan(usdtBefore);
|
|
489
520
|
}, 180000);
|
|
490
521
|
|
|
491
|
-
// Test 8:
|
|
492
|
-
it.skipIf(!HAS_PRIVATE_KEY)("
|
|
493
|
-
console.log(`\n[E2E 8]
|
|
522
|
+
// Test 8: redeemAndRequestUnstake xBNB → Lista unstake
|
|
523
|
+
it.skipIf(!HAS_PRIVATE_KEY)("redeemAndRequestUnstake: xBNB → Lista unstake", async () => {
|
|
524
|
+
console.log(`\n[E2E 8] redeemAndRequestUnstake: xBNB → Lista unstake`);
|
|
525
|
+
|
|
526
|
+
// First stake fresh BNB → xBNB to ensure enough for Lista minimum
|
|
527
|
+
console.log(` Staking 0.002 BNB → xBNB first...`);
|
|
528
|
+
const stakeHash = await writeClient.stakeAndMint({
|
|
529
|
+
targetLST: TOKENS.slisBNB,
|
|
530
|
+
isXBNB: true,
|
|
531
|
+
minMintOut: 0n,
|
|
532
|
+
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
|
|
533
|
+
value: parseEther("0.002"),
|
|
534
|
+
});
|
|
535
|
+
await publicClient.waitForTransactionReceipt({ hash: stakeHash });
|
|
536
|
+
await sleep(2000);
|
|
494
537
|
|
|
495
538
|
const xBNBBalance = await getBalance(TOKENS.xBNB);
|
|
496
|
-
if (xBNBBalance === 0n) {
|
|
497
|
-
console.log(` ⚠️ No xBNB balance, skipping`);
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
539
|
console.log(` Requesting unstake for ${formatAmount(xBNBBalance, 8)} xBNB...`);
|
|
502
540
|
await approve(TOKENS.xBNB, xBNBBalance);
|
|
503
541
|
|
|
504
542
|
const indicesBefore = await readClient.getUserWithdrawalIndices(account.address);
|
|
505
543
|
|
|
506
|
-
const unstakeHash = await writeClient.
|
|
544
|
+
const unstakeHash = await writeClient.redeemAndRequestUnstake({
|
|
545
|
+
redeemXBNB: true,
|
|
546
|
+
amount: xBNBBalance,
|
|
547
|
+
});
|
|
507
548
|
const unstakeReceipt = await publicClient.waitForTransactionReceipt({ hash: unstakeHash });
|
|
508
549
|
await sleep(2000);
|
|
509
550
|
expect(unstakeReceipt.status).toBe("success");
|
|
@@ -513,19 +554,22 @@ describe("AspanRouter SDK", () => {
|
|
|
513
554
|
console.log(` Unstake requested ✓ (new indices: ${indicesAfter.length - indicesBefore.length})`);
|
|
514
555
|
}, 180000);
|
|
515
556
|
|
|
516
|
-
//
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
// Test 9: stakeAndMintXBNB (BNB → slisBNB → xBNB, simplified)
|
|
520
|
-
it.skipIf(!HAS_PRIVATE_KEY)("stakeAndMintXBNB: BNB → xBNB", async () => {
|
|
521
|
-
console.log(`\n[E2E 9] stakeAndMintXBNB: BNB → xBNB`);
|
|
557
|
+
// Test 9: stakeAndMint xBNB
|
|
558
|
+
it.skipIf(!HAS_PRIVATE_KEY)("stakeAndMint: BNB → xBNB", async () => {
|
|
559
|
+
console.log(`\n[E2E 9] stakeAndMint: BNB → xBNB`);
|
|
522
560
|
|
|
523
561
|
const amount = parseEther("0.001");
|
|
524
562
|
console.log(` Staking ${formatEther(amount)} BNB → xBNB...`);
|
|
525
563
|
|
|
526
564
|
const xBNBBefore = await getBalance(TOKENS.xBNB);
|
|
527
565
|
|
|
528
|
-
const hash = await writeClient.
|
|
566
|
+
const hash = await writeClient.stakeAndMint({
|
|
567
|
+
targetLST: TOKENS.slisBNB,
|
|
568
|
+
isXBNB: true,
|
|
569
|
+
minMintOut: 0n,
|
|
570
|
+
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
|
|
571
|
+
value: amount,
|
|
572
|
+
});
|
|
529
573
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
530
574
|
await sleep(2000);
|
|
531
575
|
expect(receipt.status).toBe("success");
|
|
@@ -536,9 +580,9 @@ describe("AspanRouter SDK", () => {
|
|
|
536
580
|
expect(minted).toBeGreaterThan(0n);
|
|
537
581
|
}, 180000);
|
|
538
582
|
|
|
539
|
-
// Test 10:
|
|
540
|
-
it.skipIf(!HAS_PRIVATE_KEY)("
|
|
541
|
-
console.log(`\n[E2E 10]
|
|
583
|
+
// Test 10: mint xBNB (direct LST → xBNB)
|
|
584
|
+
it.skipIf(!HAS_PRIVATE_KEY)("mint: slisBNB → xBNB", async () => {
|
|
585
|
+
console.log(`\n[E2E 10] mint: slisBNB → xBNB`);
|
|
542
586
|
|
|
543
587
|
const slisBNBBalance = await getBalance(TOKENS.slisBNB);
|
|
544
588
|
if (slisBNBBalance === 0n) {
|
|
@@ -552,9 +596,10 @@ describe("AspanRouter SDK", () => {
|
|
|
552
596
|
|
|
553
597
|
const xBNBBefore = await getBalance(TOKENS.xBNB);
|
|
554
598
|
|
|
555
|
-
const hash = await writeClient.
|
|
599
|
+
const hash = await writeClient.mint({
|
|
556
600
|
lst: TOKENS.slisBNB,
|
|
557
601
|
lstAmount: amount,
|
|
602
|
+
mintXBNB: true,
|
|
558
603
|
minOut: 0n,
|
|
559
604
|
});
|
|
560
605
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
@@ -567,9 +612,9 @@ describe("AspanRouter SDK", () => {
|
|
|
567
612
|
expect(minted).toBeGreaterThan(0n);
|
|
568
613
|
}, 180000);
|
|
569
614
|
|
|
570
|
-
// Test 11:
|
|
571
|
-
it.skipIf(!HAS_PRIVATE_KEY)("
|
|
572
|
-
console.log(`\n[E2E 11]
|
|
615
|
+
// Test 11: redeem xBNB → slisBNB
|
|
616
|
+
it.skipIf(!HAS_PRIVATE_KEY)("redeem: xBNB → slisBNB", async () => {
|
|
617
|
+
console.log(`\n[E2E 11] redeem: xBNB → slisBNB`);
|
|
573
618
|
|
|
574
619
|
const xBNBBalance = await getBalance(TOKENS.xBNB);
|
|
575
620
|
if (xBNBBalance === 0n) {
|
|
@@ -583,9 +628,10 @@ describe("AspanRouter SDK", () => {
|
|
|
583
628
|
|
|
584
629
|
const slisBNBBefore = await getBalance(TOKENS.slisBNB);
|
|
585
630
|
|
|
586
|
-
const hash = await writeClient.
|
|
631
|
+
const hash = await writeClient.redeem({
|
|
587
632
|
lst: TOKENS.slisBNB,
|
|
588
|
-
|
|
633
|
+
redeemXBNB: true,
|
|
634
|
+
amount,
|
|
589
635
|
minOut: 0n,
|
|
590
636
|
});
|
|
591
637
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
@@ -598,10 +644,9 @@ describe("AspanRouter SDK", () => {
|
|
|
598
644
|
expect(received).toBeGreaterThan(0n);
|
|
599
645
|
}, 180000);
|
|
600
646
|
|
|
601
|
-
// Test 12:
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
console.log(`\n[E2E 12] stakeAndMintApUSD: BNB → apUSD`);
|
|
647
|
+
// Test 12: stakeAndMint apUSD
|
|
648
|
+
it.skipIf(!HAS_PRIVATE_KEY)("stakeAndMint: BNB → apUSD (may skip if disabled)", async () => {
|
|
649
|
+
console.log(`\n[E2E 12] stakeAndMint: BNB → apUSD`);
|
|
605
650
|
|
|
606
651
|
const amount = parseEther("0.001");
|
|
607
652
|
console.log(` Staking ${formatEther(amount)} BNB → apUSD...`);
|
|
@@ -609,7 +654,13 @@ describe("AspanRouter SDK", () => {
|
|
|
609
654
|
const apUSDBefore = await getBalance(TOKENS.apUSD);
|
|
610
655
|
|
|
611
656
|
try {
|
|
612
|
-
const hash = await writeClient.
|
|
657
|
+
const hash = await writeClient.stakeAndMint({
|
|
658
|
+
targetLST: TOKENS.slisBNB,
|
|
659
|
+
isXBNB: false,
|
|
660
|
+
minMintOut: 0n,
|
|
661
|
+
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
|
|
662
|
+
value: amount,
|
|
663
|
+
});
|
|
613
664
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
614
665
|
await sleep(2000);
|
|
615
666
|
|
|
@@ -630,10 +681,9 @@ describe("AspanRouter SDK", () => {
|
|
|
630
681
|
}
|
|
631
682
|
}, 180000);
|
|
632
683
|
|
|
633
|
-
// Test 13:
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
console.log(`\n[E2E 13] mintApUSD: slisBNB → apUSD`);
|
|
684
|
+
// Test 13: mint apUSD (direct LST → apUSD)
|
|
685
|
+
it.skipIf(!HAS_PRIVATE_KEY)("mint: slisBNB → apUSD (may skip if disabled)", async () => {
|
|
686
|
+
console.log(`\n[E2E 13] mint: slisBNB → apUSD`);
|
|
637
687
|
|
|
638
688
|
const slisBNBBalance = await getBalance(TOKENS.slisBNB);
|
|
639
689
|
if (slisBNBBalance === 0n) {
|
|
@@ -648,9 +698,10 @@ describe("AspanRouter SDK", () => {
|
|
|
648
698
|
const apUSDBefore = await getBalance(TOKENS.apUSD);
|
|
649
699
|
|
|
650
700
|
try {
|
|
651
|
-
const hash = await writeClient.
|
|
701
|
+
const hash = await writeClient.mint({
|
|
652
702
|
lst: TOKENS.slisBNB,
|
|
653
703
|
lstAmount: amount,
|
|
704
|
+
mintXBNB: false,
|
|
654
705
|
minOut: 0n,
|
|
655
706
|
});
|
|
656
707
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
@@ -673,9 +724,9 @@ describe("AspanRouter SDK", () => {
|
|
|
673
724
|
}
|
|
674
725
|
}, 180000);
|
|
675
726
|
|
|
676
|
-
// Test 14:
|
|
677
|
-
it.skipIf(!HAS_PRIVATE_KEY)("
|
|
678
|
-
console.log(`\n[E2E 14]
|
|
727
|
+
// Test 14: redeem apUSD → slisBNB
|
|
728
|
+
it.skipIf(!HAS_PRIVATE_KEY)("redeem: apUSD → slisBNB", async () => {
|
|
729
|
+
console.log(`\n[E2E 14] redeem: apUSD → slisBNB`);
|
|
679
730
|
|
|
680
731
|
const apUSDBalance = await getBalance(TOKENS.apUSD);
|
|
681
732
|
if (apUSDBalance === 0n) {
|
|
@@ -689,9 +740,10 @@ describe("AspanRouter SDK", () => {
|
|
|
689
740
|
|
|
690
741
|
const slisBNBBefore = await getBalance(TOKENS.slisBNB);
|
|
691
742
|
|
|
692
|
-
const hash = await writeClient.
|
|
743
|
+
const hash = await writeClient.redeem({
|
|
693
744
|
lst: TOKENS.slisBNB,
|
|
694
|
-
|
|
745
|
+
redeemXBNB: false,
|
|
746
|
+
amount,
|
|
695
747
|
minOut: 0n,
|
|
696
748
|
});
|
|
697
749
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
@@ -704,9 +756,9 @@ describe("AspanRouter SDK", () => {
|
|
|
704
756
|
expect(received).toBeGreaterThan(0n);
|
|
705
757
|
}, 180000);
|
|
706
758
|
|
|
707
|
-
// Test 15:
|
|
708
|
-
it.skipIf(!HAS_PRIVATE_KEY)("
|
|
709
|
-
console.log(`\n[E2E 15]
|
|
759
|
+
// Test 15: redeemAndSwap apUSD → USDT
|
|
760
|
+
it.skipIf(!HAS_PRIVATE_KEY)("redeemAndSwap: apUSD → USDT", async () => {
|
|
761
|
+
console.log(`\n[E2E 15] redeemAndSwap: apUSD → USDT`);
|
|
710
762
|
|
|
711
763
|
const apUSDBalance = await getBalance(TOKENS.apUSD);
|
|
712
764
|
if (apUSDBalance === 0n) {
|
|
@@ -714,19 +766,21 @@ describe("AspanRouter SDK", () => {
|
|
|
714
766
|
return;
|
|
715
767
|
}
|
|
716
768
|
|
|
717
|
-
const amount = apUSDBalance >
|
|
769
|
+
const amount = apUSDBalance > parseAmount("1") ? parseAmount("1") : apUSDBalance;
|
|
718
770
|
console.log(` Redeeming ${formatEther(amount)} apUSD → USDT...`);
|
|
719
771
|
await approve(TOKENS.apUSD, amount);
|
|
720
772
|
|
|
721
773
|
const usdtBefore = await getBalance(TOKENS.USDT);
|
|
722
774
|
const path = encodeV3Path([TOKENS.slisBNB, TOKENS.WBNB, TOKENS.USDT], [500, 500]);
|
|
723
775
|
|
|
724
|
-
const hash = await writeClient.
|
|
776
|
+
const hash = await writeClient.redeemAndSwap({
|
|
725
777
|
lst: TOKENS.slisBNB,
|
|
778
|
+
redeemXBNB: false,
|
|
726
779
|
amount,
|
|
727
780
|
path,
|
|
728
781
|
minOut: 0n,
|
|
729
782
|
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
|
|
783
|
+
unwrapBNB: false,
|
|
730
784
|
});
|
|
731
785
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
732
786
|
await sleep(2000);
|
|
@@ -738,9 +792,9 @@ describe("AspanRouter SDK", () => {
|
|
|
738
792
|
expect(received).toBeGreaterThan(0n);
|
|
739
793
|
}, 180000);
|
|
740
794
|
|
|
741
|
-
// Test 16:
|
|
742
|
-
it.skipIf(!HAS_PRIVATE_KEY)("
|
|
743
|
-
console.log(`\n[E2E 16]
|
|
795
|
+
// Test 16: redeemAndRequestUnstake apUSD → Lista unstake
|
|
796
|
+
it.skipIf(!HAS_PRIVATE_KEY)("redeemAndRequestUnstake: apUSD → Lista unstake", async () => {
|
|
797
|
+
console.log(`\n[E2E 16] redeemAndRequestUnstake: apUSD → Lista unstake`);
|
|
744
798
|
|
|
745
799
|
const apUSDBalance = await getBalance(TOKENS.apUSD);
|
|
746
800
|
if (apUSDBalance === 0n) {
|
|
@@ -748,13 +802,15 @@ describe("AspanRouter SDK", () => {
|
|
|
748
802
|
return;
|
|
749
803
|
}
|
|
750
804
|
|
|
751
|
-
|
|
752
|
-
console.log(` Requesting unstake for ${formatEther(apUSDBalance)} apUSD...`);
|
|
805
|
+
console.log(` Requesting unstake for ${formatAmount(apUSDBalance)} apUSD...`);
|
|
753
806
|
await approve(TOKENS.apUSD, apUSDBalance);
|
|
754
807
|
|
|
755
808
|
const indicesBefore = await readClient.getUserWithdrawalIndices(account.address);
|
|
756
809
|
|
|
757
|
-
const hash = await writeClient.
|
|
810
|
+
const hash = await writeClient.redeemAndRequestUnstake({
|
|
811
|
+
redeemXBNB: false,
|
|
812
|
+
amount: apUSDBalance,
|
|
813
|
+
});
|
|
758
814
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
759
815
|
await sleep(2000);
|
|
760
816
|
expect(receipt.status).toBe("success");
|
package/src/abi/diamond.ts
CHANGED
|
@@ -477,6 +477,30 @@ export const DiamondABI = [
|
|
|
477
477
|
outputs: [{ name: "", type: "bool", internalType: "bool" }],
|
|
478
478
|
stateMutability: "view"
|
|
479
479
|
},
|
|
480
|
+
{
|
|
481
|
+
type: "function",
|
|
482
|
+
name: "bootstrap",
|
|
483
|
+
inputs: [
|
|
484
|
+
{ name: "_lstToken", type: "address", internalType: "address" },
|
|
485
|
+
{ name: "_lstAmount", type: "uint256", internalType: "uint256" }
|
|
486
|
+
],
|
|
487
|
+
outputs: [{ name: "xBNBAmount", type: "uint256", internalType: "uint256" }],
|
|
488
|
+
stateMutability: "nonpayable"
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
type: "function",
|
|
492
|
+
name: "isBootstrapped",
|
|
493
|
+
inputs: [],
|
|
494
|
+
outputs: [{ name: "", type: "bool", internalType: "bool" }],
|
|
495
|
+
stateMutability: "view"
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
type: "function",
|
|
499
|
+
name: "getBootstrapXBNBAmount",
|
|
500
|
+
inputs: [],
|
|
501
|
+
outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
|
|
502
|
+
stateMutability: "view"
|
|
503
|
+
},
|
|
480
504
|
|
|
481
505
|
// ============ StabilityModeFacet ============
|
|
482
506
|
{
|
|
@@ -510,6 +534,13 @@ export const DiamondABI = [
|
|
|
510
534
|
],
|
|
511
535
|
stateMutability: "nonpayable"
|
|
512
536
|
},
|
|
537
|
+
{
|
|
538
|
+
type: "function",
|
|
539
|
+
name: "cleanXbnb",
|
|
540
|
+
inputs: [{ name: "_xBNBAmount", type: "uint256", internalType: "uint256" }],
|
|
541
|
+
outputs: [],
|
|
542
|
+
stateMutability: "nonpayable"
|
|
543
|
+
},
|
|
513
544
|
|
|
514
545
|
// ============ StabilityModeFacet Events ============
|
|
515
546
|
{
|
|
@@ -531,5 +562,48 @@ export const DiamondABI = [
|
|
|
531
562
|
inputs: [],
|
|
532
563
|
outputs: [{ name: "", type: "address", internalType: "address" }],
|
|
533
564
|
stateMutability: "view"
|
|
565
|
+
},
|
|
566
|
+
|
|
567
|
+
// ============ Fee Manager ============
|
|
568
|
+
{
|
|
569
|
+
type: "function",
|
|
570
|
+
name: "setFeeManager",
|
|
571
|
+
inputs: [{ name: "_feeManager", type: "address", internalType: "address" }],
|
|
572
|
+
outputs: [],
|
|
573
|
+
stateMutability: "nonpayable"
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
type: "function",
|
|
577
|
+
name: "getFeeManager",
|
|
578
|
+
inputs: [],
|
|
579
|
+
outputs: [{ name: "", type: "address", internalType: "address" }],
|
|
580
|
+
stateMutability: "view"
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
type: "function",
|
|
584
|
+
name: "setFeeTiers",
|
|
585
|
+
inputs: [{
|
|
586
|
+
name: "_tiers",
|
|
587
|
+
type: "tuple[]",
|
|
588
|
+
internalType: "struct LibAppStorage.FeeTier[]",
|
|
589
|
+
components: [
|
|
590
|
+
{ name: "minCR", type: "uint256", internalType: "uint256" },
|
|
591
|
+
{ name: "apUSDMintFee", type: "uint16", internalType: "uint16" },
|
|
592
|
+
{ name: "apUSDRedeemFee", type: "uint16", internalType: "uint16" },
|
|
593
|
+
{ name: "xBNBMintFee", type: "uint16", internalType: "uint16" },
|
|
594
|
+
{ name: "xBNBRedeemFee", type: "uint16", internalType: "uint16" },
|
|
595
|
+
{ name: "apUSDMintDisabled", type: "bool", internalType: "bool" }
|
|
596
|
+
]
|
|
597
|
+
}],
|
|
598
|
+
outputs: [],
|
|
599
|
+
stateMutability: "nonpayable"
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
type: "event",
|
|
603
|
+
name: "FeeManagerSet",
|
|
604
|
+
inputs: [
|
|
605
|
+
{ name: "feeManager", type: "address", indexed: true, internalType: "address" }
|
|
606
|
+
],
|
|
607
|
+
anonymous: false
|
|
534
608
|
}
|
|
535
609
|
] as const;
|