@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aspan/sdk",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "TypeScript SDK for Aspan Protocol - LST-backed stablecoin on BNB Chain",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -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: "0x1977097E2E5697A6DD91b6732F368a14F50f6B3d" as Address,
31
- xBNB: "0xB78eB4d5928bAb158Eb23c3154544084cD2661d5" as Address,
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 = "0x159B2990966B0E4f07cD58c9Def513EA1fF81c0C" as Address;
26
+ const ROUTER = BSC_ADDRESSES.router;
26
27
 
27
28
  const TOKENS = {
28
- WBNB: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c" as Address,
29
- USDT: "0x55d398326f99059fF775485246999027B3197955" as Address,
30
- slisBNB: "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B" as Address,
31
- apUSD: "0x1977097E2E5697A6DD91b6732F368a14F50f6B3d" as Address,
32
- xBNB: "0xB78eB4d5928bAb158Eb23c3154544084cD2661d5" as Address,
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
- // Minimal amounts
36
- const BNB_AMOUNT = parseEther("0.0005"); // ~$0.30
37
- const USDT_AMOUNT = parseAmount("0.5"); // $0.50
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
- const apUSDOut = await readClient.previewMintApUSD(TOKENS.slisBNB, lstAmount);
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
- const lstBack = await readClient.previewRedeemApUSD(TOKENS.slisBNB, apUSDOut);
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.stakeAndMintApUSD(0n, BNB_AMOUNT);
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); // Wait for RPC state sync
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.redeemApUSD({
178
+ const redeemHash = await writeClient.redeem({
170
179
  lst: TOKENS.slisBNB,
171
- apUSDAmount: apUSDMinted,
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.mintApUSD({
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.redeemApUSDAndRequestUnstake(directMinted);
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.stakeAndMintXBNB(0n, BNB_AMOUNT);
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.redeemXBNB({
259
+ const redeemHash = await writeClient.redeem({
240
260
  lst: TOKENS.slisBNB,
241
- xBNBAmount: xBNBMinted,
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.mintXBNB({
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.swapAndMintApUSD({
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.redeemApUSDAndSwap({
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.swapAndMintXBNB({
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.redeemXBNB({
406
+ const redeemHash = await writeClient.redeem({
381
407
  lst: TOKENS.slisBNB,
382
- xBNBAmount: xBNBMinted,
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: swapAndMintApUSDDefault (USDT → apUSD with default LST)
395
- it.skipIf(!HAS_PRIVATE_KEY)("swapAndMintApUSDDefault: USDT → apUSD", async () => {
396
- console.log(`\n[E2E 5] swapAndMintApUSDDefault: USDT → apUSD`);
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.swapAndMintApUSDDefault({
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: swapAndMintXBNBDefault (USDT → xBNB with default LST)
427
- it.skipIf(!HAS_PRIVATE_KEY)("swapAndMintXBNBDefault: USDT → xBNB", async () => {
428
- console.log(`\n[E2E 6] swapAndMintXBNBDefault: USDT → xBNB`);
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.swapAndMintXBNBDefault({
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: redeemXBNBAndSwap (xBNB → USDT via V3)
459
- it.skipIf(!HAS_PRIVATE_KEY)("redeemXBNBAndSwap: xBNB → USDT", async () => {
460
- console.log(`\n[E2E 7] redeemXBNBAndSwap: xBNB → USDT`);
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.redeemXBNBAndSwap({
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: redeemXBNBAndRequestUnstake (xBNB → Lista unstake)
492
- it.skipIf(!HAS_PRIVATE_KEY)("redeemXBNBAndRequestUnstake: xBNB → Lista unstake", async () => {
493
- console.log(`\n[E2E 8] redeemXBNBAndRequestUnstake: xBNB → Lista unstake`);
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.redeemXBNBAndRequestUnstake(xBNBBalance);
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
- // Note: claimUnstake requires 7+ days waiting period, cannot be tested in e2e
517
- // Would need a fork test with time manipulation
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.stakeAndMintXBNB(0n, amount);
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: mintXBNB (direct LST → xBNB)
540
- it.skipIf(!HAS_PRIVATE_KEY)("mintXBNB: slisBNB → xBNB", async () => {
541
- console.log(`\n[E2E 10] mintXBNB: slisBNB → xBNB`);
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.mintXBNB({
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: redeemXBNB (direct xBNB → slisBNB)
571
- it.skipIf(!HAS_PRIVATE_KEY)("redeemXBNB: xBNB → slisBNB", async () => {
572
- console.log(`\n[E2E 11] redeemXBNB: xBNB → slisBNB`);
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.redeemXBNB({
631
+ const hash = await writeClient.redeem({
587
632
  lst: TOKENS.slisBNB,
588
- xBNBAmount: amount,
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: stakeAndMintApUSD (BNB → slisBNB → apUSD, simplified)
602
- // Note: May fail if apUSD minting is disabled on mainnet
603
- it.skipIf(!HAS_PRIVATE_KEY)("stakeAndMintApUSD: BNB → apUSD (may skip if disabled)", async () => {
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.stakeAndMintApUSD(0n, amount);
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: mintApUSD (direct LST → apUSD)
634
- // Note: May fail if apUSD minting is disabled on mainnet
635
- it.skipIf(!HAS_PRIVATE_KEY)("mintApUSD: slisBNB → apUSD (may skip if disabled)", async () => {
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.mintApUSD({
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: redeemApUSD (direct apUSD → slisBNB)
677
- it.skipIf(!HAS_PRIVATE_KEY)("redeemApUSD: apUSD → slisBNB", async () => {
678
- console.log(`\n[E2E 14] redeemApUSD: apUSD → slisBNB`);
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.redeemApUSD({
743
+ const hash = await writeClient.redeem({
693
744
  lst: TOKENS.slisBNB,
694
- apUSDAmount: amount,
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: redeemApUSDAndSwap (apUSD → slisBNB → USDT)
708
- it.skipIf(!HAS_PRIVATE_KEY)("redeemApUSDAndSwap: apUSD → USDT", async () => {
709
- console.log(`\n[E2E 15] redeemApUSDAndSwap: apUSD → USDT`);
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 > parseEther("0.1") ? parseEther("0.1") : 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.redeemApUSDAndSwap({
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: redeemApUSDAndRequestUnstake (apUSD → Lista unstake)
742
- it.skipIf(!HAS_PRIVATE_KEY)("redeemApUSDAndRequestUnstake: apUSD → Lista unstake", async () => {
743
- console.log(`\n[E2E 16] redeemApUSDAndRequestUnstake: apUSD → Lista unstake`);
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
- // Use remaining apUSD balance
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.redeemApUSDAndRequestUnstake(apUSDBalance);
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");
@@ -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;