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