@aspan/sdk 0.4.6 → 0.4.8

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",
@@ -681,13 +674,13 @@ var SApUSDABI = [
681
674
  },
682
675
  {
683
676
  type: "function",
684
- name: "previewRedeemMulti",
677
+ name: "previewRedeem",
685
678
  inputs: [
686
679
  { name: "shares", type: "uint256", internalType: "uint256" }
687
680
  ],
688
681
  outputs: [
689
- { name: "assets", type: "address[]", internalType: "address[]" },
690
- { name: "amounts", type: "uint256[]", internalType: "uint256[]" }
682
+ { name: "apUSDOut", type: "uint256", internalType: "uint256" },
683
+ { name: "xBNBOut", type: "uint256", internalType: "uint256" }
691
684
  ],
692
685
  stateMutability: "view"
693
686
  },
@@ -734,56 +727,69 @@ var SApUSDABI = [
734
727
  },
735
728
  {
736
729
  type: "function",
737
- name: "previewCleanXBNB",
730
+ name: "hasRole",
738
731
  inputs: [
739
- { name: "_xBNBAmount", type: "uint256", internalType: "uint256" },
740
- { name: "_router", type: "address", internalType: "address" },
741
- { name: "_path", type: "address[]", internalType: "address[]" }
732
+ { name: "role", type: "bytes32", internalType: "bytes32" },
733
+ { name: "account", type: "address", internalType: "address" }
742
734
  ],
743
- outputs: [{ name: "expectedApUSD", type: "uint256", internalType: "uint256" }],
735
+ outputs: [{ name: "", type: "bool", internalType: "bool" }],
744
736
  stateMutability: "view"
745
737
  },
738
+ // ============ Write Functions ============
746
739
  {
747
740
  type: "function",
748
- name: "KEEPER_ROLE",
749
- inputs: [],
750
- outputs: [{ name: "", type: "bytes32", internalType: "bytes32" }],
751
- 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"
752
752
  },
753
753
  {
754
754
  type: "function",
755
- name: "hasRole",
755
+ name: "deposit",
756
756
  inputs: [
757
- { name: "role", type: "bytes32", internalType: "bytes32" },
758
- { name: "account", type: "address", internalType: "address" }
757
+ { name: "assets", type: "uint256", internalType: "uint256" },
758
+ { name: "receiver", type: "address", internalType: "address" }
759
759
  ],
760
- outputs: [{ name: "", type: "bool", internalType: "bool" }],
761
- stateMutability: "view"
760
+ outputs: [{ name: "shares", type: "uint256", internalType: "uint256" }],
761
+ stateMutability: "nonpayable"
762
762
  },
763
- // ============ Keeper Functions ============
763
+ // ============ Accounting Functions ============
764
764
  {
765
765
  type: "function",
766
- name: "cleanXBNB",
766
+ name: "addAccounting",
767
767
  inputs: [
768
- { name: "_xBNBAmount", type: "uint256", internalType: "uint256" },
769
- { name: "_minApUSDOut", type: "uint256", internalType: "uint256" },
770
- { name: "_router", type: "address", internalType: "address" },
771
- { name: "_path", type: "address[]", internalType: "address[]" },
772
- { name: "_deadline", type: "uint256", internalType: "uint256" }
768
+ { name: "apUSD", type: "uint256", internalType: "uint256" },
769
+ { name: "xBNB", type: "uint256", internalType: "uint256" }
773
770
  ],
774
- outputs: [{ name: "apUSDReceived", type: "uint256", internalType: "uint256" }],
771
+ outputs: [],
775
772
  stateMutability: "nonpayable"
776
773
  },
