@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/README.md +44 -4
- package/dist/index.d.mts +245 -8
- package/dist/index.d.ts +245 -8
- package/dist/index.js +278 -8
- package/dist/index.mjs +265 -7
- package/package.json +1 -1
- package/src/__tests__/fork.test.ts +2 -2
- package/src/abi/diamond.ts +74 -0
- package/src/abi/sApUSD.ts +100 -0
- package/src/bot/config.ts +51 -1
- package/src/bot/index.ts +69 -1
- package/src/bot/services/fee-manager.ts +303 -0
- package/src/bot/services/risk-keeper.ts +517 -0
- package/src/client.ts +215 -1
- package/src/index.ts +30 -6
- package/src/router.ts +69 -0
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
|
-
|
|
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: "
|
|
106
|
-
router: "
|
|
107
|
-
apUSD: "
|
|
108
|
-
xBNB: "
|
|
109
|
-
sApUSD: "
|
|
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: "
|
|
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
|
+
}
|