@aspan/sdk 0.4.0 → 0.4.2

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/client.ts CHANGED
@@ -16,6 +16,43 @@ import {
16
16
  type Hash,
17
17
  } from "viem";
18
18
  import { bsc, bscTestnet } from "viem/chains";
19
+
20
+ /** Pharos Atlantic Testnet chain definition */
21
+ export const pharosTestnet: Chain = {
22
+ id: 688689,
23
+ name: "Pharos Atlantic Testnet",
24
+ nativeCurrency: { name: "PHRS", symbol: "PHRS", decimals: 18 },
25
+ rpcUrls: {
26
+ default: { http: ["https://atlantic.dplabs-internal.com"] },
27
+ },
28
+ blockExplorers: {
29
+ default: { name: "Pharos Explorer", url: "https://atlantic.pharosscan.xyz" },
30
+ },
31
+ } as const satisfies Chain;
32
+
33
+ /** Supported chain IDs */
34
+ export const CHAIN_IDS = {
35
+ BSC: 56,
36
+ BSC_TESTNET: 97,
37
+ PHAROS: 688689,
38
+ } as const;
39
+
40
+ /** Internal: map chainId to viem Chain object */
41
+ const CHAIN_MAP: Record<number, Chain> = {
42
+ [CHAIN_IDS.BSC]: bsc,
43
+ [CHAIN_IDS.BSC_TESTNET]: bscTestnet,
44
+ [CHAIN_IDS.PHAROS]: pharosTestnet,
45
+ };
46
+
47
+ /**
48
+ * Get the viem Chain object for a given chainId.
49
+ * @throws if the chainId is not supported
50
+ */
51
+ export function getChainById(chainId: number): Chain {
52
+ const chain = CHAIN_MAP[chainId];
53
+ if (!chain) throw new Error(`[Aspan SDK] Unsupported chainId: ${chainId}`);
54
+ return chain;
55
+ }
19
56
  import { DiamondABI } from "./abi/diamond";
20
57
  import type {
21
58
  LSTInfo,
@@ -65,7 +102,7 @@ export interface AspanWriteClientConfig extends AspanClientConfig {
65
102
  */
66
103
  export class AspanReadClient {
67
104
  protected readonly publicClient: PublicClient;
68
- protected readonly diamondAddress: Address;
105
+ public readonly diamondAddress: Address;
69
106
  protected readonly chain: Chain;
70
107
 
71
108
  // Known error signatures for zero supply states
@@ -798,6 +835,32 @@ export class AspanReadClient {
798
835
  });
799
836
  }
800
837
 
838
+ // ============ Bootstrap View Functions ============
839
+
840
+ /**
841
+ * Check if xBNB liquidity has been bootstrapped
842
+ * @returns true if bootstrap has been called
843
+ */
844
+ async isBootstrapped(): Promise<boolean> {
845
+ return this.publicClient.readContract({
846
+ address: this.diamondAddress,
847
+ abi: DiamondABI,
848
+ functionName: "isBootstrapped",
849
+ });
850
+ }
851
+
852
+ /**
853
+ * Get the amount of xBNB locked in bootstrap (burned to dead address)
854
+ * @returns xBNB amount in wei
855
+ */
856
+ async getBootstrapXBNBAmount(): Promise<bigint> {
857
+ return this.publicClient.readContract({
858
+ address: this.diamondAddress,
859
+ abi: DiamondABI,
860
+ functionName: "getBootstrapXBNBAmount",
861
+ });
862
+ }
863
+
801
864
  // ============ Stability Mode View Functions ============
802
865
 
803
866
  async getStabilityMode(): Promise<StabilityModeInfo> {
@@ -1031,6 +1094,89 @@ export class AspanClient extends AspanReadClient {
1031
1094
  });
1032
1095
  }
1033
1096
 