777
- // ============ Events ============
778
774
  {
779
- type: "event",
780
- name: "VaultCleaned",
775
+ type: "function",
776
+ name: "subAccounting",
781
777
  inputs: [
782
- { name: "xBNBSold", type: "uint256", indexed: false, internalType: "uint256" },
783
- { name: "apUSDReceived", type: "uint256", indexed: false, internalType: "uint256" },
784
- { name: "keeper", type: "address", indexed: true, internalType: "address" }
778
+ { name: "apUSD", type: "uint256", internalType: "uint256" },
779
+ { name: "xBNB", type: "uint256", internalType: "uint256" }
785
780
  ],
786
- 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"
787
793
  }
788
794
  ];
789
795
 
@@ -828,7 +834,8 @@ var AspanReadClient = class _AspanReadClient {
828
834
  this.chain = config.chain ?? bsc;
829
835
  this.publicClient = createPublicClient({
830
836
  chain: this.chain,
831
- transport: http(config.rpcUrl)
837
+ transport: http(config.rpcUrl),
838
+ batch: { multicall: true }
832
839
  });
833
840
  }
834
841
  /**
@@ -1755,21 +1762,6 @@ var AspanClient = class extends AspanReadClient {
1755
1762
  args: [params.shares]
1756
1763
  });
1757
1764
  }
1758
- /**
1759
- * Withdraw from stability pool by asset amount
1760
- * @param params Withdraw parameters
1761
- * @returns Transaction hash
1762
- */
1763
- async withdrawAssets(params) {
1764
- return this.walletClient.writeContract({
1765
- chain: this.chain,
1766
- account: this.walletClient.account,
1767
- address: this.diamondAddress,
1768
- abi: DiamondABI,
1769
- functionName: "withdrawAssets",
1770
- args: [params.assets]
1771
- });
1772
- }
1773
1765
  /**
1774
1766
  * Harvest yield from LSTs
1775
1767
  * @returns Transaction hash
@@ -2104,6 +2096,20 @@ var RouterABI = [
2104
2096
  outputs: [{ type: "bool" }],
2105
2097
  stateMutability: "view"
2106
2098
  },
2099
+ {
2100
+ type: "function",
2101
+ name: "lstModes",
2102
+ inputs: [{ name: "lst", type: "address" }],
2103
+ outputs: [{ type: "uint8" }],
2104
+ stateMutability: "view"
2105
+ },
2106
+ {
2107
+ type: "function",
2108
+ name: "isLSTRoutable",
2109
+ inputs: [{ name: "lst", type: "address" }],
2110
+ outputs: [{ type: "bool" }],
2111
+ stateMutability: "view"
2112
+ },
2107
2113
  // Preview functions - unified
2108
2114
  {
2109
2115
  type: "function",
@@ -2325,14 +2331,23 @@ var AspanRouterReadClient = class {
2325
2331
  publicClient;
2326
2332
  routerAddress;
2327
2333
  chain;
2334
+ _addressCache = /* @__PURE__ */ new Map();
2328
2335
  constructor(config) {
2329
2336
  this.routerAddress = config.routerAddress;
2330
2337
  this.chain = config.chain ?? bsc2;
2331
2338
  this.publicClient = createPublicClient2({
2332
2339
  chain: this.chain,
2333
- transport: http2(config.rpcUrl)
2340
+ transport: http2(config.rpcUrl),
2341
+ batch: { multicall: true }
2334
2342
  });
2335
2343
  }
2344
+ async _getCachedAddress(key, fetcher) {
2345
+ const cached = this._addressCache.get(key);
2346
+ if (cached) return cached;
2347
+ const addr = await fetcher();
2348
+ this._addressCache.set(key, addr);
2349
+ return addr;
2350
+ }
2336
2351
  // ============ View Functions ============
2337
2352
  /**
2338
2353
  * Get the default LST address
@@ -2367,15 +2382,41 @@ var AspanRouterReadClient = class {
2367
2382
  });
2368
2383
  }
2369
2384
  /**
2370
- * Get the Diamond contract address
2385
+ * Get LST mode (0 = SYNC, 1 = ASYNC_DIRECT_ONLY)
2371
2386
  */
