@aspan/sdk 0.4.5 → 0.4.7

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/dist/index.mjs CHANGED
@@ -256,13 +256,6 @@ var DiamondABI = [
256
256
  outputs: [{ name: "assets", type: "uint256", internalType: "uint256" }],
257
257
  stateMutability: "nonpayable"
258
258
  },
259
- {
260
- type: "function",
261
- name: "withdrawAssets",
262
- inputs: [{ name: "_assets", type: "uint256", internalType: "uint256" }],
263
- outputs: [{ name: "shares", type: "uint256", internalType: "uint256" }],
264
- stateMutability: "nonpayable"
265
- },
266
259
  {
267
260
  type: "function",
268
261
  name: "getShares",
@@ -305,6 +298,16 @@ var DiamondABI = [
305
298
  outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
306
299
  stateMutability: "view"
307
300
  },
301
+ {
302
+ type: "function",
303
+ name: "previewRedeemMulti",
304
+ inputs: [{ name: "_shares", type: "uint256", internalType: "uint256" }],
305
+ outputs: [
306
+ { name: "assets", type: "address[]", internalType: "address[]" },
307
+ { name: "amounts", type: "uint256[]", internalType: "uint256[]" }
308
+ ],
309
+ stateMutability: "view"
310
+ },
308
311
  {
309
312
  type: "function",
310
313
  name: "getPendingYield",
@@ -671,13 +674,13 @@ var SApUSDABI = [
671
674
  },
672
675
  {
673
676
  type: "function",
674
- name: "previewRedeemMulti",
677
+ name: "previewRedeem",
675
678
  inputs: [
676
679
  { name: "shares", type: "uint256", internalType: "uint256" }
677
680
  ],
678
681
  outputs: [
679
- { name: "assets", type: "address[]", internalType: "address[]" },
680
- { name: "amounts", type: "uint256[]", internalType: "uint256[]" }
682
+ { name: "apUSDOut", type: "uint256", internalType: "uint256" },
683
+ { name: "xBNBOut", type: "uint256", internalType: "uint256" }
681
684
  ],
682
685
  stateMutability: "view"
683
686
  },
@@ -724,56 +727,69 @@ var SApUSDABI = [
724
727
  },
725
728
  {
726
729
  type: "function",
727
- name: "previewCleanXBNB",
730
+ name: "hasRole",
728
731
  inputs: [
729
- { name: "_xBNBAmount", type: "uint256", internalType: "uint256" },
730
- { name: "_router", type: "address", internalType: "address" },
731
- { name: "_path", type: "address[]", internalType: "address[]" }
732
+ { name: "role", type: "bytes32", internalType: "bytes32" },
733
+ { name: "account", type: "address", internalType: "address" }
732
734
  ],
733
- outputs: [{ name: "expectedApUSD", type: "uint256", internalType: "uint256" }],
735
+ outputs: [{ name: "", type: "bool", internalType: "bool" }],
734
736
  stateMutability: "view"
735
737
  },
738
+ // ============ Write Functions ============
736
739
  {
737
740
  type: "function",
738
- name: "KEEPER_ROLE",
739
- inputs: [],
740
- outputs: [{ name: "", type: "bytes32", internalType: "bytes32" }],
741
- stateMutability: "view"
741
+ name: "redeem",
742
+ inputs: [
743
+ { name: "shares", type: "uint256", internalType: "uint256" },
744
+ { name: "receiver", type: "address", internalType: "address" },
745
+ { name: "owner", type: "address", internalType: "address" }
746
+ ],
747
+ outputs: [
748
+ { name: "apUSDOut", type: "uint256", internalType: "uint256" },
749
+ { name: "xBNBOut", type: "uint256", internalType: "uint256" }
750
+ ],
751
+ stateMutability: "nonpayable"
742
752
  },
743
753
  {
744
754
  type: "function",
745
- name: "hasRole",
755
+ name: "deposit",
746
756
  inputs: [
747
- { name: "role", type: "bytes32", internalType: "bytes32" },
748
- { name: "account", type: "address", internalType: "address" }
757
+ { name: "assets", type: "uint256", internalType: "uint256" },
758
+ { name: "receiver", type: "address", internalType: "address" }
749
759
  ],
750
- outputs: [{ name: "", type: "bool", internalType: "bool" }],
751
- stateMutability: "view"
760
+ outputs: [{ name: "shares", type: "uint256", internalType: "uint256" }],
761
+ stateMutability: "nonpayable"
752
762
  },
753
- // ============ Keeper Functions ============
763
+ // ============ Accounting Functions ============
754
764
  {
755
765
  type: "function",
756
- name: "cleanXBNB",
766
+ name: "addAccounting",
757
767
  inputs: [
758
- { name: "_xBNBAmount", type: "uint256", internalType: "uint256" },
759
- { name: "_minApUSDOut", type: "uint256", internalType: "uint256" },
760
- { name: "_router", type: "address", internalType: "address" },
761
- { name: "_path", type: "address[]", internalType: "address[]" },
762
- { name: "_deadline", type: "uint256", internalType: "uint256" }
768
+ { name: "apUSD", type: "uint256", internalType: "uint256" },
769
+ { name: "xBNB", type: "uint256", internalType: "uint256" }
763
770
  ],
764
- outputs: [{ name: "apUSDReceived", type: "uint256", internalType: "uint256" }],
771
+ outputs: [],
765
772
  stateMutability: "nonpayable"
766
773
  },
767
- // ============ Events ============
768
774
  {
769
- type: "event",
770
- name: "VaultCleaned",
775
+ type: "function",
776
+ name: "subAccounting",
771
777
  inputs: [
772
- { name: "xBNBSold", type: "uint256", indexed: false, internalType: "uint256" },
773
- { name: "apUSDReceived", type: "uint256", indexed: false, internalType: "uint256" },
774
- { name: "keeper", type: "address", indexed: true, internalType: "address" }
778
+ { name: "apUSD", type: "uint256", internalType: "uint256" },
779
+ { name: "xBNB", type: "uint256", internalType: "uint256" }
775
780
  ],
776
- anonymous: false
781
+ outputs: [],
782
+ stateMutability: "nonpayable"
783
+ },
784
+ // ============ Admin Functions ============
785
+ {
786
+ type: "function",
787
+ name: "sweepExcess",
788
+ inputs: [
789
+ { name: "recipient", type: "address", internalType: "address" }
790
+ ],
791
+ outputs: [],
792
+ stateMutability: "nonpayable"
777
793
  }
778
794
  ];
779
795
 
@@ -1233,45 +1249,26 @@ var AspanReadClient = class _AspanReadClient {
1233
1249
  throw error;
1234
1250
  }
1235
1251
  }