1097
+ // ============ Risk Management Functions ============
1098
+
1099
+ /**
1100
+ * Trigger Stability Mode 2 forced conversion
1101
+ * @description Anyone can call when CR < 130%. Burns apUSD from stability pool
1102
+ * and mints xBNB to compensate stakers.
1103
+ * @param targetCR Target CR to restore to (in BPS, e.g., 14000 = 140%)
1104
+ * @returns Transaction hash
1105
+ */
1106
+ async triggerStabilityMode2(targetCR: bigint = 14000n): Promise<Hash> {
1107
+ return this.walletClient.writeContract({
1108
+ chain: this.chain,
1109
+ account: this.walletClient.account!,
1110
+ address: this.diamondAddress,
1111
+ abi: DiamondABI,
1112
+ functionName: "triggerStabilityMode2",
1113
+ args: [targetCR],
1114
+ });
1115
+ }
1116
+
1117
+ /**
1118
+ * Clean underwater xBNB (burn without getting LST back)
1119
+ * @description Only works when xBNB is underwater (price = 0)
1120
+ * @param xBNBAmount Amount of xBNB to clean (burn)
1121
+ * @returns Transaction hash
1122
+ */
1123
+ async cleanXbnb(xBNBAmount: bigint): Promise<Hash> {
1124
+ return this.walletClient.writeContract({
1125
+ chain: this.chain,
1126
+ account: this.walletClient.account!,
1127
+ address: this.diamondAddress,
1128
+ abi: DiamondABI,
1129
+ functionName: "cleanXbnb",
1130
+ args: [xBNBAmount],
1131
+ });
1132
+ }
1133
+
1134
+ // ============ Fee Management Functions ============
1135
+
1136
+ /**
1137
+ * Set fee tiers (requires feeManager or owner role)
1138
+ * @param tiers Array of fee tier configurations
1139
+ * @returns Transaction hash
1140
+ */
1141
+ async setFeeTiers(tiers: Array<{
1142
+ minCR: bigint;
1143
+ apUSDMintFee: number;
1144
+ apUSDRedeemFee: number;
1145
+ xBNBMintFee: number;
1146
+ xBNBRedeemFee: number;
1147
+ apUSDMintDisabled: boolean;
1148
+ }>): Promise<Hash> {
1149
+ return this.walletClient.writeContract({
1150
+ chain: this.chain,
1151
+ account: this.walletClient.account!,
1152
+ address: this.diamondAddress,
1153
+ abi: DiamondABI,
1154
+ functionName: "setFeeTiers",
1155
+ args: [tiers],
1156
+ });
1157
+ }
1158
+
1159
+ // ============ Admin Functions ============
1160
+
1161
+ /**
1162
+ * Bootstrap xBNB liquidity (owner only)
1163
+ * @description Initializes xBNB supply by minting to dead address, preventing extreme initial prices.
1164
+ * Similar to Uniswap V2's MINIMUM_LIQUIDITY. Can only be called once.
1165
+ * @param lstToken LST token address to deposit
1166
+ * @param lstAmount Amount of LST to deposit for bootstrap
1167
+ * @returns Transaction hash
1168
+ */
1169
+ async bootstrap(lstToken: Address, lstAmount: bigint): Promise<Hash> {
1170
+ return this.walletClient.writeContract({
1171
+ chain: this.chain,
1172
+ account: this.walletClient.account!,
1173
+ address: this.diamondAddress,
1174
+ abi: DiamondABI,
1175
+ functionName: "bootstrap",
1176
+ args: [lstToken, lstAmount],
1177
+ });
1178
+ }
1179
+
1034
1180
  // ============ Transaction Helpers ============
1035
1181
 
