@aspan/sdk 0.1.7 → 0.1.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/README.md CHANGED
@@ -124,8 +124,12 @@ function MintComponent() {
124
124
  // Step 3: Approve LST (use wagmi's useWriteContract or viem)
125
125
  // ...
126
126
 
127
- // Step 4: Mint apUSD
128
- const hash = await client.mintApUSD({ lstToken: SLISBNB, lstAmount });
127
+ // Step 4: Calculate minOut for slippage protection (e.g., 0.5% slippage)
128
+ const expectedApUSD = lstAmount; // Simplified, use actual calculation
129
+ const minOut = expectedApUSD * 995n / 1000n; // 0.5% slippage
130
+
131
+ // Step 5: Mint apUSD
132
+ const hash = await client.mintApUSD({ lstToken: SLISBNB, lstAmount, minOut });
129
133
  const receipt = await client.waitForTransaction(hash);
130
134
  console.log("Minted apUSD:", receipt.status);
131
135
  };
@@ -161,8 +165,9 @@ if (fees.apUSDMintDisabled) throw new Error("Minting disabled in current CR");
161
165
  // args: [DIAMOND, lstAmount],
162
166
  // });
163
167
 
164
- // Step 4: Mint apUSD
165
- const hash = await client.mintApUSD({ lstToken: SLISBNB, lstAmount });
168
+ // Step 4: Mint apUSD with slippage protection
169
+ const minOut = lstAmount * 995n / 1000n; // 0.5% slippage tolerance
170
+ const hash = await client.mintApUSD({ lstToken: SLISBNB, lstAmount, minOut });
166
171
  const receipt = await client.waitForTransaction(hash);
167
172
  console.log("Minted apUSD:", receipt.status);
168
173
  ```
@@ -181,8 +186,13 @@ const maxRedeemable = (collateral * lstPrice) / parseAmount("1");
181
186
  console.log("Max redeemable:", formatAmount(maxRedeemable), "USD");
182
187
 
183
188
  // Step 2: Approve apUSD spending (use viem directly)
184
- // Step 3: Redeem
185
- const hash = await client.redeemApUSD({ lstToken: SLISBNB, apUSDAmount });
189
+
190
+ // Step 3: Calculate expected LST output and set minOut
191
+ const expectedLST = (apUSDAmount * parseAmount("1")) / lstPrice;
192
+ const minOut = expectedLST * 995n / 1000n; // 0.5% slippage
193
+
194
+ // Step 4: Redeem with slippage protection
195
+ const hash = await client.redeemApUSD({ lstToken: SLISBNB, apUSDAmount, minOut });
186
196
  await client.waitForTransaction(hash);
187
197
  ```
188
198
 
@@ -202,12 +212,46 @@ if (xBNBPrice === 0n) {
202
212
  throw new Error("xBNB underwater - cannot mint");
203
213
  }
204
214
 
205
- // Step 3: Approve LST, then mint xBNB
206
- const hash = await client.mintXBNB({ lstToken: SLISBNB, lstAmount });
215
+ // Step 3: Approve LST, then mint xBNB with slippage protection
216
+ const minOut = 0n; // Set appropriate minOut based on expected xBNB output
217
+ const hash = await client.mintXBNB({ lstToken: SLISBNB, lstAmount, minOut });
218
+ await client.waitForTransaction(hash);
219
+ ```
220
+
221
+ ### Flow 4: Redeem xBNB for LST
222
+
223
+ User burns xBNB to get back LST.
224
+
225
+ ```typescript
226
+ const xBNBAmount = parseAmount("100"); // 100 xBNB
227
+
228
+ // Step 1: Check xBNB price (ensure not underwater)
229
+ const xBNBPrice = await client.getXBNBPriceBNB();
230
+ if (xBNBPrice === 0n) {
231
+ throw new Error("xBNB is underwater - redemption may result in 0 LST");
232
+ }
233
+
234
+ // Step 2: Check current fees
235
+ const fees = await client.getCurrentFees();
236
+ console.log("xBNB Redeem fee:", fees.xBNBRedeemFee / 100, "%");
237
+
238
+ // Step 3: Check available LST liquidity
239
+ const collateral = await client.getLSTCollateral(SLISBNB);
240
+ console.log("Available collateral:", formatAmount(collateral));
241
+
242
+ // Step 4: Approve xBNB spending (use viem directly)
243
+ // ...
244
+
245
+ // Step 5: Calculate expected LST and set minOut for slippage protection
246
+ const expectedLST = (xBNBAmount * xBNBPrice) / parseAmount("1");
247
+ const minOut = expectedLST * 995n / 1000n; // 0.5% slippage
248
+
249
+ // Step 6: Redeem xBNB
250
+ const hash = await client.redeemXBNB({ lstToken: SLISBNB, xBNBAmount, minOut });
207
251
  await client.waitForTransaction(hash);
208
252
  ```
209
253
 
210
- ### Flow 4: Stake apUSD to Earn Yield (sApUSD)
254
+ ### Flow 5: Stake apUSD to Earn Yield (sApUSD)
211
255
 
212
256
  User deposits apUSD to stability pool to earn LST yield.
213
257
 
@@ -232,7 +276,7 @@ console.log("Shares:", formatAmount(position.shares));
232
276
  console.log("Balance (incl. yield):", formatAmount(position.balance));
233
277
  ```
234
278
 
235
- ### Flow 5: Withdraw from Stability Pool
279
+ ### Flow 6: Withdraw from Stability Pool
236
280
 
237
281
  User withdraws their staked apUSD plus accumulated yield.
238
282
 
@@ -255,7 +299,7 @@ await client.waitForTransaction(hash);
255
299
  // const hash = await client.withdrawAssets({ assets: parseAmount("500") });
