@aspan/sdk 0.4.3 → 0.4.5

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
@@ -405,6 +405,13 @@ var DiamondABI = [
405
405
  outputs: [{ name: "", type: "address", internalType: "address" }],
406
406
  stateMutability: "view"
407
407
  },
408
+ {
409
+ type: "function",
410
+ name: "setSApUSD",
411
+ inputs: [{ name: "_sApUSD", type: "address", internalType: "address" }],
412
+ outputs: [],
413
+ stateMutability: "nonpayable"
414
+ },
408
415
  {
409
416
  type: "function",
410
417
  name: "getStabilityPool",
@@ -426,6 +433,25 @@ var DiamondABI = [
426
433
  outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
427
434
  stateMutability: "view"
428
435
  },
436
+ {
437
+ type: "function",
438
+ name: "getAllFeeTiers",
439
+ inputs: [],
440
+ outputs: [{
441
+ name: "",
442
+ type: "tuple[]",
443
+ internalType: "struct LibAppStorage.FeeTier[]",
444
+ components: [
445
+ { name: "minCR", type: "uint256", internalType: "uint256" },
446
+ { name: "apUSDMintFee", type: "uint16", internalType: "uint16" },
447
+ { name: "apUSDRedeemFee", type: "uint16", internalType: "uint16" },
448
+ { name: "xBNBMintFee", type: "uint16", internalType: "uint16" },
449
+ { name: "xBNBRedeemFee", type: "uint16", internalType: "uint16" },
450
+ { name: "apUSDMintDisabled", type: "bool", internalType: "bool" }
451
+ ]
452
+ }],
453
+ stateMutability: "view"
454
+ },
429
455
  {
430
456
  type: "function",
431
457
  name: "getFeeTier",
@@ -534,11 +560,27 @@ var DiamondABI = [
534
560
  },
535
561
  {
536
562
  type: "function",
537
- name: "cleanXbnb",
538
- inputs: [{ name: "_xBNBAmount", type: "uint256", internalType: "uint256" }],
539
- outputs: [],
563
+ name: "cleanVaultXBNB",
564
+ inputs: [
565
+ { name: "_xBNBAmount", type: "uint256", internalType: "uint256" },
566
+ { name: "_minApUSDOut", type: "uint256", internalType: "uint256" }
567
+ ],
568
+ outputs: [
569
+ { name: "apUSDMinted", type: "uint256", internalType: "uint256" }
570
+ ],
540
571
  stateMutability: "nonpayable"
541
572
  },
573
+ {
574
+ type: "function",
575
+ name: "previewCleanVaultXBNB",
576
+ inputs: [
577
+ { name: "_xBNBAmount", type: "uint256", internalType: "uint256" }
578
+ ],
579
+ outputs: [
580
+ { name: "apUSDOut", type: "uint256", internalType: "uint256" }
581
+ ],
582
+ stateMutability: "view"
583
+ },
542
584
  // ============ StabilityModeFacet Events ============
543
585
  {
544
586
  type: "event",
@@ -603,6 +645,138 @@ var DiamondABI = [
603
645
  }
604
646
  ];
605
647
 
648
+ // src/abi/sApUSD.ts
649
+ var SApUSDABI = [
650
+ // ============ View Functions ============
651
+ {
652
+ type: "function",
653
+ name: "totalAssets",
654
+ inputs: [],
655
+ outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
656
+ stateMutability: "view"
657
+ },
658
+ {
659
+ type: "function",
660
+ name: "totalSupply",
661
+ inputs: [],
662
+ outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
663
+ stateMutability: "view"
664
+ },
665
+ {
666
+ type: "function",
667
+ name: "balanceOf",
668
+ inputs: [{ name: "account", type: "address", internalType: "address" }],
669
+ outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
670
+ stateMutability: "view"
671
+ },
672
+ {
673
+ type: "function",
674
+ name: "previewRedeemMulti",
675
+ inputs: [
676
+ { name: "shares", type: "uint256", internalType: "uint256" }
677
+ ],
678
+ outputs: [
679
+ { name: "assets", type: "address[]", internalType: "address[]" },
680
+ { name: "amounts", type: "uint256[]", internalType: "uint256[]" }
681
+ ],
682
+ stateMutability: "view"
683
+ },
684
+ {
685
+ type: "function",
686
+ name: "hasStabilityConversion",
687
+ inputs: [],
688
+ outputs: [
689
+ { name: "hasXBNB", type: "bool", internalType: "bool" },
690
+ { name: "xBNBAmount", type: "uint256", internalType: "uint256" }
691
+ ],
692
+ stateMutability: "view"
693
+ },
694
+ {
695
+ type: "function",
696
+ name: "totalValue",
697
+ inputs: [],
698
+ outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
699
+ stateMutability: "view"
700
+ },
701
+ {
702
+ type: "function",
703
+ name: "underlyingBalances",
704
+ inputs: [{ name: "_user", type: "address", internalType: "address" }],
705
+ outputs: [
706
+ { name: "apUSDBalance", type: "uint256", internalType: "uint256" },
707
+ { name: "xBNBBalance", type: "uint256", internalType: "uint256" }
708
+ ],
709
+ stateMutability: "view"
710
+ },
711
+ {
712
+ type: "function",
713
+ name: "xBNBToApUSDRate",
714
+ inputs: [],
715
+ outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
716
+ stateMutability: "view"
717
+ },
718
+ {
719
+ type: "function",
720
+ name: "exchangeRate",
721
+ inputs: [],
722
+ outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
723
+ stateMutability: "view"
724
+ },
725
+ {
726
+ type: "function",
727
+ name: "previewCleanXBNB",
728
+ inputs: [
729
+ { name: "_xBNBAmount", type: "uint256", internalType: "uint256" },
730
+ { name: "_router", type: "address", internalType: "address" },
731
+ { name: "_path", type: "address[]", internalType: "address[]" }
732
+ ],
733
+ outputs: [{ name: "expectedApUSD", type: "uint256", internalType: "uint256" }],
734
+ stateMutability: "view"
735
+ },
736
+ {
737
+ type: "function",
738
+ name: "KEEPER_ROLE",
739
+ inputs: [],
740
+ outputs: [{ name: "", type: "bytes32", internalType: "bytes32" }],
741
+ stateMutability: "view"
742
+ },
743
+ {
744
+ type: "function",
745
+ name: "hasRole",
746
+ inputs: [
747
+ { name: "role", type: "bytes32", internalType: "bytes32" },
748
+ { name: "account", type: "address", internalType: "address" }
749
+ ],
750
+ outputs: [{ name: "", type: "bool", internalType: "bool" }],
751
+ stateMutability: "view"
752
+ },
753
+ // ============ Keeper Functions ============
754
+ {
755
+ type: "function",
756
+ name: "cleanXBNB",
757
+ 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" }
763
+ ],
764
+ outputs: [{ name: "apUSDReceived", type: "uint256", internalType: "uint256" }],
765
+ stateMutability: "nonpayable"
766
+ },
767
+ // ============ Events ============
768
+ {
769
+ type: "event",
770
+ name: "VaultCleaned",
771
+ 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" }
775
+ ],
776
+ anonymous: false
777
+ }
778
+ ];
779
+
606
780
  // src/client.ts