1236
- async previewRedeem(shares) {
1237
- try {
1238
- return await this.publicClient.readContract({
1239
- address: this.diamondAddress,
1240
- abi: DiamondABI,
1241
- functionName: "previewRedeem",
1242
- args: [shares]
1243
- });
1244
- } catch (error) {
1245
- if (this.isZeroSupplyError(error)) {
1246
- return shares;
1247
- }
1248
- throw error;
1249
- }
1250
- }
1251
1252
  /**
1252
1253
  * Preview withdraw with multi-asset support (apUSD + xBNB)
1254
+ * Replaces previewRedeem — works for both clean and dirty pools.
1253
1255
  * @param shares Amount of sApUSD shares to redeem
1254
1256
  * @returns Object with apUSD and xBNB amounts, plus whether vault has xBNB
1255
1257
  */
1256
1258
  async previewRedeemMulti(shares) {
1257
- const sApUSDAddress = await this.getSApUSD();
1258
- const [result, conversion] = await Promise.all([
1259
- this.publicClient.readContract({
1260
- address: sApUSDAddress,
1261
- abi: SApUSDABI,
1262
- functionName: "previewRedeemMulti",
1263
- args: [shares]
1264
- }),
1265
- this.publicClient.readContract({
1266
- address: sApUSDAddress,
1267
- abi: SApUSDABI,
1268
- functionName: "hasStabilityConversion"
1269
- })
1270
- ]);
1259
+ const [assets, amounts] = await this.publicClient.readContract({
1260
+ address: this.diamondAddress,
1261
+ abi: DiamondABI,
1262
+ functionName: "previewRedeemMulti",
1263
+ args: [shares]
1264
+ });
1265
+ const hasXBNB = amounts.length > 1 && amounts[1] > 0n;
1271
1266
  return {
1272
- apUSD: result[1][0],
1273
- xBNB: result[1][1],
1274
- hasXBNB: conversion[0]
1267
+ apUSD: amounts[0] ?? 0n,
1268
+ xBNB: amounts[1] ?? 0n,
1269
+ hasXBNB,
1270
+ assets,
1271
+ amounts
1275
1272
  };
1276
1273
  }
1277
1274
  async getPendingYield() {
@@ -1764,21 +1761,6 @@ var AspanClient = class extends AspanReadClient {
1764
1761
  args: [params.shares]
1765
1762
  });
1766
1763
  }
