@aspan/sdk 0.1.8 → 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/src/router.ts ADDED
@@ -0,0 +1,686 @@
1
+ /**
2
+ * Aspan Router Client
3
+ * TypeScript client for interacting with the AspanRouter periphery contract
4
+ */
5
+
6
+ import {
7
+ createPublicClient,
8
+ createWalletClient,
9
+ http,
10
+ zeroAddress,
11
+ type Address,
12
+ type PublicClient,
13
+ type WalletClient,
14
+ type Transport,
15
+ type Chain,
16
+ type Account,
17
+ type Hash,
18
+ } from "viem";
19
+ import { bsc, bscTestnet } from "viem/chains";
20
+ import { RouterABI } from "./abi/router";
21
+ import type {
22
+ SwapAndMintParams,
23
+ StakeAndMintParams,
24
+ SwapAndMintDefaultParams,
25
+ RouterMintApUSDParams,
26
+ RouterMintXBNBParams,
27
+ RouterRedeemApUSDParams,
28
+ RouterRedeemXBNBParams,
29
+ RouterRedeemAndSwapParams,
30
+ RouterRedeemAndUnstakeParams,
31
+ WithdrawalRequestInfo,
32
+ ExpectedOutput,
33
+ } from "./types";
34
+
35
+ // ============ Configuration ============
36
+
37
+ export interface AspanRouterClientConfig {
38
+ /** Router contract address */
39
+ routerAddress: Address;
40
+ /** Chain to connect to */
41
+ chain?: Chain;
42
+ /** RPC URL (optional, uses default if not provided) */
43
+ rpcUrl?: string;
44
+ }
45
+
46
+ export interface AspanRouterWriteClientConfig extends AspanRouterClientConfig {
47
+ /** Account for signing transactions (required if walletClient not provided) */
48
+ account?: Account;
49
+ /** External wallet client (for browser environments with wagmi/rainbowkit) */
50
+ walletClient?: WalletClient<Transport, Chain, Account>;
51
+ }
52
+
53
+ // ============ Read-Only Client ============
54
+
55
+ /**
56
+ * Read-only client for querying AspanRouter state
57
+ */
58
+ export class AspanRouterReadClient {
59
+ protected readonly publicClient: PublicClient;
60
+ protected readonly routerAddress: Address;
61
+ protected readonly chain: Chain;
62
+
63
+ constructor(config: AspanRouterClientConfig) {
64
+ this.routerAddress = config.routerAddress;
65
+ this.chain = config.chain ?? bsc;
66
+
67
+ this.publicClient = createPublicClient({
68
+ chain: this.chain,
69
+ transport: http(config.rpcUrl),
70
+ });
71
+ }
72
+
73
+ // ============ View Functions ============
74
+
75
+ /**
76
+ * Get the default LST address
77
+ */
78
+ async getDefaultLST(): Promise<Address> {
79
+ return this.publicClient.readContract({
80
+ address: this.routerAddress,
81
+ abi: RouterABI,
82
+ functionName: "defaultLST",
83
+ });
84
+ }
85
+
86
+ /**
87
+ * Check if an input token is supported
88
+ */
89
+ async isSupportedInputToken(token: Address): Promise<boolean> {
90
+ return this.publicClient.readContract({
91
+ address: this.routerAddress,
92
+ abi: RouterABI,
93
+ functionName: "supportedInputTokens",
94
+ args: [token],
95
+ });
96
+ }
97
+
98
+ /**
99
+ * Check if an LST is supported
100
+ */
101
+ async isSupportedLST(lst: Address): Promise<boolean> {
102
+ return this.publicClient.readContract({
103
+ address: this.routerAddress,
104
+ abi: RouterABI,
105
+ functionName: "supportedLSTs",
106
+ args: [lst],
107
+ });
108
+ }
109
+
110
+ /**
111
+ * Get the Diamond contract address
112
+ */
113
+ async getDiamond(): Promise<Address> {
114
+ return this.publicClient.readContract({
115
+ address: this.routerAddress,
116
+ abi: RouterABI,
117
+ functionName: "diamond",
118
+ });
119
+ }
120
+
121
+ /**
122
+ * Get expected output from a swap and mint operation
123
+ */
124
+ async getExpectedOutput(
125
+ inputToken: Address,
126
+ inputAmount: bigint,
127
+ targetLST: Address,
128
+ mintXBNB: boolean
129
+ ): Promise<ExpectedOutput> {
130
+ const result = await this.publicClient.readContract({
131
+ address: this.routerAddress,
132
+ abi: RouterABI,
133
+ functionName: "getExpectedOutput",
134
+ args: [inputToken, inputAmount, targetLST, mintXBNB],
135
+ });
136
+
137
+ return {
138
+ expectedLST: result[0],
139
+ expectedMint: result[1],
140
+ };
141
+ }
142
+
143
+ /**
144
+ * Get user's withdrawal request indices
145
+ */
146
+ async getUserWithdrawalIndices(user: Address): Promise<bigint[]> {
147
+ const result = await this.publicClient.readContract({
148
+ address: this.routerAddress,
149
+ abi: RouterABI,
150
+ functionName: "getUserWithdrawalIndices",
151
+ args: [user],
152
+ });
153
+ return [...result];
154
+ }
155
+
156
+ /**
157
+ * Get withdrawal request status
158
+ */
159
+ async getWithdrawalStatus(
160
+ requestIndex: bigint
161
+ ): Promise<WithdrawalRequestInfo> {
162
+ const result = await this.publicClient.readContract({
163
+ address: this.routerAddress,
164
+ abi: RouterABI,
165
+ functionName: "getWithdrawalStatus",
166
+ args: [requestIndex],
167
+ });
168
+
169
+ return {
170
+ isClaimable: result[0],
171
+ bnbAmount: result[1],
172
+ };
173
+ }
174
+
175
+ // ============ Token Address Getters ============
176
+
177
+ async getWBNB(): Promise<Address> {
178
+ return this.publicClient.readContract({
179
+ address: this.routerAddress,
180
+ abi: RouterABI,
181
+ functionName: "wbnb",
182
+ });
183
+ }
184
+
185
+ async getUSDT(): Promise<Address> {
186
+ return this.publicClient.readContract({
187
+ address: this.routerAddress,
188
+ abi: RouterABI,
189
+ functionName: "usdt",
190
+ });
191
+ }
192
+
193
+ async getUSDC(): Promise<Address> {
194
+ return this.publicClient.readContract({
195
+ address: this.routerAddress,
196
+ abi: RouterABI,
197
+ functionName: "usdc",
198
+ });
199
+ }
200
+
201
+ async getSlisBNB(): Promise<Address> {
202
+ return this.publicClient.readContract({
203
+ address: this.routerAddress,
204
+ abi: RouterABI,
205
+ functionName: "slisBNB",
206
+ });
207
+ }
208
+
209
+ async getAsBNB(): Promise<Address> {
210
+ return this.publicClient.readContract({
211
+ address: this.routerAddress,
212
+ abi: RouterABI,
213
+ functionName: "asBNB",
214
+ });
215
+ }
216
+
217
+ async getWclisBNB(): Promise<Address> {
218
+ return this.publicClient.readContract({
219
+ address: this.routerAddress,
220
+ abi: RouterABI,
221
+ functionName: "wclisBNB",
222
+ });
223
+ }
224
+
225
+ async getApUSD(): Promise<Address> {
226
+ return this.publicClient.readContract({
227
+ address: this.routerAddress,
228
+ abi: RouterABI,
229
+ functionName: "apUSD",
230
+ });
231
+ }
232
+
233
+ async getXBNB(): Promise<Address> {
234
+ return this.publicClient.readContract({
235
+ address: this.routerAddress,
236
+ abi: RouterABI,
237
+ functionName: "xBNB",
238
+ });
239
+ }
240
+ }
241
+
242
+ // ============ Write Client ============
243
+
244
+ /**
245
+ * Full client with write capabilities for AspanRouter
246
+ */
247
+ export class AspanRouterClient extends AspanRouterReadClient {
248
+ private readonly walletClient: WalletClient;
249
+
250
+ constructor(config: AspanRouterWriteClientConfig) {
251
+ super(config);
252
+
253
+ if (config.walletClient) {
254
+ this.walletClient = config.walletClient;
255
+ } else if (config.account) {
256
+ this.walletClient = createWalletClient({
257
+ account: config.account,
258
+ chain: this.chain,
259
+ transport: http(config.rpcUrl),
260
+ });
261
+ } else {
262
+ throw new Error("Either walletClient or account must be provided");
263
+ }
264
+ }
265
+
266
+ // ============ Core Functions ============
267
+
268
+ /**
269
+ * Swap input token to LST and mint apUSD
270
+ */
271
+ async swapAndMintApUSD(params: SwapAndMintParams): Promise<Hash> {
272
+ const value =
273
+ params.swapParams.inputToken === zeroAddress
274
+ ? params.swapParams.inputAmount
275
+ : 0n;
276
+
277
+ return this.walletClient.writeContract({
278
+ chain: this.chain,
279
+ account: this.walletClient.account!,
280
+ address: this.routerAddress,
281
+ abi: RouterABI,
282
+ functionName: "swapAndMintApUSD",
283
+ args: [
284
+ {
285
+ inputToken: params.swapParams.inputToken,
286
+ inputAmount: params.swapParams.inputAmount,
287
+ targetLST: params.swapParams.targetLST,
288
+ minLSTOut: params.swapParams.minLSTOut,
289
+ poolFee: params.swapParams.poolFee,
290
+ useV2: params.swapParams.useV2,
291
+ },
292
+ {
293
+ mintXBNB: params.mintParams.mintXBNB,
294
+ minMintOut: params.mintParams.minMintOut,
295
+ recipient: params.mintParams.recipient,
296
+ deadline: params.mintParams.deadline,
297
+ },
298
+ ],
299
+ value,
300
+ });
301
+ }
302
+
303
+ /**
304
+ * Swap input token to LST and mint xBNB
305
+ */
306
+ async swapAndMintXBNB(params: SwapAndMintParams): Promise<Hash> {
307
+ const value =
308
+ params.swapParams.inputToken === zeroAddress
309
+ ? params.swapParams.inputAmount
310
+ : 0n;
311
+
312
+ return this.walletClient.writeContract({
313
+ chain: this.chain,
314
+ account: this.walletClient.account!,
315
+ address: this.routerAddress,
316
+ abi: RouterABI,
317
+ functionName: "swapAndMintXBNB",
318
+ args: [
319
+ {
320
+ inputToken: params.swapParams.inputToken,
321
+ inputAmount: params.swapParams.inputAmount,
322
+ targetLST: params.swapParams.targetLST,
323
+ minLSTOut: params.swapParams.minLSTOut,
324
+ poolFee: params.swapParams.poolFee,
325
+ useV2: params.swapParams.useV2,
326
+ },
327
+ {
328
+ mintXBNB: params.mintParams.mintXBNB,
329
+ minMintOut: params.mintParams.minMintOut,
330
+ recipient: params.mintParams.recipient,
331
+ deadline: params.mintParams.deadline,
332
+ },
333
+ ],
334
+ value,
335
+ });
336
+ }
337
+
338
+ /**
339
+ * Stake native BNB directly to LST and mint
340
+ */
341
+ async stakeAndMint(params: StakeAndMintParams): Promise<Hash> {
342
+ return this.walletClient.writeContract({
343
+ chain: this.chain,
344
+ account: this.walletClient.account!,
345
+ address: this.routerAddress,
346
+ abi: RouterABI,
347
+ functionName: "stakeAndMint",
348
+ args: [
349
+ params.targetLST,
350
+ params.mintXBNB,
351
+ params.minMintOut,
352
+ params.deadline,
353
+ ],
354
+ value: params.value,
355
+ });
356
+ }
357
+
358
+ // ============ Simplified Functions ============
359
+
360
+ /**
361
+ * Swap input token and mint apUSD using default LST
362
+ */
363
+ async swapAndMintApUSDDefault(params: SwapAndMintDefaultParams): Promise<Hash> {
364
+ const value =
365
+ params.inputToken === zeroAddress ? params.value ?? params.inputAmount : 0n;
366
+
367
+ return this.walletClient.writeContract({
368
+ chain: this.chain,
369
+ account: this.walletClient.account!,
370
+ address: this.routerAddress,
371
+ abi: RouterABI,
372
+ functionName: "swapAndMintApUSDDefault",
373
+ args: [
374
+ params.inputToken,
375
+ params.inputAmount,
376
+ params.minMintOut,
377
+ params.deadline,
378
+ ],
379
+ value,
380
+ });
381
+ }
382
+
383
+ /**
384
+ * Swap input token and mint xBNB using default LST
385
+ */
386
+ async swapAndMintXBNBDefault(params: SwapAndMintDefaultParams): Promise<Hash> {
387
+ const value =
388
+ params.inputToken === zeroAddress ? params.value ?? params.inputAmount : 0n;
389
+
390
+ return this.walletClient.writeContract({
391
+ chain: this.chain,
392
+ account: this.walletClient.account!,
393
+ address: this.routerAddress,
394
+ abi: RouterABI,
395
+ functionName: "swapAndMintXBNBDefault",
396
+ args: [
397
+ params.inputToken,
398
+ params.inputAmount,
399
+ params.minMintOut,
400
+ params.deadline,
401
+ ],
402
+ value,
403
+ });
404
+ }
405
+
406
+ /**
407
+ * Stake native BNB and mint apUSD using default LST
408
+ */
409
+ async stakeAndMintApUSD(minMintOut: bigint, value: bigint): Promise<Hash> {
410
+ return this.walletClient.writeContract({
411
+ chain: this.chain,
412
+ account: this.walletClient.account!,
413
+ address: this.routerAddress,
414
+ abi: RouterABI,
415
+ functionName: "stakeAndMintApUSD",
416
+ args: [minMintOut],
417
+ value,
418
+ });
419
+ }
420
+
421
+ /**
422
+ * Stake native BNB and mint xBNB using default LST
423
+ */
424
+ async stakeAndMintXBNB(minMintOut: bigint, value: bigint): Promise<Hash> {
425
+ return this.walletClient.writeContract({
426
+ chain: this.chain,
427
+ account: this.walletClient.account!,
428
+ address: this.routerAddress,
429
+ abi: RouterABI,
430
+ functionName: "stakeAndMintXBNB",
431
+ args: [minMintOut],
432
+ value,
433
+ });
434
+ }
435
+
436
+ // ============ Direct Mint/Redeem Functions ============
437
+
438
+ /**
439
+ * Mint apUSD by providing LST directly (no swap)
440
+ */
441
+ async mintApUSD(params: RouterMintApUSDParams): Promise<Hash> {
442
+ return this.walletClient.writeContract({
443
+ chain: this.chain,
444
+ account: this.walletClient.account!,
445
+ address: this.routerAddress,
446
+ abi: RouterABI,
447
+ functionName: "mintApUSD",
448
+ args: [params.lst, params.lstAmount, params.minOut ?? 0n],
449
+ });
450
+ }
451
+
452
+ /**
453
+ * Mint xBNB by providing LST directly (no swap)
454
+ */
455
+ async mintXBNB(params: RouterMintXBNBParams): Promise<Hash> {
456
+ return this.walletClient.writeContract({
457
+ chain: this.chain,
458
+ account: this.walletClient.account!,
459
+ address: this.routerAddress,
460
+ abi: RouterABI,
461
+ functionName: "mintXBNB",
462
+ args: [params.lst, params.lstAmount, params.minOut ?? 0n],
463
+ });
464
+ }
465
+
466
+ /**
467
+ * Redeem apUSD for LST (no swap)
468
+ */
469
+ async redeemApUSD(params: RouterRedeemApUSDParams): Promise<Hash> {
470
+ return this.walletClient.writeContract({
471
+ chain: this.chain,
472
+ account: this.walletClient.account!,
473
+ address: this.routerAddress,
474
+ abi: RouterABI,
475
+ functionName: "redeemApUSD",
476
+ args: [params.lst, params.apUSDAmount, params.minOut ?? 0n],
477
+ });
478
+ }
479
+
480
+ /**
481
+ * Redeem xBNB for LST (no swap)
482
+ */
483
+ async redeemXBNB(params: RouterRedeemXBNBParams): Promise<Hash> {
484
+ return this.walletClient.writeContract({
485
+ chain: this.chain,
486
+ account: this.walletClient.account!,
487
+ address: this.routerAddress,
488
+ abi: RouterABI,
489
+ functionName: "redeemXBNB",
490
+ args: [params.lst, params.xBNBAmount, params.minOut ?? 0n],
491
+ });
492
+ }
493
+
494
+ // ============ Redeem and Swap Functions ============
495
+
496
+ /**
497
+ * Redeem apUSD and swap LST to output token
498
+ */
499
+ async redeemApUSDAndSwap(params: RouterRedeemAndSwapParams): Promise<Hash> {
500
+ return this.walletClient.writeContract({
501
+ chain: this.chain,
502
+ account: this.walletClient.account!,
503
+ address: this.routerAddress,
504
+ abi: RouterABI,
505
+ functionName: "redeemApUSDAndSwap",
506
+ args: [
507
+ params.lst,
508
+ params.amount,
509
+ params.outputToken,
510
+ params.minOut,
511
+ params.deadline,
512
+ params.useV2,
513
+ params.poolFee,
514
+ ],
515
+ });
516
+ }
517
+
518
+ /**
519
+ * Redeem xBNB and swap LST to output token
520
+ */
521
+ async redeemXBNBAndSwap(params: RouterRedeemAndSwapParams): Promise<Hash> {
522
+ return this.walletClient.writeContract({
523
+ chain: this.chain,
524
+ account: this.walletClient.account!,
525
+ address: this.routerAddress,
526
+ abi: RouterABI,
527
+ functionName: "redeemXBNBAndSwap",
528
+ args: [
529
+ params.lst,
530
+ params.amount,
531
+ params.outputToken,
532
+ params.minOut,
533
+ params.deadline,
534
+ params.useV2,
535
+ params.poolFee,
536
+ ],
537
+ });
538
+ }
539
+
540
+ /**
541
+ * Redeem apUSD and instantly unstake LST to native BNB via DEX
542
+ */
543
+ async redeemApUSDAndUnstake(
544
+ params: RouterRedeemAndUnstakeParams
545
+ ): Promise<Hash> {
546
+ return this.walletClient.writeContract({
547
+ chain: this.chain,
548
+ account: this.walletClient.account!,
549
+ address: this.routerAddress,
550
+ abi: RouterABI,
551
+ functionName: "redeemApUSDAndUnstake",
552
+ args: [params.lst, params.amount, params.minBNBOut, params.deadline],
553
+ });
554
+ }
555
+
556
+ /**
557
+ * Redeem xBNB and instantly unstake LST to native BNB via DEX
558
+ */
559
+ async redeemXBNBAndUnstake(
560
+ params: RouterRedeemAndUnstakeParams
561
+ ): Promise<Hash> {
562
+ return this.walletClient.writeContract({
563
+ chain: this.chain,
564
+ account: this.walletClient.account!,
565
+ address: this.routerAddress,
566
+ abi: RouterABI,
567
+ functionName: "redeemXBNBAndUnstake",
568
+ args: [params.lst, params.amount, params.minBNBOut, params.deadline],
569
+ });
570
+ }
571
+
572
+ // ============ Native Unstake Functions ============
573
+
574
+ /**
575
+ * Redeem apUSD and request native unstake from Lista (starts unbonding period)
576
+ */
577
+ async redeemApUSDAndRequestUnstake(apUSDAmount: bigint): Promise<Hash> {
578
+ return this.walletClient.writeContract({
579
+ chain: this.chain,
580
+ account: this.walletClient.account!,
581
+ address: this.routerAddress,
582
+ abi: RouterABI,
583
+ functionName: "redeemApUSDAndRequestUnstake",
584
+ args: [apUSDAmount],
585
+ });
586
+ }
587
+
588
+ /**
589
+ * Redeem xBNB and request native unstake from Lista (starts unbonding period)
590
+ */
591
+ async redeemXBNBAndRequestUnstake(xBNBAmount: bigint): Promise<Hash> {
592
+ return this.walletClient.writeContract({
593
+ chain: this.chain,
594
+ account: this.walletClient.account!,
595
+ address: this.routerAddress,
596
+ abi: RouterABI,
597
+ functionName: "redeemXBNBAndRequestUnstake",
598
+ args: [xBNBAmount],
599
+ });
600
+ }
601
+
602
+ /**
603
+ * Claim BNB after unbonding period completes
604
+ */
605
+ async claimUnstake(requestIndex: bigint): Promise<Hash> {
606
+ return this.walletClient.writeContract({
607
+ chain: this.chain,
608
+ account: this.walletClient.account!,
609
+ address: this.routerAddress,
610
+ abi: RouterABI,
611
+ functionName: "claimUnstake",
612
+ args: [requestIndex],
613
+ });
614
+ }
615
+
616
+ // ============ Transaction Helpers ============
617
+
618
+ /**
619
+ * Wait for transaction confirmation
620
+ */
621
+ async waitForTransaction(hash: Hash) {
622
+ return this.publicClient.waitForTransactionReceipt({ hash });
623
+ }
624
+ }
625
+
626
+ // ============ Factory Functions ============
627
+
628
+ /**
629
+ * Create a read-only router client for BSC mainnet
630
+ */
631
+ export function createRouterReadClient(
632
+ routerAddress: Address,
633
+ rpcUrl?: string
634
+ ): AspanRouterReadClient {
635
+ return new AspanRouterReadClient({
636
+ routerAddress,
637
+ chain: bsc,
638
+ rpcUrl,
639
+ });
640
+ }
641
+
642
+ /**
643
+ * Create a full router client for BSC mainnet
644
+ */
645
+ export function createRouterClient(
646
+ routerAddress: Address,
647
+ account: Account,
648
+ rpcUrl?: string
649
+ ): AspanRouterClient {
650
+ return new AspanRouterClient({
651
+ routerAddress,
652
+ account,
653
+ chain: bsc,
654
+ rpcUrl,
655
+ });
656
+ }
657
+
658
+ /**
659
+ * Create a read-only router client for BSC testnet
660
+ */
661
+ export function createRouterTestnetReadClient(
662
+ routerAddress: Address,
663
+ rpcUrl?: string
664
+ ): AspanRouterReadClient {
665
+ return new AspanRouterReadClient({
666
+ routerAddress,
667
+ chain: bscTestnet,
668
+ rpcUrl,
669
+ });
670
+ }
671
+
672
+ /**
673
+ * Create a full router client for BSC testnet
674
+ */
675
+ export function createRouterTestnetClient(
676
+ routerAddress: Address,
677
+ account: Account,
678
+ rpcUrl?: string
679
+ ): AspanRouterClient {
680
+ return new AspanRouterClient({
681
+ routerAddress,
682
+ account,
683
+ chain: bscTestnet,
684
+ rpcUrl,
685
+ });
686
+ }