607
781
  var pharosTestnet = {
608
782
  id: 688689,
@@ -996,6 +1170,19 @@ var AspanReadClient = class _AspanReadClient {
996
1170
  args: [user]
997
1171
  });
998
1172
  }
1173
+ /**
1174
+ * Get user's underlying balances (apUSD + xBNB) from sApUSD vault
1175
+ */
1176
+ async getBalanceMulti(user) {
1177
+ const sApUSDAddress = await this.getSApUSD();
1178
+ const [apUSDBalance, xBNBBalance] = await this.publicClient.readContract({
1179
+ address: sApUSDAddress,
1180
+ abi: SApUSDABI,
1181
+ functionName: "underlyingBalances",
1182
+ args: [user]
1183
+ });
1184
+ return { apUSDBalance, xBNBBalance };
1185
+ }
999
1186
  async getUserStabilityPoolPosition(user) {
1000
1187
  const [shares, balance] = await Promise.all([
1001
1188
  this.getShares(user),
@@ -1061,6 +1248,32 @@ var AspanReadClient = class _AspanReadClient {
1061
1248
  throw error;
1062
1249
  }
1063
1250
  }
1251
+ /**
1252
+ * Preview withdraw with multi-asset support (apUSD + xBNB)
1253
+ * @param shares Amount of sApUSD shares to redeem
1254
+ * @returns Object with apUSD and xBNB amounts, plus whether vault has xBNB
1255
+ */
1256
+ 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
+ ]);
1271
+ return {
1272
+ apUSD: result[1][0],
1273
+ xBNB: result[1][1],
1274
+ hasXBNB: conversion[0]
1275
+ };
1276
+ }
1064
1277
  async getPendingYield() {
1065
1278
  try {
1066
1279
  return await this.publicClient.readContract({
@@ -1214,6 +1427,93 @@ var AspanReadClient = class _AspanReadClient {
1214
1427
  apUSDMintDisabled: result[5]
1215
1428
  };
1216
1429
  }
1430
+ /**
1431
+ * Get all fee tiers
1432
+ * @returns Array of fee tiers sorted by minCR descending
1433
+ */
1434
+ async getAllFeeTiers() {
1435
+ const result = await this.publicClient.readContract({
1436
+ address: this.diamondAddress,
1437
+ abi: DiamondABI,
1438
+ functionName: "getAllFeeTiers"
1439
+ });
1440
+ return result.map((tier) => ({
1441
+ minCR: tier.minCR,
1442
+ apUSDMintFee: tier.apUSDMintFee,
1443
+ apUSDRedeemFee: tier.apUSDRedeemFee,
1444
+ xBNBMintFee: tier.xBNBMintFee,
1445
+ xBNBRedeemFee: tier.xBNBRedeemFee,
1446
+ apUSDMintDisabled: tier.apUSDMintDisabled
1447
+ }));
1448
+ }
1449
+ /**
1450
+ * Get comprehensive vault (sApUSD) info for frontend display
1451
+ * @returns Vault state including TVL, exchange rate, xBNB status
1452
+ */
1453
+ async getVaultInfo() {
1454
+ const sApUSDAddress = await this.getSApUSD();
1455
+ const [totalSupply, totalAssets, exchangeRate, conversion] = await Promise.all([
1456
+ this.publicClient.readContract({
1457
+ address: sApUSDAddress,
1458
+ abi: SApUSDABI,
1459
+ functionName: "totalSupply"
1460
+ }),
1461
+ this.publicClient.readContract({
1462
+ address: sApUSDAddress,
1463
+ abi: SApUSDABI,
1464
+ functionName: "totalAssets"
1465
+ }),
1466
+ this.publicClient.readContract({
1467
+ address: sApUSDAddress,
1468
+ abi: SApUSDABI,
1469
+ functionName: "exchangeRate"
1470
+ }),
1471
+ this.publicClient.readContract({
1472
+ address: sApUSDAddress,
1473
+ abi: SApUSDABI,
1474
+ functionName: "hasStabilityConversion"
1475
+ })
1476
+ ]);
1477
+ return {
1478
+ totalSupply,
1479
+ totalAssets,
1480
+ exchangeRate,
1481
+ hasXBNB: conversion[0],
1482
+ xBNBAmount: conversion[1]
1483
+ };
1484
+ }
1485
+ /**
1486
+ * Get full protocol dashboard data in one call (for frontend overview page)
1487
+ * Batches all key metrics into a single multicall
1488
+ */
1489
+ async getProtocolOverview() {
1490
+ const [cr, tvlBNB, tvlUSD, apUSD, xBNB, xPriceUSD, xPriceBNB, bnbPrice, fees, sm, staked] = await Promise.all([
1491
+ this.getCollateralRatio(),
1492
+ this.getTVLInBNB(),
1493
+ this.getTVLInUSD(),
1494
+ this.getApUSDSupply(),
1495
+ this.getXBNBSupply(),
1496
+ this.getXBNBPriceUSD(),
1497
+ this.getXBNBPriceBNB(),
1498
+ this.getBNBPriceUSD(),
1499
+ this.getCurrentFeeTier(),
1500
+ this.getStabilityMode(),
1501
+ this.getTotalStaked()
1502
+ ]);
1503
+ return {
1504
+ collateralRatio: cr,
1505
+ tvlBNB,
1506
+ tvlUSD,
1507
+ apUSDSupply: apUSD,
1508
+ xBNBSupply: xBNB,
1509
+ xBNBPriceUSD: xPriceUSD,
1510
+ xBNBPriceBNB: xPriceBNB,
1511
+ bnbPriceUSD: bnbPrice,
1512
+ currentFees: fees,
1513
+ stabilityMode: sm,
1514
+ totalStaked: staked
1515
+ };
1516
+ }
1217
1517
  async getCurrentFeeTier() {
1218
1518
  try {
1219
1519
  const result = await this.publicClient.readContract({
@@ -1511,18 +1811,31 @@ var AspanClient = class extends AspanReadClient {
1511
1811
  });
1512
1812
  }
1513
1813
  /**
1514
- * Clean underwater xBNB (burn without getting LST back)
1515
- * @description Only works when xBNB is underwater (price = 0)
1516
- * @param xBNBAmount Amount of xBNB to clean (burn)
1814
+ * Clean xBNB from sApUSD vault by converting back to apUSD
1815
+ * @param xBNBAmount Amount of xBNB to clean
1816
+ * @param minApUSDOut Minimum apUSD to receive (slippage protection)
1517
1817
  * @returns Transaction hash
1518
1818
  */
1519
- async cleanXbnb(xBNBAmount) {
1819
+ async cleanVaultXBNB(xBNBAmount, minApUSDOut = 0n) {
1520
1820
  return this.walletClient.writeContract({
1521
1821
  chain: this.chain,
1522
1822
  account: this.walletClient.account,
1523
1823
  address: this.diamondAddress,
1524
1824
  abi: DiamondABI,
1525
- functionName: "cleanXbnb",
1825
+ functionName: "cleanVaultXBNB",
1826
+ args: [xBNBAmount, minApUSDOut]
1827
+ });
1828
+ }
1829
+ /**
1830
+ * Preview how much apUSD would be minted from cleaning xBNB
1831
+ * @param xBNBAmount Amount of xBNB to preview
1832
+ * @returns apUSD amount that would be minted
1833
+ */
1834
+ async previewCleanVaultXBNB(xBNBAmount) {
1835
+ return this.publicClient.readContract({
1836
+ address: this.diamondAddress,
1837
+ abi: DiamondABI,
1838
+ functionName: "previewCleanVaultXBNB",
1526
1839
  args: [xBNBAmount]
1527
1840
  });
1528
1841
  }
@@ -2441,7 +2754,7 @@ var BSC_ADDRESSES = {
2441
2754
  router: "0x29dd49b2e98674ee7531f17e4d40a7725918c3f6",
2442
2755
  apUSD: "0x4570047eeB5aDb4081c5d470494EB5134e34A287",
2443
2756
  xBNB: "0x0A0c9CD826e747D99F90D63e780B3727Da4D0d43",
2444
- sApUSD: "0x73407A291c007a47CC926EcD5CaC256A1E2d00cF",
2757
+ sApUSD: "0x896770Dba7c0481539E25aaB56bE285ECF6D65eB",
2445
2758
  // LSTs
2446
2759
  slisBNB: "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B",
2447
2760
  asBNB: "0x77734e70b6E88b4d82fE632a168EDf6e700912b6",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aspan/sdk",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
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",
@@ -0,0 +1,212 @@
1
+ /**
2
+ * AspanReadClient Tests - Read-only queries against BSC mainnet
3
+ *
4
+ * Tests new SDK methods: previewRedeemMulti, getVaultInfo, getAllFeeTiers, getProtocolOverview
5
+ * No wallet/private key needed — pure view calls.
6
+ *
7
+ * Run with: npm test -- read-client
8
+ */
9
+
10
+ import { describe, it, expect, beforeAll } from "vitest";
11
+ import { parseEther, zeroAddress, type Address } from "viem";
12
+ import { bsc } from "viem/chains";
13
+ import { AspanReadClient, BSC_ADDRESSES } from "../index";
14
+
15
+ // ============ Configuration ============
16
+
17
+ const DIAMOND = BSC_ADDRESSES.diamond;
18
+ const RPC_URL = process.env.BSC_RPC_URL || "https://bsc-dataseed.binance.org";
19
+
20
+ describe("AspanReadClient - View Functions", () => {
21
+ let client: AspanReadClient;
22
+
23
+ beforeAll(() => {
24
+ client = new AspanReadClient({
25
+ diamondAddress: DIAMOND,
26
+ chain: bsc,
27
+ rpcUrl: RPC_URL,
28
+ });
29
+ });
30
+
31
+ // ============ Vault Info ============
32
+
33
+ describe("getVaultInfo", () => {
34
+ it("should return vault state with all fields", async () => {
35
+ const info = await client.getVaultInfo();
36
+
37
+ expect(info).toHaveProperty("totalSupply");
38
+ expect(info).toHaveProperty("totalAssets");
39
+ expect(info).toHaveProperty("exchangeRate");
40
+ expect(info).toHaveProperty("hasXBNB");
41
+ expect(info).toHaveProperty("xBNBAmount");
42
+
43
+ expect(typeof info.totalSupply).toBe("bigint");
44
+ expect(typeof info.totalAssets).toBe("bigint");
45
+ expect(typeof info.exchangeRate).toBe("bigint");
46
+ expect(typeof info.hasXBNB).toBe("boolean");
47
+ expect(typeof info.xBNBAmount).toBe("bigint");
48
+
49
+ // Exchange rate should be > 0 (at least 1e18 for 1:1)
50
+ expect(info.exchangeRate).toBeGreaterThan(0n);
51
+ });
52
+ });
53
+
54
+ // ============ Preview Redeem Multi ============
55
+
56
+ describe("previewRedeemMulti", () => {
57
+ it("should return apUSD and xBNB amounts for given shares", async () => {
58
+ const shares = parseEther("1"); // 1 share
59
+ const preview = await client.previewRedeemMulti(shares);
60
+
61
+ expect(preview).toHaveProperty("apUSD");
62
+ expect(preview).toHaveProperty("xBNB");
63
+ expect(preview).toHaveProperty("hasXBNB");
64
+
65
+ expect(typeof preview.apUSD).toBe("bigint");
66
+ expect(typeof preview.xBNB).toBe("bigint");
67
+ expect(typeof preview.hasXBNB).toBe("boolean");
68
+ });
69
+
70
+ it("should return zero for zero shares", async () => {
71
+ const preview = await client.previewRedeemMulti(0n);
72
+
73
+ expect(preview.apUSD).toBe(0n);
74
+ expect(preview.xBNB).toBe(0n);
75
+ });
76
+
77
+ it("should have consistent hasXBNB with getVaultInfo", async () => {
78
+ const shares = parseEther("1");
79
+ const [preview, vault] = await Promise.all([
80
+ client.previewRedeemMulti(shares),
81
+ client.getVaultInfo(),
82
+ ]);
83
+
84
+ expect(preview.hasXBNB).toBe(vault.hasXBNB);
85
+ });
86
+ });
87
+
88
+ // ============ Fee Tiers ============
89
+
90
+ describe("getAllFeeTiers", () => {
91
+ it("should return non-empty array of fee tiers", async () => {
92
+ const tiers = await client.getAllFeeTiers();
93
+
94
+ expect(Array.isArray(tiers)).toBe(true);
95
+ expect(tiers.length).toBeGreaterThan(0);
96
+ });
97
+
98
+ it("should have correct fee tier structure", async () => {
99
+ const tiers = await client.getAllFeeTiers();
100
+ const tier = tiers[0];
101
+
102
+ expect(tier).toHaveProperty("minCR");
103
+ expect(tier).toHaveProperty("apUSDMintFee");
104
+ expect(tier).toHaveProperty("apUSDRedeemFee");
105
+ expect(tier).toHaveProperty("xBNBMintFee");
106
+ expect(tier).toHaveProperty("xBNBRedeemFee");
107
+ expect(tier).toHaveProperty("apUSDMintDisabled");
108
+
109
+ expect(typeof tier.minCR).toBe("bigint");
110
+ expect(typeof tier.apUSDMintDisabled).toBe("boolean");
111
+ });
112
+
113
+ it("should be sorted by minCR descending", async () => {
114
+ const tiers = await client.getAllFeeTiers();
115
+
116
+ for (let i = 1; i < tiers.length; i++) {
117
+ expect(tiers[i - 1].minCR).toBeGreaterThanOrEqual(tiers[i].minCR);
118
+ }
119
+ });
120
+
121
+ it("should match getFeeTierCount", async () => {
122
+ const [tiers, count] = await Promise.all([
123
+ client.getAllFeeTiers(),
124
+ client.getFeeTierCount(),
125
+ ]);
126
+
127
+ expect(BigInt(tiers.length)).toBe(count);
128
+ });
129
+ });
130
+
131
+ // ============ Protocol Overview ============
132
+
133
+ describe("getProtocolOverview", () => {
134
+ it("should return all protocol metrics", async () => {
135
+ const overview = await client.getProtocolOverview();
136
+
137
+ expect(overview).toHaveProperty("collateralRatio");
138
+ expect(overview).toHaveProperty("tvlBNB");
139
+ expect(overview).toHaveProperty("tvlUSD");
140
+ expect(overview).toHaveProperty("apUSDSupply");
141
+ expect(overview).toHaveProperty("xBNBSupply");
142
+ expect(overview).toHaveProperty("xBNBPriceUSD");
143
+ expect(overview).toHaveProperty("xBNBPriceBNB");
144
+ expect(overview).toHaveProperty("bnbPriceUSD");
145
+ expect(overview).toHaveProperty("currentFees");
146
+ expect(overview).toHaveProperty("stabilityMode");
147
+ expect(overview).toHaveProperty("totalStaked");
148
+ });
149
+
150
+ it("should have positive TVL and prices", async () => {
151
+ const overview = await client.getProtocolOverview();
152
+
153
+ expect(overview.tvlBNB).toBeGreaterThan(0n);
154
+ expect(overview.tvlUSD).toBeGreaterThan(0n);
155
+ expect(overview.bnbPriceUSD).toBeGreaterThan(0n);
156
+ expect(overview.collateralRatio).toBeGreaterThan(0n);
157
+ });
158
+
159
+ it("should have valid currentFees structure", async () => {
160
+ const overview = await client.getProtocolOverview();
161
+ const fees = overview.currentFees;
162
+
163
+ expect(fees).toHaveProperty("minCR");
164
+ expect(fees).toHaveProperty("apUSDMintFee");
165
+ });
166
+
167
+ it("should have valid stabilityMode structure", async () => {
168
+ const overview = await client.getProtocolOverview();
169
+ const sm = overview.stabilityMode;
170
+
171
+ expect(sm).toHaveProperty("mode");
172
+ // mode should be 0 (normal), 1, or 2
173
+ expect([0, 1, 2]).toContain(sm.mode);
174
+ });
175
+ });
176
+
177
+ // ============ Existing view methods consistency ============
178
+
179
+ describe("cross-method consistency", () => {
180
+ it("vault totalStaked should match getVaultInfo totalSupply context", async () => {
181
+ const [staked, vault] = await Promise.all([
182
+ client.getTotalStaked(),
183
+ client.getVaultInfo(),
184
+ ]);
185
+
186
+ // totalStaked from Diamond should relate to vault totalAssets
187
+ expect(typeof staked).toBe("bigint");
188
+ expect(typeof vault.totalAssets).toBe("bigint");
189
+ });
190
+
191
+ it("exchangeRate from getVaultInfo should match getExchangeRate", async () => {
192
+ const [vault, rate] = await Promise.all([
193
+ client.getVaultInfo(),
194
+ client.getExchangeRate(),
195
+ ]);
196
+
197
+ expect(vault.exchangeRate).toBe(rate);
198
+ });
199
+ });
200
+
201
+ describe("getBalanceMulti", () => {
202
+ it("should return apUSD and xBNB balances for a user", async () => {
203
+ const testUser = zeroAddress; // zero address will have 0 balances
204
+ const result = await client.getBalanceMulti(testUser);
205
+
206
+ expect(typeof result.apUSDBalance).toBe("bigint");
207
+ expect(typeof result.xBNBBalance).toBe("bigint");
208
+ expect(result.apUSDBalance).toBe(0n);
209
+ expect(result.xBNBBalance).toBe(0n);
210
+ });
211
+ });
212
+ });
@@ -406,6 +406,13 @@ export const DiamondABI = [
406
406
  outputs: [{ name: "", type: "address", internalType: "address" }],
407
407
  stateMutability: "view"
408
408
  },
409
+ {
410
+ type: "function",
411
+ name: "setSApUSD",
412
+ inputs: [{ name: "_sApUSD", type: "address", internalType: "address" }],
413
+ outputs: [],
414
+ stateMutability: "nonpayable"
415
+ },
409
416
  {
410
417
  type: "function",
411
418
  name: "getStabilityPool",
@@ -427,6 +434,25 @@ export const DiamondABI = [
427
434
  outputs: [{ name: "", type: "uint256", internalType: "uint256" }],
428
435
  stateMutability: "view"
429
436
  },
437
+ {
438
+ type: "function",
439
+ name: "getAllFeeTiers",
440
+ inputs: [],
441
+ outputs: [{
442
+ name: "",
443
+ type: "tuple[]",
444
+ internalType: "struct LibAppStorage.FeeTier[]",
445
+ components: [
446
+ { name: "minCR", type: "uint256", internalType: "uint256" },
447
+ { name: "apUSDMintFee", type: "uint16", internalType: "uint16" },
448
+ { name: "apUSDRedeemFee", type: "uint16", internalType: "uint16" },
449
+ { name: "xBNBMintFee", type: "uint16", internalType: "uint16" },
450
+ { name: "xBNBRedeemFee", type: "uint16", internalType: "uint16" },
451
+ { name: "apUSDMintDisabled", type: "bool", internalType: "bool" },
452
+ ],
453
+ }],
454
+ stateMutability: "view"
455
+ },
430
456
  {
431
457
  type: "function",
432
458
  name: "getFeeTier",
@@ -536,11 +562,27 @@ export const DiamondABI = [
536
562
  },
537
563
  {
538
564
  type: "function",
539
- name: "cleanXbnb",
540
- inputs: [{ name: "_xBNBAmount", type: "uint256", internalType: "uint256" }],
541
- outputs: [],
565
+ name: "cleanVaultXBNB",
566
+ inputs: [
567
+ { name: "_xBNBAmount", type: "uint256", internalType: "uint256" },
568
+ { name: "_minApUSDOut", type: "uint256", internalType: "uint256" }
569
+ ],
570
+ outputs: [
571
+ { name: "apUSDMinted", type: "uint256", internalType: "uint256" }
572
+ ],
542
573
  stateMutability: "nonpayable"
543
574
  },
575
+ {
576
+ type: "function",
577
+ name: "previewCleanVaultXBNB",
578
+ inputs: [
579
+ { name: "_xBNBAmount", type: "uint256", internalType: "uint256" }
580
+ ],
581
+ outputs: [
582
+ { name: "apUSDOut", type: "uint256", internalType: "uint256" }
583
+ ],
584
+ stateMutability: "view"
585
+ },
544
586
 
545
587
  // ============ StabilityModeFacet Events ============
546
588
  {