1767
- /**
1768
- * Withdraw from stability pool by asset amount
1769
- * @param params Withdraw parameters
1770
- * @returns Transaction hash
1771
- */
1772
- async withdrawAssets(params) {
1773
- return this.walletClient.writeContract({
1774
- chain: this.chain,
1775
- account: this.walletClient.account,
1776
- address: this.diamondAddress,
1777
- abi: DiamondABI,
1778
- functionName: "withdrawAssets",
1779
- args: [params.assets]
1780
- });
1781
- }
1782
1764
  /**
1783
1765
  * Harvest yield from LSTs
1784
1766
  * @returns Transaction hash
@@ -2113,6 +2095,20 @@ var RouterABI = [
2113
2095
  outputs: [{ type: "bool" }],
2114
2096
  stateMutability: "view"
2115
2097
  },
2098
+ {
2099
+ type: "function",
2100
+ name: "lstModes",
2101
+ inputs: [{ name: "lst", type: "address" }],
2102
+ outputs: [{ type: "uint8" }],
2103
+ stateMutability: "view"
2104
+ },
2105
+ {
2106
+ type: "function",
2107
+ name: "isLSTRoutable",
2108
+ inputs: [{ name: "lst", type: "address" }],
2109
+ outputs: [{ type: "bool" }],
2110
+ stateMutability: "view"
2111
+ },
2116
2112
  // Preview functions - unified
2117
2113
  {
2118
2114
  type: "function",
@@ -2375,6 +2371,29 @@ var AspanRouterReadClient = class {
2375
2371
  args: [lst]
2376
2372
  });
2377
2373
  }
2374
+ /**
2375
+ * Get LST mode (0 = SYNC, 1 = ASYNC_DIRECT_ONLY)
2376
+ */
2377
+ async getLSTMode(lst) {
2378
+ const mode = await this.publicClient.readContract({
2379
+ address: this.routerAddress,
2380
+ abi: RouterABI,
2381
+ functionName: "lstModes",
2382
+ args: [lst]
2383
+ });
2384
+ return Number(mode);
2385
+ }
2386
+ /**
2387
+ * Check whether an LST supports routed flows (swap/stake/redeemAndSwap)
2388
+ */
2389
+ async isLSTRoutable(lst) {
2390
+ return this.publicClient.readContract({
2391
+ address: this.routerAddress,
2392
+ abi: RouterABI,
2393
+ functionName: "isLSTRoutable",
2394
+ args: [lst]
2395
+ });
2396
+ }
2378
2397
  /**
2379
2398
  * Get the Diamond contract address
2380
2399
  */
@@ -2413,6 +2432,92 @@ var AspanRouterReadClient = class {
2413
2432
  args: [lst, redeemXBNB, amount]
2414
2433
  });
2415
2434
  }