256
300
  ```
257
301
 
258
- ### Flow 6: Manual Yield Harvest
302
+ ### Flow 7: Manual Yield Harvest
259
303
 
260
304
  Anyone can trigger yield harvest to distribute pending LST yield.
261
305
 
package/dist/index.d.mts CHANGED
@@ -71,21 +71,29 @@ interface StabilityMode2Info {
71
71
  interface MintApUSDParams {
72
72
  lstToken: Address;
73
73
  lstAmount: bigint;
74
+ /** Minimum apUSD output for slippage protection (default: 0n) */
75
+ minOut?: bigint;
74
76
  }
75
77
  /** Parameters for redeeming apUSD */
76
78
  interface RedeemApUSDParams {
77
79
  lstToken: Address;
78
80
  apUSDAmount: bigint;
81
+ /** Minimum LST output for slippage protection (default: 0n) */
82
+ minOut?: bigint;
79
83
  }
80
84
  /** Parameters for minting xBNB */
81
85
  interface MintXBNBParams {
82
86
  lstToken: Address;
83
87
  lstAmount: bigint;
88
+ /** Minimum xBNB output for slippage protection (default: 0n) */
89
+ minOut?: bigint;
84
90
  }
85
91
  /** Parameters for redeeming xBNB */
86
92
  interface RedeemXBNBParams {
87
93
  lstToken: Address;
88
94
  xBNBAmount: bigint;
95
+ /** Minimum LST output for slippage protection (default: 0n) */
96
+ minOut?: bigint;
89
97
  }
90
98
  /** Parameters for depositing to stability pool */
91
99
  interface DepositParams {
@@ -365,6 +373,10 @@ declare const DiamondABI: readonly [{
365
373
  readonly name: "_lstAmount";
366
374
  readonly type: "uint256";
367
375
  readonly internalType: "uint256";
376
+ }, {
377
+ readonly name: "_minOut";
378
+ readonly type: "uint256";
379
+ readonly internalType: "uint256";
368
380
  }];
369
381
  readonly outputs: readonly [{
370
382
  readonly name: "apUSDAmount";
@@ -383,6 +395,10 @@ declare const DiamondABI: readonly [{
383
395
  readonly name: "_apUSDAmount";
384
396
  readonly type: "uint256";
385
397
  readonly internalType: "uint256";
398
+ }, {
399
+ readonly name: "_minOut";
400
+ readonly type: "uint256";
401
+ readonly internalType: "uint256";
386
402
  }];
387
403
  readonly outputs: readonly [{
388
404
  readonly name: "lstAmount";
@@ -401,6 +417,10 @@ declare const DiamondABI: readonly [{
401
417
  readonly name: "_lstAmount";
402
418
  readonly type: "uint256";
403
419
  readonly internalType: "uint256";
420
+ }, {
421
+ readonly name: "_minOut";
422
+ readonly type: "uint256";
423
+ readonly internalType: "uint256";
404
424
  }];
405
425
  readonly outputs: readonly [{
406
426
  readonly name: "xBNBAmount";
@@ -419,6 +439,10 @@ declare const DiamondABI: readonly [{
419
439
  readonly name: "_xBNBAmount";
420
440
  readonly type: "uint256";
421
441
  readonly internalType: "uint256";
442
+ }, {
443
+ readonly name: "_minOut";
444
+ readonly type: "uint256";
445
+ readonly internalType: "uint256";
422
446
  }];
423
447
  readonly outputs: readonly [{
424
448
  readonly name: "lstAmount";
@@ -1304,8 +1328,8 @@ declare function parseAmount(amount: string): bigint;
1304
1328
  declare function formatFeeBPS(bps: number): string;
1305
1329
  /**
1306
1330
  * Format collateral ratio to percentage string
1307
- * @param cr Collateral ratio (18 decimals, 1e18 = 100%)
1308
- * @returns Percentage string (e.g., "150%")
1331
+ * @param cr Collateral ratio in BPS (10000 = 100%, contract format)
1332
+ * @returns Percentage string (e.g., "150.00%")
1309
1333
  */
1310
1334
  declare function formatCR(cr: bigint): string;
1311
1335
  /**
package/dist/index.d.ts CHANGED
@@ -71,21 +71,29 @@ interface StabilityMode2Info {
71
71
  interface MintApUSDParams {
72
72
  lstToken: Address;
73
73
  lstAmount: bigint;
74
+ /** Minimum apUSD output for slippage protection (default: 0n) */
75
+ minOut?: bigint;
74
76
  }
75
77
  /** Parameters for redeeming apUSD */
76
78
  interface RedeemApUSDParams {
77
79
  lstToken: Address;
78
80
  apUSDAmount: bigint;
81
+ /** Minimum LST output for slippage protection (default: 0n) */
82
+ minOut?: bigint;
79
83
  }
80
84
  /** Parameters for minting xBNB */
81
85
  interface MintXBNBParams {
82
86
  lstToken: Address;
83
87
  lstAmount: bigint;
88
+ /** Minimum xBNB output for slippage protection (default: 0n) */
89
+ minOut?: bigint;
84
90
  }
85
91
  /** Parameters for redeeming xBNB */
86
92
  interface RedeemXBNBParams {
87
93
  lstToken: Address;
88
94
  xBNBAmount: bigint;
95
+ /** Minimum LST output for slippage protection (default: 0n) */
96
+ minOut?: bigint;
89
97
  }
90
98
  /** Parameters for depositing to stability pool */
91
99
  interface DepositParams {
@@ -365,6 +373,10 @@ declare const DiamondABI: readonly [{
365
373
  readonly name: "_lstAmount";
366
374
  readonly type: "uint256";
367
375
  readonly internalType: "uint256";
376
+ }, {
377
+ readonly name: "_minOut";
378
+ readonly type: "uint256";
379
+ readonly internalType: "uint256";
368
380
  }];
369
381
  readonly outputs: readonly [{
370
382
  readonly name: "apUSDAmount";
@@ -383,6 +395,10 @@ declare const DiamondABI: readonly [{
383
395
  readonly name: "_apUSDAmount";
384
396
  readonly type: "uint256";
385
397
  readonly internalType: "uint256";
398
+ }, {
399
+ readonly name: "_minOut";
400
+ readonly type: "uint256";
401
+ readonly internalType: "uint256";
386
402
  }];
387
403
  readonly outputs: readonly [{
388
404
  readonly name: "lstAmount";
@@ -401,6 +417,10 @@ declare const DiamondABI: readonly [{
401
417
  readonly name: "_lstAmount";
402
418
  readonly type: "uint256";
403
419
  readonly internalType: "uint256";
420
+ }, {
421
+ readonly name: "_minOut";
422
+ readonly type: "uint256";
423
+ readonly internalType: "uint256";
404
424
  }];
405
425
  readonly outputs: readonly [{
406
426
  readonly name: "xBNBAmount";
@@ -419,6 +439,10 @@ declare const DiamondABI: readonly [{
419
439
  readonly name: "_xBNBAmount";
420
440
  readonly type: "uint256";
421
441
  readonly internalType: "uint256";
442
+ }, {
443
+ readonly name: "_minOut";
444
+ readonly type: "uint256";
445
+ readonly internalType: "uint256";
422
446
  }];
423
447
  readonly outputs: readonly [{
424
448
  readonly name: "lstAmount";
@@ -1304,8 +1328,8 @@ declare function parseAmount(amount: string): bigint;
1304
1328
  declare function formatFeeBPS(bps: number): string;
1305
1329
  /**
1306
1330
  * Format collateral ratio to percentage string
1307
- * @param cr Collateral ratio (18 decimals, 1e18 = 100%)
1308
- * @returns Percentage string (e.g., "150%")
1331
+ * @param cr Collateral ratio in BPS (10000 = 100%, contract format)
1332
+ * @returns Percentage string (e.g., "150.00%")
1309
1333
  */
1310
1334
  declare function formatCR(cr: bigint): string;
1311
1335
  /**
package/dist/index.js CHANGED
@@ -51,7 +51,8 @@ var DiamondABI = [
51
51
  name: "mintApUSD",
52
52
  inputs: [
53
53
  { name: "_lstToken", type: "address", internalType: "address" },
54
- { name: "_lstAmount", type: "uint256", internalType: "uint256" }
54
+ { name: "_lstAmount", type: "uint256", internalType: "uint256" },
55
+ { name: "_minOut", type: "uint256", internalType: "uint256" }
55
56
  ],
56
57
  outputs: [{ name: "apUSDAmount", type: "uint256", internalType: "uint256" }],
57
58
  stateMutability: "nonpayable"
@@ -61,7 +62,8 @@ var DiamondABI = [
61
62
  name: "redeemApUSD",
62
63
  inputs: [
63
64
  { name: "_lstToken", type: "address", internalType: "address" },
64
- { name: "_apUSDAmount", type: "uint256", internalType: "uint256" }
65
+ { name: "_apUSDAmount", type: "uint256", internalType: "uint256" },
66
+ { name: "_minOut", type: "uint256", internalType: "uint256" }
65
67
  ],
66
68
  outputs: [{ name: "lstAmount", type: "uint256", internalType: "uint256" }],
67
69
  stateMutability: "nonpayable"
@@ -71,7 +73,8 @@ var DiamondABI = [
71
73
  name: "mintXBNB",
72
74
  inputs: [
73
75
  { name: "_lstToken", type: "address", internalType: "address" },
74
- { name: "_lstAmount", type: "uint256", internalType: "uint256" }
76
+ { name: "_lstAmount", type: "uint256", internalType: "uint256" },
77
+ { name: "_minOut", type: "uint256", internalType: "uint256" }
75
78
  ],
76
79
  outputs: [{ name: "xBNBAmount", type: "uint256", internalType: "uint256" }],
77
80
  stateMutability: "nonpayable"
@@ -81,7 +84,8 @@ var DiamondABI = [
81
84
  name: "redeemXBNB",
82
85
  inputs: [
83
86
  { name: "_lstToken", type: "address", internalType: "address" },
84
- { name: "_xBNBAmount", type: "uint256", internalType: "uint256" }
87
+ { name: "_xBNBAmount", type: "uint256", internalType: "uint256" },
88
+ { name: "_minOut", type: "uint256", internalType: "uint256" }
85
89
  ],
86
90
  outputs: [{ name: "lstAmount", type: "uint256", internalType: "uint256" }],
87
91
  stateMutability: "nonpayable"
@@ -598,25 +602,23 @@ var AspanReadClient = class _AspanReadClient {
598
602
  * Get comprehensive protocol statistics
599
603
  */
600
604
  async getProtocolStats() {
601
- const [
602
- tvlInBNB,
603
- tvlInUSD,
604
- collateralRatio,
605
- apUSDSupply,
606
- xBNBSupply,
607
- xBNBPriceBNB,
608
- xBNBPriceUSD,
609
- effectiveLeverage
610
- ] = await Promise.all([
605
+ const [tvlInBNB, tvlInUSD, collateralRatio, apUSDSupply, xBNBSupply] = await Promise.all([
611
606
  this.getTVLInBNB(),
612
607
  this.getTVLInUSD(),
613
608
  this.getCollateralRatio(),
614
609
  this.getApUSDSupply(),
615
- this.getXBNBSupply(),
616
- this.getXBNBPriceBNB(),
617
- this.getXBNBPriceUSD(),
618
- this.getEffectiveLeverage()
610
+ this.getXBNBSupply()
619
611
  ]);
612
+ let xBNBPriceBNB = 0n;
613
+ let xBNBPriceUSD = 0n;
614
+ let effectiveLeverage = 0n;
615
+ if (xBNBSupply > 0n) {
616
+ [xBNBPriceBNB, xBNBPriceUSD, effectiveLeverage] = await Promise.all([
617
+ this.getXBNBPriceBNB(),
618
+ this.getXBNBPriceUSD(),
619
+ this.getEffectiveLeverage()
620
+ ]);
621
+ }
620
622
  return {
621
623
  tvlInBNB,
622
624
  tvlInUSD,
@@ -704,7 +706,7 @@ var AspanReadClient = class _AspanReadClient {
704
706
  abi: DiamondABI,
705
707
  functionName: "getCollateralRatio"
706
708
  });
707
- if (cr > 10000n * 10n ** 18n) {
709
+ if (cr > 100000000n) {
708
710
  return 0n;
709
711
  }
710
712
  return cr;
@@ -717,11 +719,15 @@ var AspanReadClient = class _AspanReadClient {
717
719
  }
718
720
  async getXBNBPriceBNB() {
719
721
  try {
720
- return await this.publicClient.readContract({
722
+ const price = await this.publicClient.readContract({
721
723
  address: this.diamondAddress,
722
724
  abi: DiamondABI,
723
725
  functionName: "getXBNBPriceBNB"
724
726
  });
727
+ if (price > 1000n * 10n ** 18n) {
728
+ return 0n;
729
+ }
730
+ return price;
725
731
  } catch (error) {
726
732
  if (this.isZeroSupplyError(error)) {
727
733
  return 0n;
@@ -731,11 +737,15 @@ var AspanReadClient = class _AspanReadClient {
731
737
  }
732
738
  async getXBNBPriceUSD() {
733
739
  try {
734
- return await this.publicClient.readContract({
740
+ const price = await this.publicClient.readContract({
735
741
  address: this.diamondAddress,
736
742
  abi: DiamondABI,
737
743
  functionName: "getXBNBPriceUSD"
738
744
  });
745
+ if (price > 1000000n * 10n ** 18n) {
746
+ return 0n;
747
+ }
748
+ return price;
739
749
  } catch (error) {
740
750
  if (this.isZeroSupplyError(error)) {
741
751
  return 0n;
@@ -745,11 +755,15 @@ var AspanReadClient = class _AspanReadClient {
745
755
  }
746
756
  async getEffectiveLeverage() {
747
757
  try {
748
- return await this.publicClient.readContract({
758
+ const leverage = await this.publicClient.readContract({
749
759
  address: this.diamondAddress,
750
760
  abi: DiamondABI,
751
761
  functionName: "getEffectiveLeverage"
752
762
  });
763
+ if (leverage > 100n * 10n ** 18n) {
764
+ return 0n;
765
+ }
766
+ return leverage;
753
767
  } catch (error) {
754
768
  if (this.isZeroSupplyError(error)) {
755
769
  return 0n;
@@ -807,8 +821,12 @@ var AspanReadClient = class _AspanReadClient {
807
821
  abi: DiamondABI,
808
822
  functionName: "getCurrentFees"
809
823
  });
824
+ let currentCR = result[0];
825
+ if (currentCR > 100000000n) {
826
+ currentCR = 0n;
827
+ }
810
828
  return {
811
- currentCR: result[0],
829
+ currentCR,
812
830
  tierMinCR: result[1],
813
831
  apUSDMintFee: result[2],
814
832
  apUSDRedeemFee: result[3],
@@ -1141,6 +1159,10 @@ var AspanReadClient = class _AspanReadClient {
1141
1159
  abi: DiamondABI,
1142
1160
  functionName: "getCurrentFeeTier"
1143
1161
  });
1162
+ let currentCR = result[6];
1163
+ if (currentCR > 100000000n) {
1164
+ currentCR = 0n;
1165
+ }
1144
1166
  return {
1145
1167
  minCR: result[0],
1146
1168
  apUSDMintFee: result[1],
@@ -1148,7 +1170,7 @@ var AspanReadClient = class _AspanReadClient {
1148
1170
  xBNBMintFee: result[3],
1149
1171
  xBNBRedeemFee: result[4],
1150
1172
  apUSDMintDisabled: result[5],
1151
- currentCR: result[6]
1173
+ currentCR
1152
1174
  };
1153
1175
  } catch (error) {
1154
1176
  if (this.isZeroSupplyError(error)) {
@@ -1194,9 +1216,13 @@ var AspanReadClient = class _AspanReadClient {
1194
1216
  abi: DiamondABI,
1195
1217
  functionName: "getStabilityMode"
1196
1218
  });
1219
+ let currentCR = result[1];
1220
+ if (currentCR > 100000000n) {
1221
+ currentCR = 0n;
1222
+ }
1197
1223
  return {
1198
1224
  mode: result[0],
1199
- currentCR: result[1]
1225
+ currentCR
1200
1226
  };
1201
1227
  } catch (error) {
1202
1228
  if (this.isZeroSupplyError(error)) {
@@ -1216,9 +1242,13 @@ var AspanReadClient = class _AspanReadClient {
1216
1242
  abi: DiamondABI,
1217
1243
  functionName: "canTriggerStabilityMode2"
1218
1244
  });
1245
+ let currentCR = result[1];
1246
+ if (currentCR > 100000000n) {
1247
+ currentCR = 0n;
1248
+ }
1219
1249
  return {
1220
1250
  canTrigger: result[0],
1221
- currentCR: result[1],
1251
+ currentCR,
1222
1252
  potentialConversion: result[2]
1223
1253
  };
1224
1254
  } catch (error) {
@@ -1270,7 +1300,7 @@ var AspanClient = class extends AspanReadClient {
1270
1300
  address: this.diamondAddress,
1271
1301
  abi: DiamondABI,
1272
1302
  functionName: "mintApUSD",
1273
- args: [params.lstToken, params.lstAmount]
1303
+ args: [params.lstToken, params.lstAmount, params.minOut ?? 0n]
1274
1304
  });
1275
1305
  }
1276
1306
  /**
@@ -1285,7 +1315,7 @@ var AspanClient = class extends AspanReadClient {
1285
1315
  address: this.diamondAddress,
1286
1316
  abi: DiamondABI,
1287
1317
  functionName: "redeemApUSD",
1288
- args: [params.lstToken, params.apUSDAmount]
1318
+ args: [params.lstToken, params.apUSDAmount, params.minOut ?? 0n]
1289
1319
  });
1290
1320
  }
1291
1321
  /**
@@ -1300,7 +1330,7 @@ var AspanClient = class extends AspanReadClient {
1300
1330
  address: this.diamondAddress,
1301
1331
  abi: DiamondABI,
1302
1332
  functionName: "mintXBNB",
1303
- args: [params.lstToken, params.lstAmount]
1333
+ args: [params.lstToken, params.lstAmount, params.minOut ?? 0n]
1304
1334
  });
1305
1335
  }
1306
1336
  /**
@@ -1315,7 +1345,7 @@ var AspanClient = class extends AspanReadClient {
1315
1345
  address: this.diamondAddress,
1316
1346
  abi: DiamondABI,
1317
1347
  functionName: "redeemXBNB",
1318
- args: [params.lstToken, params.xBNBAmount]
1348
+ args: [params.lstToken, params.xBNBAmount, params.minOut ?? 0n]
1319
1349
  });
1320
1350
  }
1321
1351
  // ============ Stability Pool Write Functions ============
@@ -1438,8 +1468,8 @@ function formatFeeBPS(bps) {
1438
1468
  return `${(bps / 100).toFixed(2)}%`;
1439
1469
  }
1440
1470
  function formatCR(cr) {
1441
- const percentage = cr * 100n / PRECISION;
1442
- return `${percentage}%`;
1471
+ const percentage = Number(cr) / 100;
1472
+ return `${percentage.toFixed(2)}%`;
1443
1473
  }
1444
1474
  function formatPriceUSD(price) {
1445
1475
  const dollars = price / PRICE_PRECISION;
package/dist/index.mjs CHANGED
@@ -14,7 +14,8 @@ var DiamondABI = [
14
14
  name: "mintApUSD",
15
15
  inputs: [
16
16
  { name: "_lstToken", type: "address", internalType: "address" },
17
- { name: "_lstAmount", type: "uint256", internalType: "uint256" }
17
+ { name: "_lstAmount", type: "uint256", internalType: "uint256" },
18
+ { name: "_minOut", type: "uint256", internalType: "uint256" }
18
19
  ],
19
20
  outputs: [{ name: "apUSDAmount", type: "uint256", internalType: "uint256" }],
20
21
  stateMutability: "nonpayable"
@@ -24,7 +25,8 @@ var DiamondABI = [
24
25
  name: "redeemApUSD",
25
26
  inputs: [
26
27
  { name: "_lstToken", type: "address", internalType: "address" },
27
- { name: "_apUSDAmount", type: "uint256", internalType: "uint256" }
28
+ { name: "_apUSDAmount", type: "uint256", internalType: "uint256" },
29
+ { name: "_minOut", type: "uint256", internalType: "uint256" }
28
30
  ],
29
31
  outputs: [{ name: "lstAmount", type: "uint256", internalType: "uint256" }],
30
32
  stateMutability: "nonpayable"
@@ -34,7 +36,8 @@ var DiamondABI = [
34
36
  name: "mintXBNB",
35
37
  inputs: [
36
38
  { name: "_lstToken", type: "address", internalType: "address" },
37
- { name: "_lstAmount", type: "uint256", internalType: "uint256" }
39
+ { name: "_lstAmount", type: "uint256", internalType: "uint256" },
40
+ { name: "_minOut", type: "uint256", internalType: "uint256" }
38
41
  ],
39
42
  outputs: [{ name: "xBNBAmount", type: "uint256", internalType: "uint256" }],
40
43
  stateMutability: "nonpayable"
@@ -44,7 +47,8 @@ var DiamondABI = [
44
47
  name: "redeemXBNB",
45
48
  inputs: [
46
49
  { name: "_lstToken", type: "address", internalType: "address" },
47
- { name: "_xBNBAmount", type: "uint256", internalType: "uint256" }
50
+ { name: "_xBNBAmount", type: "uint256", internalType: "uint256" },
51
+ { name: "_minOut", type: "uint256", internalType: "uint256" }
48
52
  ],
49
53
  outputs: [{ name: "lstAmount", type: "uint256", internalType: "uint256" }],
50
54
  stateMutability: "nonpayable"
@@ -561,25 +565,23 @@ var AspanReadClient = class _AspanReadClient {
561
565
  * Get comprehensive protocol statistics
562
566
  */
563
567
  async getProtocolStats() {
564
- const [
565
- tvlInBNB,
566
- tvlInUSD,
567
- collateralRatio,
568
- apUSDSupply,
569
- xBNBSupply,
570
- xBNBPriceBNB,
571
- xBNBPriceUSD,
572
- effectiveLeverage
573
- ] = await Promise.all([
568
+ const [tvlInBNB, tvlInUSD, collateralRatio, apUSDSupply, xBNBSupply] = await Promise.all([
574
569
  this.getTVLInBNB(),
575
570
  this.getTVLInUSD(),
576
571
  this.getCollateralRatio(),
577
572
  this.getApUSDSupply(),
578
- this.getXBNBSupply(),
579
- this.getXBNBPriceBNB(),
580
- this.getXBNBPriceUSD(),
581
- this.getEffectiveLeverage()
573
+ this.getXBNBSupply()
582
574
  ]);
575
+ let xBNBPriceBNB = 0n;
576
+ let xBNBPriceUSD = 0n;
577
+ let effectiveLeverage = 0n;
578
+ if (xBNBSupply > 0n) {
579
+ [xBNBPriceBNB, xBNBPriceUSD, effectiveLeverage] = await Promise.all([
580
+ this.getXBNBPriceBNB(),
581
+ this.getXBNBPriceUSD(),
582
+ this.getEffectiveLeverage()
583
+ ]);
584
+ }
583
585
  return {
584
586
  tvlInBNB,
585
587
  tvlInUSD,
@@ -667,7 +669,7 @@ var AspanReadClient = class _AspanReadClient {
667
669
  abi: DiamondABI,
668
670
  functionName: "getCollateralRatio"
669
671
  });
670
- if (cr > 10000n * 10n ** 18n) {
672
+ if (cr > 100000000n) {
671
673
  return 0n;
672
674
  }
673
675
  return cr;
@@ -680,11 +682,15 @@ var AspanReadClient = class _AspanReadClient {
680
682
  }
681
683
  async getXBNBPriceBNB() {
682
684
  try {
683
- return await this.publicClient.readContract({
685
+ const price = await this.publicClient.readContract({
684
686
  address: this.diamondAddress,
685
687
  abi: DiamondABI,
686
688
  functionName: "getXBNBPriceBNB"
687
689
  });
690
+ if (price > 1000n * 10n ** 18n) {
691
+ return 0n;
692
+ }
693
+ return price;
688
694
  } catch (error) {
689
695
  if (this.isZeroSupplyError(error)) {
690
696
  return 0n;
@@ -694,11 +700,15 @@ var AspanReadClient = class _AspanReadClient {
694
700
  }
695
701
  async getXBNBPriceUSD() {
696
702
  try {
697
- return await this.publicClient.readContract({
703
+ const price = await this.publicClient.readContract({
698
704
  address: this.diamondAddress,
699
705
  abi: DiamondABI,
700
706
  functionName: "getXBNBPriceUSD"
701
707
  });
708
+ if (price > 1000000n * 10n ** 18n) {
709
+ return 0n;
710
+ }
711
+ return price;
702
712
  } catch (error) {
703
713
  if (this.isZeroSupplyError(error)) {
704
714
  return 0n;
@@ -708,11 +718,15 @@ var AspanReadClient = class _AspanReadClient {
708
718
  }
709
719
  async getEffectiveLeverage() {
710
720
  try {
711
- return await this.publicClient.readContract({
721
+ const leverage = await this.publicClient.readContract({
712
722
  address: this.diamondAddress,
713
723
  abi: DiamondABI,
714
724
  functionName: "getEffectiveLeverage"
715
725
  });
726
+ if (leverage > 100n * 10n ** 18n) {
727
+ return 0n;
728
+ }
729
+ return leverage;
716
730
  } catch (error) {
717
731
  if (this.isZeroSupplyError(error)) {
718
732
  return 0n;
@@ -770,8 +784,12 @@ var AspanReadClient = class _AspanReadClient {
770
784
  abi: DiamondABI,
771
785
  functionName: "getCurrentFees"
772
786
  });
787
+ let currentCR = result[0];
788
+ if (currentCR > 100000000n) {
789
+ currentCR = 0n;
790
+ }
773
791
  return {
774
- currentCR: result[0],
792
+ currentCR,
775
793
  tierMinCR: result[1],
776
794
  apUSDMintFee: result[2],
777
795
  apUSDRedeemFee: result[3],
@@ -1104,6 +1122,10 @@ var AspanReadClient = class _AspanReadClient {
1104
1122
  abi: DiamondABI,
1105
1123
  functionName: "getCurrentFeeTier"
1106
1124
  });
1125
+ let currentCR = result[6];
1126
+ if (currentCR > 100000000n) {
1127
+ currentCR = 0n;
1128
+ }
1107
1129
  return {
1108
1130
  minCR: result[0],
1109
1131
  apUSDMintFee: result[1],
@@ -1111,7 +1133,7 @@ var AspanReadClient = class _AspanReadClient {
1111
1133
  xBNBMintFee: result[3],
1112
1134
  xBNBRedeemFee: result[4],
1113
1135
  apUSDMintDisabled: result[5],
1114
- currentCR: result[6]
1136
+ currentCR
1115
1137
  };
1116
1138
  } catch (error) {
1117
1139
  if (this.isZeroSupplyError(error)) {
@@ -1157,9 +1179,13 @@ var AspanReadClient = class _AspanReadClient {
1157
1179
  abi: DiamondABI,
1158
1180
  functionName: "getStabilityMode"
1159
1181
  });
1182
+ let currentCR = result[1];
1183
+ if (currentCR > 100000000n) {
1184
+ currentCR = 0n;
1185
+ }
1160
1186
  return {
1161
1187
  mode: result[0],
1162
- currentCR: result[1]
1188
+ currentCR
1163
1189
  };
1164
1190
  } catch (error) {
1165
1191
  if (this.isZeroSupplyError(error)) {
@@ -1179,9 +1205,13 @@ var AspanReadClient = class _AspanReadClient {
1179
1205
  abi: DiamondABI,
1180
1206
  functionName: "canTriggerStabilityMode2"
1181
1207
  });
1208
+ let currentCR = result[1];
1209
+ if (currentCR > 100000000n) {
1210
+ currentCR = 0n;
1211
+ }
1182
1212
  return {
1183
1213
  canTrigger: result[0],
1184
- currentCR: result[1],
1214
+ currentCR,
1185
1215
  potentialConversion: result[2]
1186
1216
  };
1187
1217
  } catch (error) {
@@ -1233,7 +1263,7 @@ var AspanClient = class extends AspanReadClient {
1233
1263
  address: this.diamondAddress,
1234
1264
  abi: DiamondABI,
1235
1265
  functionName: "mintApUSD",
1236
- args: [params.lstToken, params.lstAmount]
1266
+ args: [params.lstToken, params.lstAmount, params.minOut ?? 0n]
1237
1267
  });
1238
1268
  }
1239
1269
  /**
@@ -1248,7 +1278,7 @@ var AspanClient = class extends AspanReadClient {
1248
1278
  address: this.diamondAddress,
1249
1279
  abi: DiamondABI,
1250
1280
  functionName: "redeemApUSD",
1251
- args: [params.lstToken, params.apUSDAmount]
1281
+ args: [params.lstToken, params.apUSDAmount, params.minOut ?? 0n]
1252
1282
  });
1253
1283
  }
1254
1284
  /**
@@ -1263,7 +1293,7 @@ var AspanClient = class extends AspanReadClient {
1263
1293
  address: this.diamondAddress,
1264
1294
  abi: DiamondABI,
1265
1295
  functionName: "mintXBNB",
1266
- args: [params.lstToken, params.lstAmount]
1296
+ args: [params.lstToken, params.lstAmount, params.minOut ?? 0n]
1267
1297
  });
1268
1298
  }
1269
1299
  /**
@@ -1278,7 +1308,7 @@ var AspanClient = class extends AspanReadClient {
1278
1308
  address: this.diamondAddress,
1279
1309
  abi: DiamondABI,
1280
1310
  functionName: "redeemXBNB",
1281
- args: [params.lstToken, params.xBNBAmount]
1311
+ args: [params.lstToken, params.xBNBAmount, params.minOut ?? 0n]
1282
1312
  });
1283
1313
  }
1284
1314
  // ============ Stability Pool Write Functions ============
@@ -1401,8 +1431,8 @@ function formatFeeBPS(bps) {
1401
1431
  return `${(bps / 100).toFixed(2)}%`;
1402
1432
  }
1403
1433
  function formatCR(cr) {
1404
- const percentage = cr * 100n / PRECISION;
1405
- return `${percentage}%`;
1434
+ const percentage = Number(cr) / 100;
1435
+ return `${percentage.toFixed(2)}%`;
1406
1436
  }
1407
1437
  function formatPriceUSD(price) {
1408
1438
  const dollars = price / PRICE_PRECISION;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aspan/sdk",
3
- "version": "0.1.7",
3
+ "version": "0.1.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",
@@ -9,7 +9,8 @@ export const DiamondABI = [
9
9
  name: "mintApUSD",
10
10
  inputs: [
11
11
  { name: "_lstToken", type: "address", internalType: "address" },
12
- { name: "_lstAmount", type: "uint256", internalType: "uint256" }
12
+ { name: "_lstAmount", type: "uint256", internalType: "uint256" },
13
+ { name: "_minOut", type: "uint256", internalType: "uint256" }
13
14
  ],
14
15
  outputs: [{ name: "apUSDAmount", type: "uint256", internalType: "uint256" }],
15
16
  stateMutability: "nonpayable"
@@ -19,7 +20,8 @@ export const DiamondABI = [
19
20
  name: "redeemApUSD",
20
21
  inputs: [
21
22
  { name: "_lstToken", type: "address", internalType: "address" },
22
- { name: "_apUSDAmount", type: "uint256", internalType: "uint256" }
23
+ { name: "_apUSDAmount", type: "uint256", internalType: "uint256" },
24
+ { name: "_minOut", type: "uint256", internalType: "uint256" }
23
25
  ],
24
26
  outputs: [{ name: "lstAmount", type: "uint256", internalType: "uint256" }],
25
27
  stateMutability: "nonpayable"
@@ -29,7 +31,8 @@ export const DiamondABI = [
29
31
  name: "mintXBNB",
30
32
  inputs: [
31
33
  { name: "_lstToken", type: "address", internalType: "address" },
32
- { name: "_lstAmount", type: "uint256", internalType: "uint256" }
34
+ { name: "_lstAmount", type: "uint256", internalType: "uint256" },
35
+ { name: "_minOut", type: "uint256", internalType: "uint256" }
33
36
  ],
34
37
  outputs: [{ name: "xBNBAmount", type: "uint256", internalType: "uint256" }],
35
38
  stateMutability: "nonpayable"
@@ -39,7 +42,8 @@ export const DiamondABI = [
39
42
  name: "redeemXBNB",
40
43
  inputs: [
41
44
  { name: "_lstToken", type: "address", internalType: "address" },
42
- { name: "_xBNBAmount", type: "uint256", internalType: "uint256" }
45
+ { name: "_xBNBAmount", type: "uint256", internalType: "uint256" },
46
+ { name: "_minOut", type: "uint256", internalType: "uint256" }
43
47
  ],
44
48
  outputs: [{ name: "lstAmount", type: "uint256", internalType: "uint256" }],
45
49
  stateMutability: "nonpayable"
package/src/bot/config.ts CHANGED
@@ -17,7 +17,7 @@ export interface BotConfig {
17
17
  statsReportInterval: number;
18
18
  crCheckInterval: number;
19
19
 
20
- // CR Thresholds (18 decimals: 1.5e18 = 150%)
20
+ // CR Thresholds (BPS: 15000 = 150%, matches contract format)
21
21
  crThresholdMode1: bigint;
22
22
  crThresholdMode2: bigint;
23
23
 
@@ -59,13 +59,13 @@ export function loadConfig(): BotConfig {
59
59
  ), // 5 min
60
60
  crCheckInterval: parseInt(process.env.CR_CHECK_INTERVAL_MS ?? "30000", 10), // 30 sec
61
61
 
62
- // CR Thresholds
62
+ // CR Thresholds (BPS format to match contract)
63
63
  crThresholdMode1: BigInt(
64
- process.env.CR_THRESHOLD_MODE1 ?? "1500000000000000000"
65
- ), // 150%
64
+ process.env.CR_THRESHOLD_MODE1 ?? "15000"
65
+ ), // 150% in BPS
66
66
  crThresholdMode2: BigInt(
67
- process.env.CR_THRESHOLD_MODE2 ?? "1300000000000000000"
68
- ), // 130%
67
+ process.env.CR_THRESHOLD_MODE2 ?? "13000"
68
+ ), // 130% in BPS
69
69
 
70
70
  // TVL
71
71
  tvlImpactThresholdPercent: parseFloat(
@@ -79,6 +79,14 @@ export class CRMonitor {
79
79
  const currentCR = stabilityMode.currentCR;
80
80
  const now = Date.now();
81
81
 
82
+ // Skip alerts when protocol is effectively empty
83
+ // CR < 100 BPS (1%) indicates negligible TVL/supply, not a real crisis
84
+ const MIN_MEANINGFUL_CR = 100n; // 1% in BPS
85
+ if (currentCR < MIN_MEANINGFUL_CR) {
86
+ console.log(`[CRMonitor] Protocol effectively empty (CR = ${formatCR(currentCR)}), skipping alerts`);
87
+ return;
88
+ }
89
+
82
90
  // Check Mode 2 threshold (< 130%)
83
91
  if (currentCR < this.config.crThresholdMode2) {
84
92
  if (!this.state.mode2Triggered || this.shouldRepeatAlert(now)) {
@@ -143,7 +143,7 @@ export class StatsMonitor {
143
143
  },
144
144
  {
145
145
  title: "xBNB Supply",
146
- value: formatAmount(stats.xBNBSupply, 4),
146
+ value: formatAmount(stats.xBNBSupply, 18),
147
147
  short: true,
148
148
  },
149
149
  {
package/src/client.ts CHANGED
@@ -102,25 +102,28 @@ export class AspanReadClient {
102
102
  * Get comprehensive protocol statistics
103
103
  */
104
104
  async getProtocolStats(): Promise<ProtocolStats> {
105
- const [
106
- tvlInBNB,
107
- tvlInUSD,
108
- collateralRatio,
109
- apUSDSupply,
110
- xBNBSupply,
111
- xBNBPriceBNB,
112
- xBNBPriceUSD,
113
- effectiveLeverage,
114
- ] = await Promise.all([
115
- this.getTVLInBNB(),
116
- this.getTVLInUSD(),
117
- this.getCollateralRatio(),
118
- this.getApUSDSupply(),
119
- this.getXBNBSupply(),
120
- this.getXBNBPriceBNB(),
121
- this.getXBNBPriceUSD(),
122
- this.getEffectiveLeverage(),
123
- ]);
105
+ // First get supplies to determine if xBNB exists
106
+ const [tvlInBNB, tvlInUSD, collateralRatio, apUSDSupply, xBNBSupply] =
107
+ await Promise.all([
108
+ this.getTVLInBNB(),
109
+ this.getTVLInUSD(),
110
+ this.getCollateralRatio(),
111
+ this.getApUSDSupply(),
112
+ this.getXBNBSupply(),
113
+ ]);
114
+
115
+ // Only fetch xBNB price and leverage if xBNB supply exists
116
+ let xBNBPriceBNB = 0n;
117
+ let xBNBPriceUSD = 0n;
118
+ let effectiveLeverage = 0n;
119
+
120
+ if (xBNBSupply > 0n) {
121
+ [xBNBPriceBNB, xBNBPriceUSD, effectiveLeverage] = await Promise.all([
122
+ this.getXBNBPriceBNB(),
123
+ this.getXBNBPriceUSD(),
124
+ this.getEffectiveLeverage(),
125
+ ]);
126
+ }
124
127
 
125
128
  return {
126
129
  tvlInBNB,
@@ -218,8 +221,8 @@ export class AspanReadClient {
218
221
  functionName: "getCollateralRatio",
219
222
  });
220
223
  // Contract returns max uint256 when supply is zero (infinite CR)
221
- // Treat anything above 1000000% (10000 * 1e18) as zero/undefined
222
- if (cr > 10000n * 10n ** 18n) {
224
+ // CR is in BPS format (10000 = 100%), anything above 100000000 BPS is invalid
225
+ if (cr > 100000000n) {
223
226
  return 0n;
224
227
  }
225
228
  return cr;
@@ -234,11 +237,17 @@ export class AspanReadClient {
234
237
 
235
238
  async getXBNBPriceBNB(): Promise<bigint> {
236
239
  try {
237
- return await this.publicClient.readContract({
240
+ const price = await this.publicClient.readContract({
238
241
  address: this.diamondAddress,
239
242
  abi: DiamondABI,
240
243
  functionName: "getXBNBPriceBNB",
241
244
  });
245
+ // Contract returns extreme values when xBNB supply is zero
246
+ // Normal xBNB price should be < 1000 BNB (1000 * 1e18)
247
+ if (price > 1000n * 10n ** 18n) {
248
+ return 0n;
249
+ }
250
+ return price;
242
251
  } catch (error) {
243
252
  // Price undefined when no xBNB exists
244
253
  if (this.isZeroSupplyError(error)) {
@@ -250,11 +259,17 @@ export class AspanReadClient {
250
259
 
251
260
  async getXBNBPriceUSD(): Promise<bigint> {
252
261
  try {
253
- return await this.publicClient.readContract({
262
+ const price = await this.publicClient.readContract({
254
263
  address: this.diamondAddress,
255
264
  abi: DiamondABI,
256
265
  functionName: "getXBNBPriceUSD",
257
266
  });
267
+ // Contract returns extreme values when xBNB supply is zero
268
+ // xBNBPriceUSD is in 18 decimals, normal price should be < $1,000,000 (1e6 * 1e18)
269
+ if (price > 1000000n * 10n ** 18n) {
270
+ return 0n;
271
+ }
272
+ return price;
258
273
  } catch (error) {
259
274
  // Price undefined when no xBNB exists
260
275
  if (this.isZeroSupplyError(error)) {
@@ -266,11 +281,17 @@ export class AspanReadClient {
266
281
 
267
282
  async getEffectiveLeverage(): Promise<bigint> {
268
283
  try {
269
- return await this.publicClient.readContract({
284
+ const leverage = await this.publicClient.readContract({
270
285
  address: this.diamondAddress,
271
286
  abi: DiamondABI,
272
287
  functionName: "getEffectiveLeverage",
273
288
  });
289
+ // Contract returns extreme values when xBNB supply is zero
290
+ // Normal leverage should be < 100x (100 * 1e18)
291
+ if (leverage > 100n * 10n ** 18n) {
292
+ return 0n;
293
+ }
294
+ return leverage;
274
295
  } catch (error) {
275
296
  // Leverage undefined when no xBNB exists
276
297
  if (this.isZeroSupplyError(error)) {
@@ -336,8 +357,15 @@ export class AspanReadClient {
336
357
  functionName: "getCurrentFees",
337
358
  });
338
359
 
360
+ let currentCR = result[0];
361
+ // Contract returns max uint256 when supply is zero (infinite CR)
362
+ // CR is in BPS format (10000 = 100%)
363
+ if (currentCR > 100000000n) {
364
+ currentCR = 0n;
365
+ }
366
+
339
367
  return {
340
- currentCR: result[0],
368
+ currentCR,
341
369
  tierMinCR: result[1],
342
370
  apUSDMintFee: result[2],
343
371
  apUSDRedeemFee: result[3],
@@ -713,6 +741,13 @@ export class AspanReadClient {
713
741
  functionName: "getCurrentFeeTier",
714
742
  });
715
743
 
744
+ let currentCR = result[6];
745
+ // Contract returns max uint256 when supply is zero (infinite CR)
746
+ // CR is in BPS format (10000 = 100%)
747
+ if (currentCR > 100000000n) {
748
+ currentCR = 0n;
749
+ }
750
+
716
751
  return {
717
752
  minCR: result[0],
718
753
  apUSDMintFee: result[1],
@@ -720,7 +755,7 @@ export class AspanReadClient {
720
755
  xBNBMintFee: result[3],
721
756
  xBNBRedeemFee: result[4],
722
757
  apUSDMintDisabled: result[5],
723
- currentCR: result[6],
758
+ currentCR,
724
759
  };
725
760
  } catch (error) {
726
761
  // Return default fee tier when protocol is empty
@@ -773,9 +808,16 @@ export class AspanReadClient {
773
808
  functionName: "getStabilityMode",
774
809
  });
775
810
 
811
+ let currentCR = result[1];
812
+ // Contract returns max uint256 when supply is zero (infinite CR)
813
+ // CR is in BPS format (10000 = 100%)
814
+ if (currentCR > 100000000n) {
815
+ currentCR = 0n;
816
+ }
817
+
776
818
  return {
777
819
  mode: result[0],
778
- currentCR: result[1],
820
+ currentCR,
779
821
  };
780
822
  } catch (error) {
781
823
  // Return normal mode when protocol is empty
@@ -797,9 +839,16 @@ export class AspanReadClient {
797
839
  functionName: "canTriggerStabilityMode2",
798
840
  });
799
841
 
842
+ let currentCR = result[1];
843
+ // Contract returns max uint256 when supply is zero (infinite CR)
844
+ // CR is in BPS format (10000 = 100%)
845
+ if (currentCR > 100000000n) {
846
+ currentCR = 0n;
847
+ }
848
+
800
849
  return {
801
850
  canTrigger: result[0],
802
- currentCR: result[1],
851
+ currentCR,
803
852
  potentialConversion: result[2],
804
853
  };
805
854
  } catch (error) {
@@ -866,7 +915,7 @@ export class AspanClient extends AspanReadClient {
866
915
  address: this.diamondAddress,
867
916
  abi: DiamondABI,
868
917
  functionName: "mintApUSD",
869
- args: [params.lstToken, params.lstAmount],
918
+ args: [params.lstToken, params.lstAmount, params.minOut ?? 0n],
870
919
  });
871
920
  }
872
921
 
@@ -882,7 +931,7 @@ export class AspanClient extends AspanReadClient {
882
931
  address: this.diamondAddress,
883
932
  abi: DiamondABI,
884
933
  functionName: "redeemApUSD",
885
- args: [params.lstToken, params.apUSDAmount],
934
+ args: [params.lstToken, params.apUSDAmount, params.minOut ?? 0n],
886
935
  });
887
936
  }
888
937
 
@@ -898,7 +947,7 @@ export class AspanClient extends AspanReadClient {
898
947
  address: this.diamondAddress,
899
948
  abi: DiamondABI,
900
949
  functionName: "mintXBNB",
901
- args: [params.lstToken, params.lstAmount],
950
+ args: [params.lstToken, params.lstAmount, params.minOut ?? 0n],
902
951
  });
903
952
  }
904
953
 
@@ -914,7 +963,7 @@ export class AspanClient extends AspanReadClient {
914
963
  address: this.diamondAddress,
915
964
  abi: DiamondABI,
916
965
  functionName: "redeemXBNB",
917
- args: [params.lstToken, params.xBNBAmount],
966
+ args: [params.lstToken, params.xBNBAmount, params.minOut ?? 0n],
918
967
  });
919
968
  }
920
969
 
package/src/index.ts CHANGED
@@ -102,12 +102,13 @@ export function formatFeeBPS(bps: number): string {
102
102
 
103
103
  /**
104
104
  * Format collateral ratio to percentage string
105
- * @param cr Collateral ratio (18 decimals, 1e18 = 100%)
106
- * @returns Percentage string (e.g., "150%")
105
+ * @param cr Collateral ratio in BPS (10000 = 100%, contract format)
106
+ * @returns Percentage string (e.g., "150.00%")
107
107
  */
108
108
  export function formatCR(cr: bigint): string {
109
- const percentage = (cr * 100n) / PRECISION;
110
- return `${percentage}%`;
109
+ // CR is in BPS: 15000 = 150%
110
+ const percentage = Number(cr) / 100;
111
+ return `${percentage.toFixed(2)}%`;
111
112
  }
112
113
 
113
114
  /**
package/src/types.ts CHANGED
@@ -90,24 +90,32 @@ export interface StabilityMode2Result {
90
90
  export interface MintApUSDParams {
91
91
  lstToken: Address;
92
92
  lstAmount: bigint;
93
+ /** Minimum apUSD output for slippage protection (default: 0n) */
94
+ minOut?: bigint;
93
95
  }
94
96
 
95
97
  /** Parameters for redeeming apUSD */
96
98
  export interface RedeemApUSDParams {
97
99
  lstToken: Address;
98
100
  apUSDAmount: bigint;
101
+ /** Minimum LST output for slippage protection (default: 0n) */
102
+ minOut?: bigint;
99
103
  }
100
104
 
101
105
  /** Parameters for minting xBNB */
102
106
  export interface MintXBNBParams {
103
107
  lstToken: Address;
104
108
  lstAmount: bigint;
109
+ /** Minimum xBNB output for slippage protection (default: 0n) */
110
+ minOut?: bigint;
105
111
  }
106
112
 
107
113
  /** Parameters for redeeming xBNB */
108
114
  export interface RedeemXBNBParams {
109
115
  lstToken: Address;
110
116
  xBNBAmount: bigint;
117
+ /** Minimum LST output for slippage protection (default: 0n) */
118
+ minOut?: bigint;
111
119
  }
112
120
 
113
121
  /** Parameters for depositing to stability pool */