@aspan/sdk 0.1.7 → 0.2.0

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
@@ -16,6 +16,7 @@ pnpm add @aspan/sdk
16
16
  | Contract | Address |
17
17
  |----------|---------|
18
18
  | **Diamond (Main Entry)** | `0x10d25Ae0690533e0BA9E64EC7ae77dbD4fE8A46f` |
19
+ | **Router** | `0x46d4Bb3bB2d0A0a85739A6d467eFB3025388AE2E` |
19
20
  | **ApUSD** | `0x1977097E2E5697A6DD91b6732F368a14F50f6B3d` |
20
21
  | **XBNB** | `0xB78eB4d5928bAb158Eb23c3154544084cD2661d5` |
21
22
  | **SApUSD** | `0xE2BE739C4aA4126ee72D612d9548C38B1B0e5A1b` |
@@ -85,6 +86,197 @@ const hash = await client.mintApUSD({
85
86
 
86
87
  ---
87
88
 
89
+ ## Router (One-Click Operations)
90
+
91
+ AspanRouter is a periphery contract that enables users to mint/redeem apUSD or xBNB in a single transaction using common tokens like USDT, USDC, BNB, or WBNB. The router automatically handles DEX swaps, LST staking, and minting.
92
+
93
+ ### Router Client Setup
94
+
95
+ ```typescript
96
+ import { createRouterClient, AspanRouterClient } from "@aspan/sdk";
97
+ import { privateKeyToAccount } from "viem/accounts";
98
+ import { zeroAddress } from "viem";
99
+
100
+ const ROUTER = "0x46d4Bb3bB2d0A0a85739A6d467eFB3025388AE2E";
101
+
102
+ // Node.js
103
+ const account = privateKeyToAccount("0x...");
104
+ const router = createRouterClient(ROUTER, account);
105
+
106
+ // Browser (wagmi)
107
+ const router = new AspanRouterClient({
108
+ routerAddress: ROUTER,
109
+ walletClient, // from useWalletClient()
110
+ });
111
+ ```
112
+
113
+ ### Swap USDT/USDC → apUSD (One-Click)
114
+
115
+ ```typescript
116
+ import { parseAmount, type SwapAndMintParams } from "@aspan/sdk";
117
+ import { zeroAddress } from "viem";
118
+
119
+ const USDT = "0x55d398326f99059fF775485246999027B3197955";
120
+
121
+ // Full control with SwapParams + MintParams
122
+ const params: SwapAndMintParams = {
123
+ swapParams: {
124
+ inputToken: USDT,
125
+ inputAmount: parseAmount("100"), // 100 USDT
126
+ targetLST: zeroAddress, // Use default LST
127
+ minLSTOut: 0n, // Or set slippage protection
128
+ poolFee: 2500, // 0.25% V3 pool fee
129
+ useV2: true, // Use PancakeSwap V2
130
+ },
131
+ mintParams: {
132
+ mintXBNB: false, // Mint apUSD
133
+ minMintOut: parseAmount("99"), // Min 99 apUSD (1% slippage)
134
+ recipient: zeroAddress, // Send to msg.sender
135
+ deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
136
+ },
137
+ };
138
+
139
+ // Approve USDT first, then swap+mint
140
+ const hash = await router.swapAndMintApUSD(params);
141
+ await router.waitForTransaction(hash);
142
+ ```
143
+
144
+ ### Simplified: Swap → apUSD (Default LST)
145
+
146
+ ```typescript
147
+ // Simpler API using default LST and V2
148
+ const hash = await router.swapAndMintApUSDDefault({
149
+ inputToken: USDT,
150
+ inputAmount: parseAmount("100"),
151
+ minMintOut: parseAmount("99"),
152
+ deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
153
+ });
154
+ ```
155
+
156
+ ### Stake Native BNB → apUSD (Bypasses DEX)
157
+
158
+ Direct BNB staking gets better rates for large amounts by bypassing DEX.
159
+
160
+ ```typescript
161
+ // Stake BNB directly to Lista/Astherus → mint apUSD
162
+ const hash = await router.stakeAndMintApUSD(
163
+ parseAmount("99"), // minMintOut
164
+ parseAmount("1"), // 1 BNB to stake (sent as value)
165
+ );
166
+
167
+ // Or with full control
168
+ const hash = await router.stakeAndMint({
169
+ targetLST: "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B", // slisBNB
170
+ mintXBNB: false,
171
+ minMintOut: parseAmount("99"),
172
+ deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
173
+ value: parseAmount("1"), // 1 BNB
174
+ });
175
+ ```
176
+
177
+ ### Swap → xBNB (Leveraged Long)
178
+
179
+ ```typescript
180
+ // Full params
181
+ const hash = await router.swapAndMintXBNB(params);
182
+
183
+ // Or simplified
184
+ const hash = await router.swapAndMintXBNBDefault({
185
+ inputToken: USDT,
186
+ inputAmount: parseAmount("1000"),
187
+ minMintOut: parseAmount("1"), // Min 1 xBNB
188
+ deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
189
+ });
190
+
191
+ // Or stake BNB directly
192
+ const hash = await router.stakeAndMintXBNB(parseAmount("1"), parseAmount("10"));
193
+ ```
194
+
195
+ ### Direct Mint/Redeem via Router
196
+
197
+ If you already have LST, use direct functions (still routes through Diamond):
198
+
199
+ ```typescript
200
+ // Mint apUSD with LST
201
+ const hash = await router.mintApUSD({
202
+ lst: "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B", // slisBNB
203
+ lstAmount: parseAmount("10"),
204
+ minOut: parseAmount("99"),
205
+ });
206
+
207
+ // Redeem apUSD for LST
208
+ const hash = await router.redeemApUSD({
209
+ lst: "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B",
210
+ apUSDAmount: parseAmount("100"),
211
+ minOut: parseAmount("0.9"),
212
+ });
213
+ ```
214
+
215
+ ### Redeem + Swap to USDT/BNB
216
+
217
+ ```typescript
218
+ // Redeem apUSD and swap LST to USDT
219
+ const hash = await router.redeemApUSDAndSwap({
220
+ lst: "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B",
221
+ amount: parseAmount("100"), // 100 apUSD
222
+ outputToken: USDT, // Get USDT
223
+ minOut: parseAmount("99"), // Min 99 USDT
224
+ deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
225
+ useV2: true,
226
+ poolFee: 2500,
227
+ });
228
+
229
+ // Redeem and get native BNB instantly (via DEX swap)
230
+ const hash = await router.redeemApUSDAndUnstake({
231
+ lst: "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B",
232
+ amount: parseAmount("100"),
233
+ minBNBOut: parseAmount("0.1"),
234
+ deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
235
+ });
236
+ ```
237
+
238
+ ### Native Unstake (7-15 Day Unbonding)
239
+
240
+ For no-slippage BNB redemption, use Lista's native unstake:
241
+
242
+ ```typescript
243
+ // Request unstake (starts 7-15 day unbonding period)
244
+ const hash = await router.redeemApUSDAndRequestUnstake(parseAmount("100"));
245
+ const receipt = await router.waitForTransaction(hash);
246
+ // Parse event to get requestIndex...
247
+
248
+ // Check withdrawal status
249
+ const indices = await router.getUserWithdrawalIndices(account.address);
250
+ for (const idx of indices) {
251
+ const status = await router.getWithdrawalStatus(idx);
252
+ console.log(`Request ${idx}: claimable=${status.isClaimable}, amount=${status.bnbAmount}`);
253
+ }
254
+
255
+ // Claim BNB after unbonding
256
+ const hash = await router.claimUnstake(requestIndex);
257
+ ```
258
+
259
+ ### Router View Functions
260
+
261
+ ```typescript
262
+ // Check supported tokens
263
+ const defaultLST = await router.getDefaultLST();
264
+ const isSupported = await router.isSupportedInputToken(USDT);
265
+ const isLSTSupported = await router.isSupportedLST(slisBNB);
266
+
267
+ // Preview swap output
268
+ const output = await router.getExpectedOutput(USDT, parseAmount("100"), zeroAddress, false);
269
+ console.log("Expected LST:", output.expectedLST);
270
+ console.log("Expected apUSD:", output.expectedMint);
271
+
272
+ // Get token addresses
273
+ const wbnb = await router.getWBNB();
274
+ const usdt = await router.getUSDT();
275
+ const slisBNB = await router.getSlisBNB();
276
+ ```
277
+
278
+ ---
279
+
88
280
  ## User Flows
89
281
 
90
282
  ### Flow 1: Mint apUSD (Stablecoin)
@@ -124,8 +316,12 @@ function MintComponent() {
124
316
  // Step 3: Approve LST (use wagmi's useWriteContract or viem)
125
317
  // ...
126
318
 
127
- // Step 4: Mint apUSD
128
- const hash = await client.mintApUSD({ lstToken: SLISBNB, lstAmount });
319
+ // Step 4: Calculate minOut for slippage protection (e.g., 0.5% slippage)
320
+ const expectedApUSD = lstAmount; // Simplified, use actual calculation
321
+ const minOut = expectedApUSD * 995n / 1000n; // 0.5% slippage
322
+
323
+ // Step 5: Mint apUSD
324
+ const hash = await client.mintApUSD({ lstToken: SLISBNB, lstAmount, minOut });
129
325
  const receipt = await client.waitForTransaction(hash);
130
326
  console.log("Minted apUSD:", receipt.status);
131
327
  };
@@ -161,8 +357,9 @@ if (fees.apUSDMintDisabled) throw new Error("Minting disabled in current CR");
161
357
  // args: [DIAMOND, lstAmount],
162
358
  // });
163
359
 
164
- // Step 4: Mint apUSD
165
- const hash = await client.mintApUSD({ lstToken: SLISBNB, lstAmount });
360
+ // Step 4: Mint apUSD with slippage protection
361
+ const minOut = lstAmount * 995n / 1000n; // 0.5% slippage tolerance
362
+ const hash = await client.mintApUSD({ lstToken: SLISBNB, lstAmount, minOut });
166
363
  const receipt = await client.waitForTransaction(hash);
167
364
  console.log("Minted apUSD:", receipt.status);
168
365
  ```
@@ -181,8 +378,13 @@ const maxRedeemable = (collateral * lstPrice) / parseAmount("1");
181
378
  console.log("Max redeemable:", formatAmount(maxRedeemable), "USD");
182
379
 
183
380
  // Step 2: Approve apUSD spending (use viem directly)
184
- // Step 3: Redeem
185
- const hash = await client.redeemApUSD({ lstToken: SLISBNB, apUSDAmount });
381
+
382
+ // Step 3: Calculate expected LST output and set minOut
383
+ const expectedLST = (apUSDAmount * parseAmount("1")) / lstPrice;
384
+ const minOut = expectedLST * 995n / 1000n; // 0.5% slippage
385
+
386
+ // Step 4: Redeem with slippage protection
387
+ const hash = await client.redeemApUSD({ lstToken: SLISBNB, apUSDAmount, minOut });
186
388
  await client.waitForTransaction(hash);
187
389
  ```
188
390
 
@@ -202,12 +404,46 @@ if (xBNBPrice === 0n) {
202
404
  throw new Error("xBNB underwater - cannot mint");
203
405
  }
204
406
 
205
- // Step 3: Approve LST, then mint xBNB
206
- const hash = await client.mintXBNB({ lstToken: SLISBNB, lstAmount });
407
+ // Step 3: Approve LST, then mint xBNB with slippage protection
408
+ const minOut = 0n; // Set appropriate minOut based on expected xBNB output
409
+ const hash = await client.mintXBNB({ lstToken: SLISBNB, lstAmount, minOut });
207
410
  await client.waitForTransaction(hash);
208
411
  ```
209
412
 
210
- ### Flow 4: Stake apUSD to Earn Yield (sApUSD)
413
+ ### Flow 4: Redeem xBNB for LST
414
+
415
+ User burns xBNB to get back LST.
416
+
417
+ ```typescript
418
+ const xBNBAmount = parseAmount("100"); // 100 xBNB
419
+
420
+ // Step 1: Check xBNB price (ensure not underwater)
421
+ const xBNBPrice = await client.getXBNBPriceBNB();
422
+ if (xBNBPrice === 0n) {
423
+ throw new Error("xBNB is underwater - redemption may result in 0 LST");
424
+ }
425
+
426
+ // Step 2: Check current fees
427
+ const fees = await client.getCurrentFees();
428
+ console.log("xBNB Redeem fee:", fees.xBNBRedeemFee / 100, "%");
429
+
430
+ // Step 3: Check available LST liquidity
431
+ const collateral = await client.getLSTCollateral(SLISBNB);
432
+ console.log("Available collateral:", formatAmount(collateral));
433
+
434
+ // Step 4: Approve xBNB spending (use viem directly)
435
+ // ...
436
+
437
+ // Step 5: Calculate expected LST and set minOut for slippage protection
438
+ const expectedLST = (xBNBAmount * xBNBPrice) / parseAmount("1");
439
+ const minOut = expectedLST * 995n / 1000n; // 0.5% slippage
440
+
441
+ // Step 6: Redeem xBNB
442
+ const hash = await client.redeemXBNB({ lstToken: SLISBNB, xBNBAmount, minOut });
443
+ await client.waitForTransaction(hash);
444
+ ```
445
+
446
+ ### Flow 5: Stake apUSD to Earn Yield (sApUSD)
211
447
 
212
448
  User deposits apUSD to stability pool to earn LST yield.
213
449
 
@@ -232,7 +468,7 @@ console.log("Shares:", formatAmount(position.shares));
232
468
  console.log("Balance (incl. yield):", formatAmount(position.balance));
233
469
  ```
234
470
 
235
- ### Flow 5: Withdraw from Stability Pool
471
+ ### Flow 6: Withdraw from Stability Pool
236
472
 
237
473
  User withdraws their staked apUSD plus accumulated yield.
238
474
 
@@ -255,7 +491,7 @@ await client.waitForTransaction(hash);
255
491
  // const hash = await client.withdrawAssets({ assets: parseAmount("500") });
256
492
  ```
257
493
 
258
- ### Flow 6: Manual Yield Harvest
494
+ ### Flow 7: Manual Yield Harvest
259
495
 
260
496
  Anyone can trigger yield harvest to distribute pending LST yield.
261
497
 
@@ -517,6 +753,25 @@ displayDashboard();
517
753
  | **Pool** | `mintApUSD()`, `redeemApUSD()`, `mintXBNB()`, `redeemXBNB()` |
518
754
  | **Stability Pool** | `deposit()`, `withdraw()`, `withdrawAssets()`, `harvestYield()` |
519
755
 
756
+ ### Router View Functions
757
+
758
+ | Category | Methods |
759
+ |----------|---------|
760
+ | **Config** | `getDefaultLST()`, `isSupportedInputToken()`, `isSupportedLST()`, `getDiamond()` |
761
+ | **Preview** | `getExpectedOutput()` |
762
+ | **Unstake** | `getUserWithdrawalIndices()`, `getWithdrawalStatus()` |
763
+ | **Addresses** | `getWBNB()`, `getUSDT()`, `getUSDC()`, `getSlisBNB()`, `getAsBNB()`, `getWclisBNB()`, `getApUSD()`, `getXBNB()` |
764
+
765
+ ### Router Write Functions
766
+
767
+ | Category | Methods |
768
+ |----------|---------|
769
+ | **Swap+Mint** | `swapAndMintApUSD()`, `swapAndMintXBNB()`, `swapAndMintApUSDDefault()`, `swapAndMintXBNBDefault()` |
770
+ | **Stake+Mint** | `stakeAndMint()`, `stakeAndMintApUSD()`, `stakeAndMintXBNB()` |
771
+ | **Direct** | `mintApUSD()`, `mintXBNB()`, `redeemApUSD()`, `redeemXBNB()` |
772
+ | **Redeem+Swap** | `redeemApUSDAndSwap()`, `redeemXBNBAndSwap()`, `redeemApUSDAndUnstake()`, `redeemXBNBAndUnstake()` |
773
+ | **Native Unstake** | `redeemApUSDAndRequestUnstake()`, `redeemXBNBAndRequestUnstake()`, `claimUnstake()` |
774
+
520
775
  ---
521
776
 
522
777
  ## Utility Functions
@@ -543,10 +798,17 @@ import {
543
798
  import {
544
799
  createAspanTestnetReadClient,
545
800
  createAspanTestnetClient,
801
+ createRouterTestnetReadClient,
802
+ createRouterTestnetClient,
546
803
  } from "@aspan/sdk";
547
804
 
805
+ // Diamond client (testnet)
548
806
  const client = createAspanTestnetReadClient("0x...");
549
807
  const writeClient = createAspanTestnetClient("0x...", account);
808
+
809
+ // Router client (testnet)
810
+ const routerRead = createRouterTestnetReadClient("0x...");
811
+ const routerWrite = createRouterTestnetClient("0x...", account);
550
812
  ```
551
813
 
552
814
  ## License