2435
+ /**
2436
+ * SDK-only preview: input token -> estimated LST -> previewMint
2437
+ * Note: oracle-based estimation (does not include live DEX slippage/price impact)
2438
+ */
2439
+ async previewMintByInput(inputToken, inputAmount, targetLST, mintXBNB) {
2440
+ if (inputAmount === 0n) return { lstAmount: 0n, mintedAmount: 0n };
2441
+ const [diamond, wbnb, usdt, usdc] = await Promise.all([
2442
+ this.getDiamond(),
2443
+ this.getWBNB(),
2444
+ this.getUSDT(),
2445
+ this.getUSDC()
2446
+ ]);
2447
+ const [bnbPrice8, lstPrice] = await Promise.all([
2448
+ this.publicClient.readContract({
2449
+ address: diamond,
2450
+ abi: DiamondABI,
2451
+ functionName: "getBNBPriceUSD"
2452
+ }),
2453
+ this.publicClient.readContract({
2454
+ address: diamond,
2455
+ abi: DiamondABI,
2456
+ functionName: "getLSTPriceUSD",
2457
+ args: [targetLST]
2458
+ })
2459
+ ]);
2460
+ const bnbPrice18 = BigInt(bnbPrice8) * 10n ** 10n;
2461
+ const lstPrice18 = BigInt(lstPrice);
2462
+ const one = 10n ** 18n;
2463
+ let lstAmount = 0n;
2464
+ const inNorm = inputToken.toLowerCase();
2465
+ if (inNorm === targetLST.toLowerCase()) {
2466
+ lstAmount = inputAmount;
2467
+ } else if (inNorm === zeroAddress.toLowerCase() || inNorm === wbnb.toLowerCase()) {
2468
+ const usdValue = inputAmount * bnbPrice18 / one;
2469
+ lstAmount = lstPrice18 === 0n ? 0n : usdValue * one / lstPrice18;
2470
+ } else if (inNorm === usdt.toLowerCase() || inNorm === usdc.toLowerCase()) {
2471
+ lstAmount = lstPrice18 === 0n ? 0n : inputAmount * one / lstPrice18;
2472
+ } else {
2473
+ throw new Error("Unsupported input token for SDK preview");
2474
+ }
2475
+ const mintedAmount = await this.previewMint(targetLST, lstAmount, mintXBNB);
2476
+ return { lstAmount, mintedAmount };
2477
+ }
2478
+ /**
2479
+ * SDK-only preview: previewRedeem -> estimated output token
2480
+ * Note: oracle-based estimation (does not include live DEX slippage/price impact)
2481
+ */
2482
+ async previewRedeemToOutput(lst, redeemXBNB, amount, outputToken) {
2483
+ const lstAmount = await this.previewRedeem(lst, redeemXBNB, amount);
2484
+ if (lstAmount === 0n) return { lstAmount: 0n, outputAmount: 0n };
2485
+ const [diamond, wbnb, usdt, usdc] = await Promise.all([
2486
+ this.getDiamond(),
2487
+ this.getWBNB(),
2488
+ this.getUSDT(),
2489
+ this.getUSDC()
2490
+ ]);
2491
+ const [bnbPrice8, lstPrice] = await Promise.all([
2492
+ this.publicClient.readContract({
2493
+ address: diamond,
2494
+ abi: DiamondABI,
2495
+ functionName: "getBNBPriceUSD"
2496
+ }),
2497
+ this.publicClient.readContract({
2498
+ address: diamond,
2499
+ abi: DiamondABI,
2500
+ functionName: "getLSTPriceUSD",
2501
+ args: [lst]
2502
+ })
2503
+ ]);
2504
+ const bnbPrice18 = BigInt(bnbPrice8) * 10n ** 10n;
2505
+ const lstPrice18 = BigInt(lstPrice);
2506
+ const one = 10n ** 18n;
2507
+ const usdValue = lstAmount * lstPrice18 / one;
2508
+ let outputAmount = 0n;
2509
+ const outNorm = outputToken.toLowerCase();
2510
+ if (outNorm === lst.toLowerCase()) {
2511
+ outputAmount = lstAmount;
2512
+ } else if (outNorm === zeroAddress.toLowerCase() || outNorm === wbnb.toLowerCase()) {
2513
+ outputAmount = bnbPrice18 === 0n ? 0n : usdValue * one / bnbPrice18;
2514
+ } else if (outNorm === usdt.toLowerCase() || outNorm === usdc.toLowerCase()) {
2515
+ outputAmount = usdValue;
2516
+ } else {
2517
+ throw new Error("Unsupported output token for SDK preview");
2518
+ }
2519
+ return { lstAmount, outputAmount };
2520
+ }
2416
2521
  /**
2417
2522
  * Get user's withdrawal request indices
2418
2523
  */
@@ -2751,7 +2856,7 @@ var BPS_PRECISION = 10000n;
2751
2856
  var PRICE_PRECISION = 10n ** 8n;
2752
2857
  var BSC_ADDRESSES = {
2753
2858
  diamond: "0x6a11B30d3a70727d5477D6d8090e144443fA1c78",
2754
- router: "0x29dd49b2e98674ee7531f17e4d40a7725918c3f6",
2859
+ router: "0x34a64c4EbDe830773083BA8c9469456616F6723b",
2755
2860
  apUSD: "0x4570047eeB5aDb4081c5d470494EB5134e34A287",
2756
2861
  xBNB: "0x0A0c9CD826e747D99F90D63e780B3727Da4D0d43",
2757
2862
  sApUSD: "0x896770Dba7c0481539E25aaB56bE285ECF6D65eB",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aspan/sdk",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
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",
@@ -23,6 +23,8 @@
23
23
  "typecheck": "tsc --noEmit",
24
24
  "test": "vitest run",
25
25
  "test:fork": "ANVIL_RPC=http://127.0.0.1:8545 vitest run fork.test.ts",
26
+ "test:risk": "ANVIL_RPC=http://127.0.0.1:8545 vitest run risk.test.ts",
27
+ "test:risk:live": "vitest run risk.test.ts",
26
28
  "test:e2e": "vitest run router.test.ts",
27
29
  "test:watch": "vitest",
28
30
  "clean": "rm -rf dist",
@@ -26,6 +26,7 @@ const ANVIL_RPC = process.env.ANVIL_RPC || "http://127.0.0.1:8545";
26
26
  const TOKENS = {
27
27
  WBNB: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c" as Address,
28
28
  USDT: "0x55d398326f99059fF775485246999027B3197955" as Address,
29
+ USDC: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d" as Address,
29
30
  slisBNB: "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B" as Address,
30
31
  apUSD: "0x4570047eeB5aDb4081c5d470494EB5134e34A287" as Address,
31
32
  xBNB: "0x0A0c9CD826e747D99F90D63e780B3727Da4D0d43" as Address,