2372
- async getDiamond() {
2387
+ async getLSTMode(lst) {
2388
+ const mode = await this.publicClient.readContract({
2389
+ address: this.routerAddress,
2390
+ abi: RouterABI,
2391
+ functionName: "lstModes",
2392
+ args: [lst]
2393
+ });
2394
+ return Number(mode);
2395
+ }
2396
+ /**
2397
+ * Check whether an LST supports routed flows (swap/stake/redeemAndSwap)
2398
+ */
2399
+ async isLSTRoutable(lst) {
2373
2400
  return this.publicClient.readContract({
2374
2401
  address: this.routerAddress,
2375
2402
  abi: RouterABI,
2376
- functionName: "diamond"
2403
+ functionName: "isLSTRoutable",
2404
+ args: [lst]
2377
2405
  });
2378
2406
  }
2407
+ /**
2408
+ * Get the Diamond contract address
2409
+ */
2410
+ async getDiamond() {
2411
+ return this._getCachedAddress(
2412
+ "diamond",
2413
+ async () => this.publicClient.readContract({
2414
+ address: this.routerAddress,
2415
+ abi: RouterABI,
2416
+ functionName: "diamond"
2417
+ })
2418
+ );
2419
+ }
2379
2420
  /**
2380
2421
  * Preview mint output for a given LST amount
2381
2422
  * @param lst LST token address
@@ -2404,6 +2445,96 @@ var AspanRouterReadClient = class {
2404
2445
  args: [lst, redeemXBNB, amount]
2405
2446
  });
2406
2447
  }
2448
+ /**
2449
+ * SDK-only preview: input token -> estimated LST -> previewMint
2450
+ * Note: oracle-based estimation (does not include live DEX slippage/price impact)
2451
+ */
2452
+ async previewMintByInput(inputToken, inputAmount, targetLST, mintXBNB) {
2453
+ if (inputAmount === 0n) return { lstAmount: 0n, mintedAmount: 0n };
2454
+ let lstAmount = 0n;
2455
+ const inNorm = inputToken.toLowerCase();
2456
+ if (inNorm === targetLST.toLowerCase()) {
2457
+ lstAmount = inputAmount;
2458
+ } else {
2459
+ const [diamond, wbnb, usdt, usdc] = await Promise.all([
2460
+ this.getDiamond(),
2461
+ this.getWBNB(),
2462
+ this.getUSDT(),
2463
+ this.getUSDC()
2464
+ ]);
2465
+ const [bnbPrice8, lstPrice] = await Promise.all([
2466
+ this.publicClient.readContract({
2467
+ address: diamond,
2468
+ abi: DiamondABI,
2469
+ functionName: "getBNBPriceUSD"
2470
+ }),
2471
+ this.publicClient.readContract({
2472
+ address: diamond,
2473
+ abi: DiamondABI,
2474
+ functionName: "getLSTPriceUSD",
2475
+ args: [targetLST]
2476
+ })
2477
+ ]);
2478
+ const bnbPrice18 = BigInt(bnbPrice8) * 10n ** 10n;
2479
+ const lstPrice18 = BigInt(lstPrice);
2480
+ const one = 10n ** 18n;
2481
+ if (inNorm === zeroAddress.toLowerCase() || inNorm === wbnb.toLowerCase()) {
2482
+ const usdValue = inputAmount * bnbPrice18 / one;
2483
+ lstAmount = lstPrice18 === 0n ? 0n : usdValue * one / lstPrice18;
2484
+ } else if (inNorm === usdt.toLowerCase() || inNorm === usdc.toLowerCase()) {
2485
+ lstAmount = lstPrice18 === 0n ? 0n : inputAmount * one / lstPrice18;
2486
+ } else {
2487
+ throw new Error("Unsupported input token for SDK preview");
2488
+ }
2489
+ }
2490
+ const mintedAmount = await this.previewMint(targetLST, lstAmount, mintXBNB);
2491
+ return { lstAmount, mintedAmount };
2492
+ }
2493
+ /**
2494
+ * SDK-only preview: previewRedeem -> estimated output token
2495
+ * Note: oracle-based estimation (does not include live DEX slippage/price impact)
2496
+ */
2497
+ async previewRedeemToOutput(lst, redeemXBNB, amount, outputToken) {
2498
+ const lstAmount = await this.previewRedeem(lst, redeemXBNB, amount);
2499
+ if (lstAmount === 0n) return { lstAmount: 0n, outputAmount: 0n };
2500
+ let outputAmount = 0n;
2501
+ const outNorm = outputToken.toLowerCase();
2502
+ if (outNorm === lst.toLowerCase()) {
2503
+ outputAmount = lstAmount;
2504
+ } else {
2505
+ const [diamond, wbnb, usdt, usdc] = await Promise.all([
2506
+ this.getDiamond(),
2507
+ this.getWBNB(),
2508
+ this.getUSDT(),
2509
+ this.getUSDC()
2510
+ ]);
2511
+ const [bnbPrice8, lstPrice] = await Promise.all([
2512
+ this.publicClient.readContract({
2513
+ address: diamond,
2514
+ abi: DiamondABI,
2515
+ functionName: "getBNBPriceUSD"
2516
+ }),
2517
+ this.publicClient.readContract({
2518
+ address: diamond,
2519
+ abi: DiamondABI,
2520
+ functionName: "getLSTPriceUSD",
2521
+ args: [lst]
2522
+ })
2523
+ ]);
2524
+ const bnbPrice18 = BigInt(bnbPrice8) * 10n ** 10n;
2525
+ const lstPrice18 = BigInt(lstPrice);
2526
+ const one = 10n ** 18n;
2527
+ const usdValue = lstAmount * lstPrice18 / one;
2528
+ if (outNorm === zeroAddress.toLowerCase() || outNorm === wbnb.toLowerCase()) {
2529
+ outputAmount = bnbPrice18 === 0n ? 0n : usdValue * one / bnbPrice18;
2530
+ } else if (outNorm === usdt.toLowerCase() || outNorm === usdc.toLowerCase()) {
2531
+ outputAmount = usdValue;
2532
+ } else {
2533
+ throw new Error("Unsupported output token for SDK preview");
2534
+ }
2535
+ }
2536
+ return { lstAmount, outputAmount };
2537
+ }
2407
2538
  /**
2408
2539
  * Get user's withdrawal request indices
2409
2540
  */
@@ -2433,60 +2564,84 @@ var AspanRouterReadClient = class {
2433
2564
  }
2434
2565
  // ============ Token Address Getters ============
2435
2566
  async getWBNB() {
2436
- return this.publicClient.readContract({
2437
- address: this.routerAddress,
2438
- abi: RouterABI,
2439
- functionName: "wbnb"
2440
- });
2567
+ return this._getCachedAddress(
2568
+ "wbnb",
2569
+ async () => this.publicClient.readContract({
2570
+ address: this.routerAddress,
2571
+ abi: RouterABI,
2572
+ functionName: "wbnb"
2573
+ })
2574
+ );
2441
2575
  }
2442
2576
  async getUSDT() {
2443
- return this.publicClient.readContract({
2444
- address: this.routerAddress,
2445
- abi: RouterABI,
2446
- functionName: "usdt"
2447
- });
2577
+ return this._getCachedAddress(
2578
+ "usdt",
2579
+ async () => this.publicClient.readContract({
2580
+ address: this.routerAddress,
2581
+ abi: RouterABI,
2582
+ functionName: "usdt"
2583
+ })
2584
+ );
2448
2585
  }
2449
2586
  async getUSDC() {
2450
- return this.publicClient.readContract({
2451
- address: this.routerAddress,
2452
- abi: RouterABI,
2453
- functionName: "usdc"
2454
- });
2587
+ return this._getCachedAddress(
2588
+ "usdc",
2589
+ async () => this.publicClient.readContract({
2590
+ address: this.routerAddress,
2591
+ abi: RouterABI,
2592
+ functionName: "usdc"
2593
+ })
2594
+ );
2455
2595
  }
2456
2596
  async getSlisBNB() {
2457
- return this.publicClient.readContract({
2458
- address: this.routerAddress,
2459
- abi: RouterABI,
2460
- functionName: "slisBNB"
2461
- });
2597
+ return this._getCachedAddress(
2598
+ "slisBNB",
2599
+ async () => this.publicClient.readContract({
2600
+ address: this.routerAddress,
2601
+ abi: RouterABI,
2602
+ functionName: "slisBNB"
2603
+ })
2604
+ );
2462
2605
  }
2463
2606
  async getAsBNB() {
2464
- return this.publicClient.readContract({
2465
- address: this.routerAddress,
2466
- abi: RouterABI,
2467
- functionName: "asBNB"
2468
- });
2607
+ return this._getCachedAddress(
2608
+ "asBNB",
2609
+ async () => this.publicClient.readContract({
2610
+ address: this.routerAddress,
2611
+ abi: RouterABI,
2612
+ functionName: "asBNB"
2613
+ })
2614
+ );
2469
2615
  }
2470
2616
  async getWclisBNB() {
2471
- return this.publicClient.readContract({
2472
- address: this.routerAddress,
2473
- abi: RouterABI,
2474
- functionName: "wclisBNB"
2475
- });
2617
+ return this._getCachedAddress(
2618
+ "wclisBNB",
2619
+ async () => this.publicClient.readContract({
2620
+ address: this.routerAddress,
2621
+ abi: RouterABI,
2622
+ functionName: "wclisBNB"
2623
+ })
2624
+ );
2476
2625
  }
2477
2626
  async getApUSD() {
2478
- return this.publicClient.readContract({
2479
- address: this.routerAddress,
2480
- abi: RouterABI,
2481
- functionName: "apUSD"
2482
- });
2627
+ return this._getCachedAddress(
2628
+ "apUSD",
2629
+ async () => this.publicClient.readContract({
2630
+ address: this.routerAddress,
2631
+ abi: RouterABI,
2632
+ functionName: "apUSD"
2633
+ })
2634
+ );
2483
2635
  }
2484
2636
  async getXBNB() {
2485
- return this.publicClient.readContract({
2486
- address: this.routerAddress,
2487
- abi: RouterABI,
2488
- functionName: "xBNB"
2489
- });
2637
+ return this._getCachedAddress(
2638
+ "xBNB",
2639
+ async () => this.publicClient.readContract({
2640
+ address: this.routerAddress,
2641
+ abi: RouterABI,
2642
+ functionName: "xBNB"
2643
+ })
2644
+ );
2490
2645
  }
2491
2646
  };
2492
2647
  var AspanRouterClient = class extends AspanRouterReadClient {
@@ -2742,7 +2897,7 @@ var BPS_PRECISION = 10000n;
2742
2897
  var PRICE_PRECISION = 10n ** 8n;
2743
2898
  var BSC_ADDRESSES = {
2744
2899
  diamond: "0x6a11B30d3a70727d5477D6d8090e144443fA1c78",
2745
- router: "0x29dd49b2e98674ee7531f17e4d40a7725918c3f6",
2900
+ router: "0x34a64c4EbDe830773083BA8c9469456616F6723b",
2746
2901
  apUSD: "0x4570047eeB5aDb4081c5d470494EB5134e34A287",
2747
2902
  xBNB: "0x0A0c9CD826e747D99F90D63e780B3727Da4D0d43",
2748
2903
  sApUSD: "0x896770Dba7c0481539E25aaB56bE285ECF6D65eB",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aspan/sdk",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
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,