@megatao/sdk 1.1.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.
Files changed (228) hide show
  1. package/.env.example +37 -0
  2. package/CHANGELOG.md +19 -0
  3. package/README.md +199 -0
  4. package/bin/alf +4 -0
  5. package/cli/README.md +198 -0
  6. package/cli/TEST_MANUAL.md +577 -0
  7. package/cli/commands/account.ts +545 -0
  8. package/cli/commands/funding.ts +481 -0
  9. package/cli/commands/liquidation.ts +523 -0
  10. package/cli/commands/market.ts +590 -0
  11. package/cli/commands/orders.ts +395 -0
  12. package/cli/commands/position.ts +1085 -0
  13. package/cli/commands/shared/positionUtils.ts +239 -0
  14. package/cli/commands/trading.ts +483 -0
  15. package/cli/commands/utils.ts +281 -0
  16. package/cli/commands/vault.ts +522 -0
  17. package/cli/index.ts +169 -0
  18. package/cli/interactive.ts +530 -0
  19. package/cli/utils/client.ts +457 -0
  20. package/cli/utils/config.ts +226 -0
  21. package/cli/utils/display.ts +258 -0
  22. package/cli/utils/index.ts +10 -0
  23. package/cli/utils/prompts.ts +364 -0
  24. package/config.example.json +23 -0
  25. package/dist/AlphaFuturesClient.d.ts +36 -0
  26. package/dist/AlphaFuturesClient.d.ts.map +1 -0
  27. package/dist/AlphaFuturesClient.js +116 -0
  28. package/dist/AlphaFuturesClient.js.map +1 -0
  29. package/dist/abi/Alpha.json +5987 -0
  30. package/dist/abi/abis.d.ts +319 -0
  31. package/dist/abi/abis.d.ts.map +1 -0
  32. package/dist/abi/abis.js +128 -0
  33. package/dist/abi/abis.js.map +1 -0
  34. package/dist/abi/index.d.ts +11 -0
  35. package/dist/abi/index.d.ts.map +1 -0
  36. package/dist/abi/index.js +15 -0
  37. package/dist/abi/index.js.map +1 -0
  38. package/dist/config/contracts.config.d.ts +70 -0
  39. package/dist/config/contracts.config.d.ts.map +1 -0
  40. package/dist/config/contracts.config.js +137 -0
  41. package/dist/config/contracts.config.js.map +1 -0
  42. package/dist/config/environments/alpha.config.d.ts +17 -0
  43. package/dist/config/environments/alpha.config.d.ts.map +1 -0
  44. package/dist/config/environments/alpha.config.js +140 -0
  45. package/dist/config/environments/alpha.config.js.map +1 -0
  46. package/dist/config/environments/beta.config.d.ts +16 -0
  47. package/dist/config/environments/beta.config.d.ts.map +1 -0
  48. package/dist/config/environments/beta.config.js +131 -0
  49. package/dist/config/environments/beta.config.js.map +1 -0
  50. package/dist/config/environments/dev.config.d.ts +13 -0
  51. package/dist/config/environments/dev.config.d.ts.map +1 -0
  52. package/dist/config/environments/dev.config.js +123 -0
  53. package/dist/config/environments/dev.config.js.map +1 -0
  54. package/dist/config/environments/index.d.ts +48 -0
  55. package/dist/config/environments/index.d.ts.map +1 -0
  56. package/dist/config/environments/index.js +81 -0
  57. package/dist/config/environments/index.js.map +1 -0
  58. package/dist/config/environments/localhost.config.d.ts +16 -0
  59. package/dist/config/environments/localhost.config.d.ts.map +1 -0
  60. package/dist/config/environments/localhost.config.js +152 -0
  61. package/dist/config/environments/localhost.config.js.map +1 -0
  62. package/dist/config/environments/prod.config.d.ts +20 -0
  63. package/dist/config/environments/prod.config.d.ts.map +1 -0
  64. package/dist/config/environments/prod.config.js +143 -0
  65. package/dist/config/environments/prod.config.js.map +1 -0
  66. package/dist/config/index.d.ts +7 -0
  67. package/dist/config/index.d.ts.map +1 -0
  68. package/dist/config/index.js +41 -0
  69. package/dist/config/index.js.map +1 -0
  70. package/dist/constants/assets.d.ts +76 -0
  71. package/dist/constants/assets.d.ts.map +1 -0
  72. package/dist/constants/assets.js +277 -0
  73. package/dist/constants/assets.js.map +1 -0
  74. package/dist/constants/contracts.d.ts +41 -0
  75. package/dist/constants/contracts.d.ts.map +1 -0
  76. package/dist/constants/contracts.js +57 -0
  77. package/dist/constants/contracts.js.map +1 -0
  78. package/dist/constants/index.d.ts +36 -0
  79. package/dist/constants/index.d.ts.map +1 -0
  80. package/dist/constants/index.js +75 -0
  81. package/dist/constants/index.js.map +1 -0
  82. package/dist/constants/networks.d.ts +32 -0
  83. package/dist/constants/networks.d.ts.map +1 -0
  84. package/dist/constants/networks.js +174 -0
  85. package/dist/constants/networks.js.map +1 -0
  86. package/dist/contracts/index.d.ts +5 -0
  87. package/dist/contracts/index.d.ts.map +1 -0
  88. package/dist/contracts/index.js +21 -0
  89. package/dist/contracts/index.js.map +1 -0
  90. package/dist/contracts/viem/AlphaViem.d.ts +518 -0
  91. package/dist/contracts/viem/AlphaViem.d.ts.map +1 -0
  92. package/dist/contracts/viem/AlphaViem.js +1287 -0
  93. package/dist/contracts/viem/AlphaViem.js.map +1 -0
  94. package/dist/contracts/viem/PriceOracleViem.d.ts +71 -0
  95. package/dist/contracts/viem/PriceOracleViem.d.ts.map +1 -0
  96. package/dist/contracts/viem/PriceOracleViem.js +212 -0
  97. package/dist/contracts/viem/PriceOracleViem.js.map +1 -0
  98. package/dist/contracts/viem/index.d.ts +9 -0
  99. package/dist/contracts/viem/index.d.ts.map +1 -0
  100. package/dist/contracts/viem/index.js +17 -0
  101. package/dist/contracts/viem/index.js.map +1 -0
  102. package/dist/errors/index.d.ts +44 -0
  103. package/dist/errors/index.d.ts.map +1 -0
  104. package/dist/errors/index.js +83 -0
  105. package/dist/errors/index.js.map +1 -0
  106. package/dist/index.d.ts +19 -0
  107. package/dist/index.d.ts.map +1 -0
  108. package/dist/index.js +60 -0
  109. package/dist/index.js.map +1 -0
  110. package/dist/types/alpha.d.ts +299 -0
  111. package/dist/types/alpha.d.ts.map +1 -0
  112. package/dist/types/alpha.js +6 -0
  113. package/dist/types/alpha.js.map +1 -0
  114. package/dist/types/client.d.ts +24 -0
  115. package/dist/types/client.d.ts.map +1 -0
  116. package/dist/types/client.js +13 -0
  117. package/dist/types/client.js.map +1 -0
  118. package/dist/types/contracts.d.ts +48 -0
  119. package/dist/types/contracts.d.ts.map +1 -0
  120. package/dist/types/contracts.js +6 -0
  121. package/dist/types/contracts.js.map +1 -0
  122. package/dist/types/funding.d.ts +27 -0
  123. package/dist/types/funding.d.ts.map +1 -0
  124. package/dist/types/funding.js +6 -0
  125. package/dist/types/funding.js.map +1 -0
  126. package/dist/types/index.d.ts +92 -0
  127. package/dist/types/index.d.ts.map +1 -0
  128. package/dist/types/index.js +47 -0
  129. package/dist/types/index.js.map +1 -0
  130. package/dist/types/liquidation.d.ts +20 -0
  131. package/dist/types/liquidation.d.ts.map +1 -0
  132. package/dist/types/liquidation.js +6 -0
  133. package/dist/types/liquidation.js.map +1 -0
  134. package/dist/types/margin.d.ts +29 -0
  135. package/dist/types/margin.d.ts.map +1 -0
  136. package/dist/types/margin.js +6 -0
  137. package/dist/types/margin.js.map +1 -0
  138. package/dist/types/oracle.d.ts +21 -0
  139. package/dist/types/oracle.d.ts.map +1 -0
  140. package/dist/types/oracle.js +6 -0
  141. package/dist/types/oracle.js.map +1 -0
  142. package/dist/types/positions.d.ts +43 -0
  143. package/dist/types/positions.d.ts.map +1 -0
  144. package/dist/types/positions.js +13 -0
  145. package/dist/types/positions.js.map +1 -0
  146. package/dist/utils/calculations.d.ts +84 -0
  147. package/dist/utils/calculations.d.ts.map +1 -0
  148. package/dist/utils/calculations.js +155 -0
  149. package/dist/utils/calculations.js.map +1 -0
  150. package/dist/utils/errors.d.ts +24 -0
  151. package/dist/utils/errors.d.ts.map +1 -0
  152. package/dist/utils/errors.js +129 -0
  153. package/dist/utils/errors.js.map +1 -0
  154. package/dist/utils/events.d.ts +40 -0
  155. package/dist/utils/events.d.ts.map +1 -0
  156. package/dist/utils/events.js +73 -0
  157. package/dist/utils/events.js.map +1 -0
  158. package/dist/utils/format.d.ts +40 -0
  159. package/dist/utils/format.d.ts.map +1 -0
  160. package/dist/utils/format.js +86 -0
  161. package/dist/utils/format.js.map +1 -0
  162. package/dist/utils/index.d.ts +10 -0
  163. package/dist/utils/index.d.ts.map +1 -0
  164. package/dist/utils/index.js +26 -0
  165. package/dist/utils/index.js.map +1 -0
  166. package/dist/utils/network.d.ts +52 -0
  167. package/dist/utils/network.d.ts.map +1 -0
  168. package/dist/utils/network.js +192 -0
  169. package/dist/utils/network.js.map +1 -0
  170. package/dist/utils/positionCalculations.d.ts +145 -0
  171. package/dist/utils/positionCalculations.d.ts.map +1 -0
  172. package/dist/utils/positionCalculations.js +278 -0
  173. package/dist/utils/positionCalculations.js.map +1 -0
  174. package/dist/utils/validation.d.ts +28 -0
  175. package/dist/utils/validation.d.ts.map +1 -0
  176. package/dist/utils/validation.js +68 -0
  177. package/dist/utils/validation.js.map +1 -0
  178. package/docs/README.md +40 -0
  179. package/docs/api/API.md +831 -0
  180. package/docs/guides/GETTING_STARTED.md +316 -0
  181. package/docs/guides/TRADING_GUIDE.md +677 -0
  182. package/docs/integration/INTEGRATION_GUIDE.md +1679 -0
  183. package/docs/integration/VIEM_INTEGRATION.md +294 -0
  184. package/docs/reference/CLI_QUICK_REFERENCE.md +197 -0
  185. package/docs/reference/TROUBLESHOOTING.md +922 -0
  186. package/package.json +113 -0
  187. package/src/AlphaFuturesClient.ts +158 -0
  188. package/src/abi/.gitkeep +1 -0
  189. package/src/abi/Alpha.json +5987 -0
  190. package/src/abi/README.md +99 -0
  191. package/src/abi/abis.ts +131 -0
  192. package/src/abi/index.ts +13 -0
  193. package/src/config/contracts.config.ts +186 -0
  194. package/src/config/environments/alpha.config.ts +139 -0
  195. package/src/config/environments/beta.config.ts +130 -0
  196. package/src/config/environments/dev.config.ts +122 -0
  197. package/src/config/environments/index.ts +87 -0
  198. package/src/config/environments/localhost.config.ts +153 -0
  199. package/src/config/environments/prod.config.ts +142 -0
  200. package/src/config/index.ts +29 -0
  201. package/src/constants/assets.ts +299 -0
  202. package/src/constants/contracts.ts +64 -0
  203. package/src/constants/index.ts +69 -0
  204. package/src/constants/networks.ts +182 -0
  205. package/src/contracts/index.ts +5 -0
  206. package/src/contracts/viem/AlphaViem.ts +1615 -0
  207. package/src/contracts/viem/PriceOracleViem.ts +272 -0
  208. package/src/contracts/viem/index.ts +11 -0
  209. package/src/errors/index.ts +87 -0
  210. package/src/index.ts +59 -0
  211. package/src/types/VIEM_TYPES_README.md +70 -0
  212. package/src/types/alpha.ts +358 -0
  213. package/src/types/client.ts +27 -0
  214. package/src/types/contracts.ts +74 -0
  215. package/src/types/funding.ts +31 -0
  216. package/src/types/index.ts +108 -0
  217. package/src/types/liquidation.ts +23 -0
  218. package/src/types/margin.ts +34 -0
  219. package/src/types/oracle.ts +24 -0
  220. package/src/types/positions.ts +48 -0
  221. package/src/utils/calculations.ts +175 -0
  222. package/src/utils/errors.ts +147 -0
  223. package/src/utils/events.ts +98 -0
  224. package/src/utils/format.ts +84 -0
  225. package/src/utils/index.ts +10 -0
  226. package/src/utils/network.ts +212 -0
  227. package/src/utils/positionCalculations.ts +317 -0
  228. package/src/utils/validation.ts +76 -0