1036
1182
  /**
@@ -1104,3 +1250,71 @@ export function createAspanTestnetClient(
1104
1250
  rpcUrl,
1105
1251
  });
1106
1252
  }
1253
+
1254
+ /**
1255
+ * Create a read-only client for Pharos Atlantic Testnet
1256
+ */
1257
+ export function createAspanPharosReadClient(
1258
+ diamondAddress: Address,
1259
+ rpcUrl?: string
1260
+ ): AspanReadClient {
1261
+ return new AspanReadClient({
1262
+ diamondAddress,
1263
+ chain: pharosTestnet,
1264
+ rpcUrl,
1265
+ });
1266
+ }
1267
+
1268
+ /**
1269
+ * Create a full client for Pharos Atlantic Testnet
1270
+ */
1271
+ export function createAspanPharosClient(
1272
+ diamondAddress: Address,
1273
+ account: Account,
1274
+ rpcUrl?: string
1275
+ ): AspanClient {
1276
+ return new AspanClient({
1277
+ diamondAddress,
1278
+ account,
1279
+ chain: pharosTestnet,
1280
+ rpcUrl,
1281
+ });
1282
+ }
1283
+
1284
+ // ============ Chain-ID-based Factory Functions ============
1285
+
1286
+ /**
1287
+ * Create a read-only Aspan client by chainId.
1288
+ * Defaults to BSC mainnet (chainId = 56) if chainId is not provided.
1289
+ *
1290
+ * @param diamondAddress Diamond contract address for the target chain.
1291
+ * @param chainId Target chain ID (56 = BSC, 688689 = Pharos). Defaults to 56.
1292
+ * @param rpcUrl Optional custom RPC endpoint.
1293
+ */
1294
+ export function createAspanReadClientForChain(
1295
+ diamondAddress: Address,
1296
+ chainId: number = CHAIN_IDS.BSC,
1297
+ rpcUrl?: string
1298
+ ): AspanReadClient {
1299
+ const chain = getChainById(chainId);
1300
+ return new AspanReadClient({ diamondAddress, chain, rpcUrl });
1301
+ }
1302
+
1303
+ /**
1304
+ * Create a write Aspan client by chainId using an external wagmi WalletClient.
1305
+ * Defaults to BSC mainnet (chainId = 56) if chainId is not provided.
1306
+ *
1307
+ * @param diamondAddress Diamond contract address for the target chain.
1308
+ * @param chainId Target chain ID (56 = BSC, 688689 = Pharos). Defaults to 56.
1309
+ * @param walletClient External WalletClient from wagmi / viem.
1310
+ * @param rpcUrl Optional custom RPC endpoint.
1311
+ */
1312
+ export function createAspanWriteClientForChain(
1313
+ diamondAddress: Address,
1314
+ chainId: number = CHAIN_IDS.BSC,
1315
+ walletClient: WalletClient<Transport, Chain, Account>,
1316
+ rpcUrl?: string
1317
+ ): AspanClient {
1318
+ const chain = getChainById(chainId);
1319
+ return new AspanClient({ diamondAddress, chain, walletClient, rpcUrl });
1320
+ }
package/src/index.ts CHANGED
@@ -13,6 +13,15 @@ export {
13
13
  createAspanClient,
14
14
  createAspanTestnetReadClient,
15
15
  createAspanTestnetClient,
16
+ createAspanPharosReadClient,
17
+ createAspanPharosClient,
18
+ // Chain-ID-based factory functions
19
+ createAspanReadClientForChain,
20
+ createAspanWriteClientForChain,
21
+ // Chain helpers
22
+ pharosTestnet,
23
+ CHAIN_IDS,
24
+ getChainById,
16
25
  type AspanClientConfig,
17
26
  type AspanWriteClientConfig,
18
27
  } from "./client";
@@ -25,6 +34,11 @@ export {
25
34
  createRouterClient,
26
35
  createRouterTestnetReadClient,
27
36
  createRouterTestnetClient,
37
+ createRouterPharosReadClient,
38
+ createRouterPharosClient,
39
+ // Chain-ID-based factory functions
40
+ createRouterReadClientForChain,
41
+ createRouterWriteClientForChain,
28
42
  type AspanRouterClientConfig,
29
43
  type AspanRouterWriteClientConfig,
30
44
  } from "./router";
@@ -102,21 +116,31 @@ export const PRICE_PRECISION = 10n ** 8n; // Chainlink price precision
102
116
 
103
117
  // ============ Contract Addresses (BSC Mainnet) ============
104
118
  export const BSC_ADDRESSES = {
105
- diamond: "0x10d25Ae0690533e0BA9E64EC7ae77dbD4fE8A46f" as const,
106
- router: "0x813d3D1A3154950E2f1d8718305426a335A974A9" as const,
107
- apUSD: "0x1977097E2E5697A6DD91b6732F368a14F50f6B3d" as const,
108
- xBNB: "0xB78eB4d5928bAb158Eb23c3154544084cD2661d5" as const,
109
- sApUSD: "0xE2BE739C4aA4126ee72D612d9548C38B1B0e5A1b" as const,
119
+ diamond: "0x6a11B30d3a70727d5477D6d8090e144443fA1c78" as const,
120
+ router: "0x29dd49b2e98674ee7531f17e4d40a7725918c3f6" as const,
121
+ apUSD: "0x4570047eeB5aDb4081c5d470494EB5134e34A287" as const,
122
+ xBNB: "0x0A0c9CD826e747D99F90D63e780B3727Da4D0d43" as const,
123
+ sApUSD: "0x73407A291c007a47CC926EcD5CaC256A1E2d00cF" as const,
110
124
  // LSTs
111
125
  slisBNB: "0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B" as const,
112
126
  asBNB: "0x77734e70b6E88b4d82fE632a168EDf6e700912b6" as const,
113
- wclisBNB: "0x439faaC2229559121C4Ad4fd8B3FE13Dff038046" as const,
127
+ wclisBNB: "0x448f7c2fa4e5135a4a5B50879602cf3CD428e108" as const,
114
128
  // Stablecoins
115
129
  USDT: "0x55d398326f99059fF775485246999027B3197955" as const,
116
130
  USDC: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d" as const,
117
131
  WBNB: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c" as const,
118
132
  } as const;
