@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 +55 -11
- package/dist/index.d.mts +26 -2
- package/dist/index.d.ts +26 -2
- package/dist/index.js +62 -32
- package/dist/index.mjs +62 -32
- package/package.json +1 -1
- package/src/abi/diamond.ts +8 -4
- package/src/bot/config.ts +6 -6
- package/src/bot/monitors/cr-monitor.ts +8 -0
- package/src/bot/monitors/stats-monitor.ts +1 -1
- package/src/client.ts +81 -32
- package/src/index.ts +5 -4
- package/src/types.ts +8 -0
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:
|
|
128
|
-
const
|
|
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
|
|
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
|
-
|
|
185
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 >
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 >
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
package/src/abi/diamond.ts
CHANGED
|
@@ -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 (
|
|
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 ?? "
|
|
65
|
-
), // 150%
|
|
64
|
+
process.env.CR_THRESHOLD_MODE1 ?? "15000"
|
|
65
|
+
), // 150% in BPS
|
|
66
66
|
crThresholdMode2: BigInt(
|
|
67
|
-
process.env.CR_THRESHOLD_MODE2 ?? "
|
|
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)) {
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
//
|
|
222
|
-
if (cr >
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
110
|
-
|
|
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 */
|