@@ -0,0 +1,457 @@
1
+ /**
2
+ * Client Utilities
3
+ *
4
+ * Helper functions for initializing and managing the Alpha Futures contracts
5
+ */
6
+
7
+ import { localhost, mainnet, sepolia } from 'viem/chains';
8
+
9
+ import type { Chain } from 'viem';
10
+ import { AlphaFuturesClient, type AlphaClientConfig } from '../../src/AlphaFuturesClient';
11
+ import type { Environment } from '../../src/config/environments';
12
+ import { NETWORK_CONFIGS } from '../../src/constants/networks';
13
+ import { SUBNET_MARKETS, getAssetAddress, isSubnetMarket } from '../../src/constants/assets';
14
+ import chalk from 'chalk';
15
+ import type { PublicClient } from 'viem';
16
+
17
+ export interface ClientOptions {
18
+ network?: string;
19
+ rpc?: string;
20
+ key?: string;
21
+ privateKey?: string;
22
+ }
23
+
24
+ /**
25
+ * Initialize and return Alpha Futures client
26
+ */
27
+ export async function getClient(options: ClientOptions): Promise<AlphaFuturesClient> {
28
+ const privateKey = options.key || options.privateKey || process.env.PRIVATE_KEY;
29
+ const rpcUrl = options.rpc;
30
+
31
+ // Auto-detect network: if custom RPC URL points to localhost, override to use 'localhost' network
32
+ // This ensures contract addresses are read from process.env instead of hardcoded production addresses
33
+ let network = options.network;
34
+ if (
35
+ rpcUrl &&
36
+ (rpcUrl.includes('localhost') ||
37
+ rpcUrl.includes('127.0.0.1') ||
38
+ rpcUrl.includes('::1') ||
39
+ rpcUrl.includes(':8545'))
40
+ ) {
41
+ network = 'localhost';
42
+ } else if (!network) {
43
+ network = 'localhost'; // Default to localhost for safety
44
+ }
45
+
46
+ // Get contract addresses for the network
47
+ const networkConfig = NETWORK_CONFIGS[network];
48
+ if (!networkConfig) {
49
+ throw new Error(`Network ${network} not found in configuration`);
50
+ }
51
+
52
+ // Get the chain based on network - use RPC from config
53
+ let chain: Chain;
54
+ switch (network) {
55
+ case 'mainnet':
56
+ chain = mainnet;
57
+ break;
58
+ case 'bittensor':
59
+ // Use RPC URL from network config
60
+ chain = {
61
+ id: networkConfig.chainId,
62
+ name: networkConfig.name,
63
+ nativeCurrency: {
64
+ decimals: 18,
65
+ name: 'TAO',
66
+ symbol: 'TAO',
67
+ },
68
+ rpcUrls: {
69
+ default: {
70
+ http: [
71
+ rpcUrl || networkConfig.rpcUrl || 'https://bittensor-finney.api.onfinality.io/public',
72
+ ],
73
+ },
74
+ },
75
+ blockExplorers: {
76
+ default: {
77
+ name: 'Bittensor Explorer',
78
+ url: networkConfig.blockExplorer || 'https://evm.taostats.io',
79
+ },
80
+ },
81
+ };
82
+ break;
83
+ case 'sepolia':
84
+ chain = sepolia;
85
+ break;
86
+ case 'localhost':
87
+ default:
88
+ // Use custom localhost/anvil chain configuration
89
+ chain = {
90
+ id: 31337,
91
+ name: 'Localhost',
92
+ nativeCurrency: {
93
+ decimals: 18,
94
+ name: 'Ether',
95
+ symbol: 'ETH',
96
+ },
97
+ rpcUrls: {
98
+ default: { http: [rpcUrl || networkConfig.rpcUrl || 'http://127.0.0.1:8545'] },
99
+ },
100
+ };
101
+ break;
102
+ }
103
+
104
+ const config: AlphaClientConfig = {
105
+ rpcUrl: rpcUrl || chain.rpcUrls.default.http[0],
106
+ privateKey: privateKey as `0x${string}` | undefined,
107
+ };
108
+
109
+ try {
110
+ // Map network string to Environment type
111
+ const environment = network as Environment;
112
+ return new AlphaFuturesClient(environment, config);
113
+ } catch (error: any) {
114
+ throw new Error(`Failed to initialize client: ${error.message}`);
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Validates if a string is a valid Ethereum address
120
+ */
121
+ function isValidAddress(address: string): boolean {
122
+ return /^0x[a-fA-F0-9]{40}$/.test(address);
123
+ }
124
+
125
+ /**
126
+ * Get asset mapping for supported subnet markets
127
+ */
128
+ export function getAssetMapping(): Record<string, `0x${string}`> {
129
+ const mapping: Record<string, `0x${string}`> = {};
130
+
131
+ // Map all subnet markets
132
+ Object.entries(SUBNET_MARKETS).forEach(([symbol, market]) => {
133
+ mapping[symbol] = market.marketAddress;
134
+ });
135
+
136
+ return mapping;
137
+ }
138
+
139
+ /**
140
+ * Get market address for an asset symbol using subnet markets configuration
141
+ * Also supports BTC/ETH from environment variables for localhost testing
142
+ */
143
+ export function getMarketAddress(asset: string): `0x${string}` {
144
+ const normalizedAsset = asset.toUpperCase();
145
+
146
+ // Check if it's a supported subnet market
147
+ if (isSubnetMarket(normalizedAsset)) {
148
+ return SUBNET_MARKETS[normalizedAsset].marketAddress;
149
+ }
150
+
151
+ // Check for test assets from environment variables (localhost/anvil testing)
152
+ const testAssetEnvMap: Record<string, string> = {
153
+ BTC: 'ALPHA_BTC',
154
+ ETH: 'ALPHA_ETH',
155
+ };
156
+
157
+ if (testAssetEnvMap[normalizedAsset]) {
158
+ const envVar = testAssetEnvMap[normalizedAsset];
159
+ const address = process.env[envVar];
160
+ if (address && /^0x[a-fA-F0-9]{40}$/.test(address)) {
161
+ return address as `0x${string}`;
162
+ }
163
+ }
164
+
165
+ // Check legacy assets
166
+ try {
167
+ return getAssetAddress(normalizedAsset);
168
+ } catch (error) {
169
+ // Asset not found
170
+ const availableMarkets = Object.keys(SUBNET_MARKETS).join(', ');
171
+
172
+ throw new Error(
173
+ `Market address not found for asset '${asset}'.\n` +
174
+ `Available subnet markets: ${availableMarkets}\n` +
175
+ `For localhost testing, BTC/ETH are available if ALPHA_BTC/ALPHA_ETH env vars are set.\n` +
176
+ `\n` +
177
+ `Supported assets are subnet markets only. Please use one of:\n` +
178
+ ` BITMIND (BitMind - SN34)\n` +
179
+ ` CHUTES (Chutes - SN64)\n` +
180
+ ` AFFINE (Affine - SN120)\n` +
181
+ ` RIDGES (Ridges - SN62)\n`,
182
+ );
183
+ }
184
+ }
185
+
186
+ /**
187
+ * List all available subnet market addresses
188
+ */
189
+ export function listAvailableMarketAddresses(): Record<string, string> {
190
+ const markets: Record<string, string> = {};
191
+
192
+ // Get all subnet markets
193
+ Object.entries(SUBNET_MARKETS).forEach(([symbol, market]) => {
194
+ markets[symbol] = market.marketAddress;
195
+ });
196
+
197
+ return markets;
198
+ }
199
+
200
+ /**
201
+ * Validate that required subnet market addresses are available
202
+ */
203
+ export function validateMarketAddresses(requiredAssets: string[] = ['BITMIND']): {
204
+ valid: boolean;
205
+ missing: string[];
206
+ available: Record<string, string>;
207
+ } {
208
+ const missing: string[] = [];
209
+ const available = listAvailableMarketAddresses();
210
+
211
+ for (const asset of requiredAssets) {
212
+ if (!available[asset.toUpperCase()]) {
213
+ missing.push(asset);
214
+ }
215
+ }
216
+
217
+ return {
218
+ valid: missing.length === 0,
219
+ missing,
220
+ available,
221
+ };
222
+ }
223
+
224
+ /**
225
+ * Resolve asset symbol to validated market address
226
+ */
227
+ export function resolveAsset(asset?: string): string {
228
+ // Default to BitMind (first subnet market) if no asset specified
229
+ const targetAsset = asset || 'BITMIND';
230
+ const normalizedAsset = targetAsset.toUpperCase();
231
+
232
+ // Check if it's a supported subnet market
233
+ if (isSubnetMarket(normalizedAsset)) {
234
+ return normalizedAsset;
235
+ }
236
+
237
+ // Check for test assets from environment variables (localhost/anvil testing)
238
+ const testAssetEnvMap: Record<string, string> = {
239
+ BTC: 'ALPHA_BTC',
240
+ ETH: 'ALPHA_ETH',
241
+ };
242
+
243
+ if (testAssetEnvMap[normalizedAsset]) {
244
+ const envVar = testAssetEnvMap[normalizedAsset];
245
+ const address = process.env[envVar];
246
+ if (address && /^0x[a-fA-F0-9]{40}$/.test(address)) {
247
+ return normalizedAsset;
248
+ }
249
+ }
250
+
251
+ const availableMarkets = Object.keys(SUBNET_MARKETS).join(', ');
252
+ throw new Error(
253
+ `Unsupported asset '${targetAsset}'. Available subnet markets: ${availableMarkets}`,
254
+ );
255
+ }
256
+
257
+ /**
258
+ * Chunk large block ranges for event queries to prevent RPC timeouts
259
+ */
260
+ export async function getEventsInChunks<T>(
261
+ publicClient: PublicClient,
262
+ getEventsConfig: {
263
+ address: `0x${string}`;
264
+ abi: any;
265
+ eventName: string;
266
+ args?: any;
267
+ },
268
+ fromBlock: bigint | 'earliest' = 'earliest',
269
+ toBlock: bigint | 'latest' = 'latest',
270
+ chunkSize: number = 5000,
271
+ ): Promise<T[]> {
272
+ // Get current block number if needed
273
+ const currentBlock = await publicClient.getBlockNumber();
274
+
275
+ // Convert 'earliest' and 'latest' to actual block numbers
276
+ let startBlock: bigint;
277
+ let endBlock: bigint;
278
+
279
+ if (fromBlock === 'earliest') {
280
+ startBlock = 0n;
281
+ } else {
282
+ startBlock = fromBlock;
283
+ }
284
+
285
+ if (toBlock === 'latest') {
286
+ endBlock = currentBlock;
287
+ } else {
288
+ endBlock = toBlock;
289
+ }
290
+
291
+ // If the range is small enough, query directly
292
+ const totalBlocks = Number(endBlock - startBlock);
293
+ if (totalBlocks <= chunkSize) {
294
+ return (await publicClient.getContractEvents({
295
+ ...getEventsConfig,
296
+ fromBlock: startBlock,
297
+ toBlock: endBlock,
298
+ })) as T[];
299
+ }
300
+
301
+ // Query in chunks
302
+ const allEvents: T[] = [];
303
+ const chunkSizeBigInt = BigInt(chunkSize);
304
+
305
+ for (let currentStart = startBlock; currentStart <= endBlock; currentStart += chunkSizeBigInt) {
306
+ const currentEnd =
307
+ currentStart + chunkSizeBigInt - 1n > endBlock
308
+ ? endBlock
309
+ : currentStart + chunkSizeBigInt - 1n;
310
+
311
+ try {
312
+ const events = (await publicClient.getContractEvents({
313
+ ...getEventsConfig,
314
+ fromBlock: currentStart,
315
+ toBlock: currentEnd,
316
+ })) as T[];
317
+
318
+ allEvents.push(...events);
319
+ } catch (error) {
320
+ console.warn(
321
+ `Warning: Failed to fetch events for blocks ${currentStart}-${currentEnd}:`,
322
+ error,
323
+ );
324
+ // Continue with remaining chunks
325
+ }
326
+ }
327
+
328
+ return allEvents;
329
+ }
330
+
331
+ /**
332
+ * Get recent events with a reasonable limit to prevent timeouts
333
+ */
334
+ export async function getRecentEvents<T>(
335
+ publicClient: PublicClient,
336
+ getEventsConfig: {
337
+ address: `0x${string}`;
338
+ abi: any;
339
+ eventName: string;
340
+ args?: any;
341
+ },
342
+ maxBlocks: number = 10000,
343
+ ): Promise<T[]> {
344
+ const currentBlock = await publicClient.getBlockNumber();
345
+ const fromBlock = currentBlock > BigInt(maxBlocks) ? currentBlock - BigInt(maxBlocks) : 0n;
346
+
347
+ return getEventsInChunks<T>(
348
+ publicClient,
349
+ getEventsConfig,
350
+ fromBlock,
351
+ currentBlock,
352
+ 5000, // 5k block chunks
353
+ );
354
+ }
355
+
356
+ /**
357
+ * Get events with optimized chunking for better performance
358
+ * Uses larger chunks initially and reduces on failures
359
+ */
360
+ export async function getEventsOptimized<T>(
361
+ publicClient: PublicClient,
362
+ getEventsConfig: {
363
+ address: `0x${string}`;
364
+ abi: any;
365
+ eventName: string;
366
+ args?: any;
367
+ },
368
+ fromBlock: bigint | 'earliest' = 'earliest',
369
+ toBlock: bigint | 'latest' = 'latest',
370
+ limit?: number,
371
+ ): Promise<T[]> {
372
+ // For very large ranges, prefer recent events
373
+ if (fromBlock === 'earliest') {
374
+ const currentBlock = await publicClient.getBlockNumber();
375
+ const maxRecentBlocks = 50000; // Look back up to 50k blocks
376
+ fromBlock =
377
+ currentBlock > BigInt(maxRecentBlocks) ? currentBlock - BigInt(maxRecentBlocks) : 0n;
378
+ }
379
+
380
+ const events = await getEventsInChunks<T>(
381
+ publicClient,
382
+ getEventsConfig,
383
+ fromBlock,
384
+ toBlock,
385
+ 10000, // 10k block chunks for better performance
386
+ );
387
+
388
+ // Apply limit if specified
389
+ if (limit && events.length > limit) {
390
+ return events.slice(-limit); // Get most recent
391
+ }
392
+
393
+ return events;
394
+ }
395
+
396
+ /**
397
+ * Handle errors with appropriate formatting
398
+ */
399
+ export function handleError(error: any, options: any) {
400
+ if (options.json) {
401
+ console.error(
402
+ JSON.stringify(
403
+ {
404
+ error: true,
405
+ message: error.message || error.toString(),
406
+ code: error.code,
407
+ stack: options.debug ? error.stack : undefined,
408
+ },
409
+ null,
410
+ 2,
411
+ ),
412
+ );
413
+ } else {
414
+ console.error(chalk.red('\n❌ Error:'), error.message || error);
415
+
416
+ // Show additional error details
417
+ if (error.reason) {
418
+ console.error(chalk.gray('Reason:'), error.reason);
419
+ }
420
+ if (error.code) {
421
+ console.error(chalk.gray('Code:'), error.code);
422
+ }
423
+ if (error.transaction) {
424
+ console.error(chalk.gray('Transaction:'), error.transaction.hash);
425
+ }
426
+
427
+ // Show stack trace in debug mode
428
+ if (options.debug) {
429
+ console.error(chalk.gray('\nStack trace:'));
430
+ console.error(error.stack);
431
+ }
432
+
433
+ // Provide helpful suggestions based on error type
434
+ if (error.message?.includes('insufficient funds')) {
435
+ console.error(
436
+ chalk.yellow('\n💡 Tip: Check your account balance with "alpha-futures account balance"'),
437
+ );
438
+ } else if (error.message?.includes('nonce')) {
439
+ console.error(
440
+ chalk.yellow('\n💡 Tip: This might be a network issue. Try again in a moment.'),
441
+ );
442
+ } else if (error.message?.includes('gas')) {
443
+ console.error(
444
+ chalk.yellow(
445
+ '\n💡 Tip: The transaction may require more gas. Try increasing the gas limit.',
446
+ ),
447
+ );
448
+ } else if (error.message?.includes('revert')) {
449
+ console.error(
450
+ chalk.yellow('\n💡 Tip: The transaction was reverted. Check your inputs and try again.'),
451
+ );
452
+ }
453
+ }
454
+
455
+ // Exit with error code
456
+ process.exit(1);
457
+ }
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Configuration Utilities
3
+ *
4
+ * Helper functions for managing CLI configuration
5
+ */
6
+
7
+ import fs from 'fs/promises';
8
+ import path from 'path';
9
+ import os from 'os';
10
+ import chalk from 'chalk';
11
+ import { getSupportedNetworks, isValidNetwork } from '../../src/constants/networks';
12
+
13
+ export interface CliConfig {
14
+ network?: string;
15
+ rpcUrl?: string;
16
+ defaultAsset?: string;
17
+ defaultLeverage?: number;
18
+ confirmations?: boolean;
19
+ displayCurrency?: 'USD' | 'TAO';
20
+ theme?: 'default' | 'dark' | 'light';
21
+ gasPrice?: string;
22
+ gasLimit?: string;
23
+ slippage?: number;
24
+ [key: string]: any;
25
+ }
26
+
27
+ /**
28
+ * Get the config file path
29
+ */
30
+ function getConfigPath(customPath?: string): string {
31
+ if (customPath) {
32
+ // Expand ~ to home directory
33
+ if (customPath.startsWith('~')) {
34
+ return path.join(os.homedir(), customPath.slice(1));
35
+ }
36
+ return customPath;
37
+ }
38
+
39
+ // Default path: ~/.alpha-futures/config.json
40
+ return path.join(os.homedir(), '.alpha-futures', 'config.json');
41
+ }
42
+
43
+ /**
44
+ * Load configuration from file
45
+ */
46
+ export async function loadConfig(configPath?: string): Promise<CliConfig> {
47
+ const filePath = getConfigPath(configPath);
48
+
49
+ try {
50
+ const data = await fs.readFile(filePath, 'utf-8');
51
+ return JSON.parse(data);
52
+ } catch (error: any) {
53
+ // Return empty config if file doesn't exist
54
+ if (error.code === 'ENOENT') {
55
+ return {};
56
+ }
57
+
58
+ console.warn(chalk.yellow('Warning: Failed to load config:'), error.message);
59
+ return {};
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Save configuration to file
65
+ */
66
+ export async function saveConfig(configPath: string | undefined, config: CliConfig): Promise<void> {
67
+ const filePath = getConfigPath(configPath);
68
+
69
+ try {
70
+ // Ensure directory exists
71
+ const dir = path.dirname(filePath);
72
+ await fs.mkdir(dir, { recursive: true });
73
+
74
+ // Save config
75
+ await fs.writeFile(filePath, JSON.stringify(config, null, 2));
76
+ } catch (error: any) {
77
+ throw new Error(`Failed to save config: ${error.message}`);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Get a config value with fallback
83
+ */
84
+ export function getConfigValue<T>(config: CliConfig, key: string, defaultValue: T): T {
85
+ return config[key] !== undefined ? config[key] : defaultValue;
86
+ }
87
+
88
+ /**
89
+ * Merge configurations with priority
90
+ */
91
+ export function mergeConfigs(...configs: Partial<CliConfig>[]): CliConfig {
92
+ return configs.reduce((merged, config) => {
93
+ return { ...merged, ...config };
94
+ }, {} as CliConfig);
95
+ }
96
+
97
+ /**
98
+ * Validate configuration
99
+ */
100
+ export function validateConfig(config: CliConfig): string[] {
101
+ const errors: string[] = [];
102
+
103
+ // Validate network
104
+ if (config.network) {
105
+ if (!isValidNetwork(config.network)) {
106
+ const validNetworks = getSupportedNetworks();
107
+ errors.push(
108
+ `Invalid network: ${config.network}. Must be one of: ${validNetworks.join(', ')}`,
109
+ );
110
+ }
111
+ }
112
+
113
+ // Validate RPC URL
114
+ if (config.rpcUrl) {
115
+ try {
116
+ new URL(config.rpcUrl);
117
+ } catch {
118
+ errors.push(`Invalid RPC URL: ${config.rpcUrl}`);
119
+ }
120
+ }
121
+
122
+ // Validate leverage
123
+ if (config.defaultLeverage !== undefined) {
124
+ if (config.defaultLeverage < 1 || config.defaultLeverage > 10) {
125
+ errors.push('Default leverage must be between 1 and 10');
126
+ }
127
+ }
128
+
129
+ // Validate slippage
130
+ if (config.slippage !== undefined) {
131
+ if (config.slippage < 0 || config.slippage > 50) {
132
+ errors.push('Slippage must be between 0 and 50 percent');
133
+ }
134
+ }
135
+
136
+ return errors;
137
+ }
138
+
139
+ /**
140
+ * Get default configuration
141
+ */
142
+ export function getDefaultConfig(): CliConfig {
143
+ return {
144
+ network: 'localhost',
145
+ defaultAsset: 'ALPHA',
146
+ defaultLeverage: 3,
147
+ confirmations: true,
148
+ displayCurrency: 'USD',
149
+ theme: 'default',
150
+ slippage: 0.5,
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Export configuration
156
+ */
157
+ export async function exportConfig(config: CliConfig, outputPath: string): Promise<void> {
158
+ try {
159
+ await fs.writeFile(outputPath, JSON.stringify(config, null, 2));
160
+ console.log(chalk.green(`✓ Configuration exported to ${outputPath}`));
161
+ } catch (error: any) {
162
+ throw new Error(`Failed to export config: ${error.message}`);
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Import configuration
168
+ */
169
+ export async function importConfig(inputPath: string): Promise<CliConfig> {
170
+ try {
171
+ const data = await fs.readFile(inputPath, 'utf-8');
172
+ const config = JSON.parse(data);
173
+
174
+ // Validate imported config
175
+ const errors = validateConfig(config);
176
+ if (errors.length > 0) {
177
+ throw new Error(`Invalid configuration:\n${errors.join('\n')}`);
178
+ }
179
+
180
+ return config;
181
+ } catch (error: any) {
182
+ throw new Error(`Failed to import config: ${error.message}`);
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Create a config preset
188
+ */
189
+ export function createPreset(name: string): CliConfig {
190
+ const presets: Record<string, CliConfig> = {
191
+ conservative: {
192
+ defaultLeverage: 1,
193
+ confirmations: true,
194
+ slippage: 0.1,
195
+ },
196
+ moderate: {
197
+ defaultLeverage: 3,
198
+ confirmations: true,
199
+ slippage: 0.5,
200
+ },
201
+ aggressive: {
202
+ defaultLeverage: 5,
203
+ confirmations: false,
204
+ slippage: 1.0,
205
+ },
206
+ testnet: {
207
+ network: 'sepolia',
208
+ confirmations: true,
209
+ defaultLeverage: 3,
210
+ },
211
+ mainnet: {
212
+ network: 'bittensor',
213
+ confirmations: true,
214
+ defaultLeverage: 2,
215
+ slippage: 0.3,
216
+ },
217
+ bittensor: {
218
+ network: 'bittensor',
219
+ confirmations: true,
220
+ defaultLeverage: 3,
221
+ slippage: 0.5,
222
+ },
223
+ };
224
+
225
+ return presets[name] || getDefaultConfig();
226
+ }