119
133
 
134
+ export const PHAROS_ADDRESSES = {
135
+ diamond: "0x67011Ce4B5E534FA78dD9922559B7005197DBcc8" as const,
136
+ apUSD: "0x82ac96db027772cE8a770e099370b5D7941B3ae4" as const,
137
+ xBNB: "0xFF43b2f50b2c6c588d14Ea4565Ee5Eb93e438576" as const,
138
+ sApUSD: "0xFA4B826CfD5faaAfA48E4963d8a7d6Cf9889A9A8" as const,
139
+ mockLST: "0xc7BB55759e8e04AfAA620De96E9E133B1FAd3e18" as const,
140
+ mockPriceFeed: "0xa2B41DDE7a7BB0A897DE476Ea441fB27A6978682" as const,
141
+ mockExchangeRate: "0xffA1938e072Ea3b144EA7e61662dd8B0818D82B9" as const,
142
+ } as const;
143
+
120
144
  // ============ Utility Functions ============
121
145
 
122
146
  /**
package/src/router.ts CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  type Hash,
19
19
  } from "viem";
20
20
  import { bsc, bscTestnet } from "viem/chains";
21
+ import { pharosTestnet, CHAIN_IDS, getChainById } from "./client";
21
22
  import { RouterABI } from "./abi/router";
22
23
  import type {
23
24
  SwapAndMintParams,
@@ -514,3 +515,71 @@ export function createRouterTestnetClient(
514
515
  rpcUrl,
515
516
  });
516
517
  }
518
+
519
+ /**
520
+ * Create a read-only router client for Pharos Atlantic Testnet
521
+ */
522
+ export function createRouterPharosReadClient(
523
+ routerAddress: Address,
524
+ rpcUrl?: string
525
+ ): AspanRouterReadClient {
526
+ return new AspanRouterReadClient({
527
+ routerAddress,
528
+ chain: pharosTestnet,
529
+ rpcUrl,
530
+ });
531
+ }
532
+
533
+ /**
534
+ * Create a full router client for Pharos Atlantic Testnet
535
+ */
536
+ export function createRouterPharosClient(
537
+ routerAddress: Address,
538
+ account: Account,
539
+ rpcUrl?: string
540
+ ): AspanRouterClient {
541
+ return new AspanRouterClient({
542
+ routerAddress,
543
+ account,
544
+ chain: pharosTestnet,
545
+ rpcUrl,
546
+ });
547
+ }
548
+
549
+ // ============ Chain-ID-based Factory Functions ============
550
+
551
+ /**
552
+ * Create a read-only AspanRouter client by chainId.
553
+ * Defaults to BSC mainnet (chainId = 56) if chainId is not provided.
554
+ *
555
+ * @param routerAddress Router contract address for the target chain.
556
+ * @param chainId Target chain ID (56 = BSC, 688689 = Pharos). Defaults to 56.
557
+ * @param rpcUrl Optional custom RPC endpoint.
558
+ */
559
+ export function createRouterReadClientForChain(
560
+ routerAddress: Address,
561
+ chainId: number = CHAIN_IDS.BSC,
562
+ rpcUrl?: string
563
+ ): AspanRouterReadClient {
564
+ const chain = getChainById(chainId);
565
+ return new AspanRouterReadClient({ routerAddress, chain, rpcUrl });
566
+ }
567
+
568
+ /**
569
+ * Create a write AspanRouter client by chainId using an external wagmi WalletClient.
570
+ * Defaults to BSC mainnet (chainId = 56) if chainId is not provided.
571
+ *
572
+ * @param routerAddress Router contract address for the target chain.
573
+ * @param chainId Target chain ID (56 = BSC, 688689 = Pharos). Defaults to 56.
574
+ * @param walletClient External WalletClient from wagmi / viem.
575
+ * @param rpcUrl Optional custom RPC endpoint.
576
+ */
577
+ export function createRouterWriteClientForChain(
578
+ routerAddress: Address,
579
+ chainId: number = CHAIN_IDS.BSC,
580
+ walletClient: WalletClient<Transport, Chain, Account>,
581
+ rpcUrl?: string
582
+ ): AspanRouterClient {
583
+ const chain = getChainById(chainId);
584
+ return new AspanRouterClient({ routerAddress, chain, walletClient, rpcUrl });
585
+ }