@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,212 @@
1
+ /**
2
+ * Network utilities for Alpha Futures SDK
3
+ */
4
+
5
+ import { Chain, PublicClient, createPublicClient, http } from 'viem';
6
+ import { mainnet, sepolia } from 'viem/chains';
7
+ import { NetworkConfig } from '../types';
8
+ import {
9
+ NETWORK_CONFIGS,
10
+ getCurrentNetwork,
11
+ getNetworkByChainId,
12
+ isValidNetwork,
13
+ } from '../constants/networks';
14
+
15
+ export interface NetworkInfo {
16
+ chainId: number;
17
+ name: string;
18
+ isSupported: boolean;
19
+ config?: NetworkConfig;
20
+ }
21
+
22
+ /**
23
+ * Create a custom chain configuration
24
+ */
25
+ export function createCustomChain(config: NetworkConfig): Chain {
26
+ return {
27
+ id: config.chainId,
28
+ name: config.name,
29
+ nativeCurrency: {
30
+ decimals: 18,
31
+ name: 'TAO',
32
+ symbol: 'TAO',
33
+ },
34
+ rpcUrls: {
35
+ default: {
36
+ http: config.rpcUrl ? [config.rpcUrl] : [],
37
+ },
38
+ },
39
+ blockExplorers: config.blockExplorer
40
+ ? {
41
+ default: {
42
+ name: `${config.name} Explorer`,
43
+ url: config.blockExplorer,
44
+ },
45
+ }
46
+ : undefined,
47
+ };
48
+ }
49
+
50
+ /**
51
+ * Get chain configuration by ID
52
+ */
53
+ export function getChainById(chainId: number): Chain {
54
+ switch (chainId) {
55
+ case 1:
56
+ return mainnet;
57
+ case 11155111:
58
+ return sepolia;
59
+ default: {
60
+ // Try to find in our configs
61
+ const config = getNetworkByChainId(chainId);
62
+ if (config) {
63
+ return createCustomChain(config);
64
+ }
65
+ // Return a basic chain config
66
+ return {
67
+ id: chainId,
68
+ name: `Chain ${chainId}`,
69
+ nativeCurrency: {
70
+ decimals: 18,
71
+ name: 'ETH',
72
+ symbol: 'ETH',
73
+ },
74
+ rpcUrls: {
75
+ default: { http: [] },
76
+ },
77
+ };
78
+ }
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Detects the current network from a public client
84
+ */
85
+ export async function detectNetwork(client: PublicClient): Promise<NetworkInfo> {
86
+ try {
87
+ const chainId = await client.getChainId();
88
+ const config = getNetworkByChainId(chainId);
89
+
90
+ return {
91
+ chainId,
92
+ name: config?.name || `Unknown (${chainId})`,
93
+ isSupported: !!config,
94
+ config,
95
+ };
96
+ } catch (error) {
97
+ throw new Error(`Failed to detect network: ${error}`);
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Switches to a different network (for wallet providers like MetaMask)
103
+ */
104
+ export async function switchNetwork(
105
+ provider: Record<string, unknown>, // EIP-1193 provider
106
+ targetNetwork: string,
107
+ ): Promise<void> {
108
+ if (!isValidNetwork(targetNetwork)) {
109
+ throw new Error(`Invalid network: ${targetNetwork}`);
110
+ }
111
+
112
+ const config = NETWORK_CONFIGS[targetNetwork];
113
+ const chainIdHex = `0x${config.chainId.toString(16)}`;
114
+
115
+ try {
116
+ // Try to switch to the network
117
+ await (provider as any).request({
118
+ method: 'wallet_switchEthereumChain',
119
+ params: [{ chainId: chainIdHex }],
120
+ });
121
+ } catch (error: unknown) {
122
+ // Network not added to wallet
123
+ if ((error as { code?: number }).code === 4902) {
124
+ await addNetwork(provider, targetNetwork);
125
+ } else {
126
+ throw error;
127
+ }
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Adds a network to the wallet
133
+ */
134
+ export async function addNetwork(
135
+ provider: Record<string, unknown>, // EIP-1193 provider
136
+ networkName: string,
137
+ ): Promise<void> {
138
+ const config = NETWORK_CONFIGS[networkName];
139
+ if (!config) {
140
+ throw new Error(`Network configuration not found for: ${networkName}`);
141
+ }
142
+
143
+ const params = {
144
+ chainId: `0x${config.chainId.toString(16)}`,
145
+ chainName: config.name,
146
+ nativeCurrency: {
147
+ name: 'TAO',
148
+ symbol: 'TAO',
149
+ decimals: 18,
150
+ },
151
+ rpcUrls: config.rpcUrl ? [config.rpcUrl] : [],
152
+ blockExplorerUrls: config.blockExplorer ? [config.blockExplorer] : undefined,
153
+ };
154
+
155
+ await (provider as any).request({
156
+ method: 'wallet_addEthereumChain',
157
+ params: [params],
158
+ });
159
+ }
160
+
161
+ /**
162
+ * Creates a public client for a specific network
163
+ */
164
+ export function createNetworkClient(networkName: string): PublicClient {
165
+ const config = NETWORK_CONFIGS[networkName];
166
+ if (!config || !config.rpcUrl) {
167
+ throw new Error(`Invalid network or missing RPC URL: ${networkName}`);
168
+ }
169
+
170
+ const chain = getChainById(config.chainId);
171
+
172
+ return createPublicClient({
173
+ chain,
174
+ transport: http(config.rpcUrl),
175
+ });
176
+ }
177
+
178
+ /**
179
+ * Validates chain ID matches expected network
180
+ */
181
+ export async function validateChainId(
182
+ client: PublicClient,
183
+ expectedNetwork: string,
184
+ ): Promise<boolean> {
185
+ const config = NETWORK_CONFIGS[expectedNetwork];
186
+ if (!config) return false;
187
+
188
+ try {
189
+ const chainId = await client.getChainId();
190
+ return chainId === config.chainId;
191
+ } catch {
192
+ return false;
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Get all supported networks
198
+ */
199
+ export function getSupportedNetworks(): string[] {
200
+ return Object.keys(NETWORK_CONFIGS);
201
+ }
202
+
203
+ /**
204
+ * Format network name for display
205
+ */
206
+ export function formatNetworkName(network: string): string {
207
+ const config = NETWORK_CONFIGS[network];
208
+ return config?.name || network;
209
+ }
210
+
211
+ // Re-export from constants for backward compatibility
212
+ export { getCurrentNetwork, isValidNetwork, NETWORK_CONFIGS };
@@ -0,0 +1,317 @@
1
+ import { parseEther, formatEther } from 'viem';
2
+
3
+ /**
4
+ * Position calculation utilities for Alpha Futures protocol
5
+ * Supports both collateral-first and size-first paradigms
6
+ * All calculations use 18 decimal precision (wei format in contracts)
7
+ */
8
+
9
+ // ============================================================================
10
+ // COLLATERAL-FIRST PARADIGM (NEW - Primary UX)
11
+ // ============================================================================
12
+
13
+ /**
14
+ * Calculate position size in tokens from collateral and leverage
15
+ * @param collateralUSD - Collateral amount in USD (as number, e.g., 100 for $100)
16
+ * @param leverage - Leverage multiplier (as number, e.g., 3.5 for 3.5x)
17
+ * @param tokenPriceUSD - Token price in USD (as number, e.g., 0.019)
18
+ * @returns Position size as bigint in wei (18 decimals)
19
+ * @example
20
+ * // $100 collateral, 3.5x leverage, $0.019 token price
21
+ * // = (100 * 3.5) / 0.019 = 18,421 tokens
22
+ * calculatePositionSizeFromCollateral(100, 3.5, 0.019)
23
+ * // Returns: 18421052631578947368421n (18421.05... * 1e18)
24
+ */
25
+ export function calculatePositionSizeFromCollateral(
26
+ collateralUSD: number,
27
+ leverage: number,
28
+ tokenPriceUSD: number,
29
+ ): bigint {
30
+ if (tokenPriceUSD <= 0) {
31
+ throw new Error('Token price must be greater than 0');
32
+ }
33
+ if (leverage <= 0) {
34
+ throw new Error('Leverage must be greater than 0');
35
+ }
36
+ if (collateralUSD < 0) {
37
+ throw new Error('Collateral must be non-negative');
38
+ }
39
+
40
+ const positionSizeInTokens = (collateralUSD * leverage) / tokenPriceUSD;
41
+ return parseEther(positionSizeInTokens.toString());
42
+ }
43
+
44
+ /**
45
+ * Calculate position value in USD from collateral and leverage
46
+ * @param collateralUSD - Collateral amount in USD
47
+ * @param leverage - Leverage multiplier
48
+ * @returns Position value in USD
49
+ * @example
50
+ * // $100 collateral, 3.5x leverage
51
+ * calculatePositionValue(100, 3.5) // Returns: 350
52
+ */
53
+ export function calculatePositionValue(collateralUSD: number, leverage: number): number {
54
+ if (leverage <= 0) {
55
+ throw new Error('Leverage must be greater than 0');
56
+ }
57
+ if (collateralUSD < 0) {
58
+ throw new Error('Collateral must be non-negative');
59
+ }
60
+
61
+ return collateralUSD * leverage;
62
+ }
63
+
64
+ // ============================================================================
65
+ // SIZE-FIRST PARADIGM (OLD - Backward Compatibility)
66
+ // ============================================================================
67
+
68
+ /**
69
+ * Calculate required collateral from position size and leverage
70
+ * @param positionSizeUSD - Position size in USD
71
+ * @param leverage - Leverage multiplier
72
+ * @returns Required collateral in USD
73
+ * @example
74
+ * // $30 position size, 3x leverage
75
+ * // = 30 / 3 = $10 collateral required
76
+ * calculateCollateralFromSize(30, 3) // Returns: 10
77
+ */
78
+ export function calculateCollateralFromSize(positionSizeUSD: number, leverage: number): number {
79
+ if (leverage <= 0) {
80
+ throw new Error('Leverage must be greater than 0');
81
+ }
82
+ if (positionSizeUSD < 0) {
83
+ throw new Error('Position size must be non-negative');
84
+ }
85
+
86
+ return positionSizeUSD / leverage;
87
+ }
88
+
89
+ /**
90
+ * Calculate leverage from position size and collateral
91
+ * @param positionSizeUSD - Position size in USD
92
+ * @param collateralUSD - Collateral amount in USD
93
+ * @returns Calculated leverage
94
+ * @example
95
+ * calculateLeverageFromSizeAndCollateral(30, 10) // Returns: 3
96
+ */
97
+ export function calculateLeverageFromSizeAndCollateral(
98
+ positionSizeUSD: number,
99
+ collateralUSD: number,
100
+ ): number {
101
+ if (collateralUSD <= 0) {
102
+ throw new Error('Collateral must be greater than 0');
103
+ }
104
+ if (positionSizeUSD < 0) {
105
+ throw new Error('Position size must be non-negative');
106
+ }
107
+
108
+ return positionSizeUSD / collateralUSD;
109
+ }
110
+
111
+ // ============================================================================
112
+ // CONVERSION UTILITIES
113
+ // ============================================================================
114
+
115
+ /**
116
+ * Convert USD amount to token amount using price
117
+ * @param amountUSD - Amount in USD
118
+ * @param tokenPriceUSD - Token price in USD
119
+ * @returns Token amount as bigint in wei
120
+ * @example
121
+ * // $100 USD at $0.019 per token
122
+ * usdToTokens(100, 0.019) // Returns: ~5263157894736842105263n (5263.16 * 1e18)
123
+ */
124
+ export function usdToTokens(amountUSD: number, tokenPriceUSD: number): bigint {
125
+ if (tokenPriceUSD <= 0) {
126
+ throw new Error('Token price must be greater than 0');
127
+ }
128
+ if (amountUSD < 0) {
129
+ throw new Error('Amount must be non-negative');
130
+ }
131
+
132
+ const tokens = amountUSD / tokenPriceUSD;
133
+ return parseEther(tokens.toString());
134
+ }
135
+
136
+ /**
137
+ * Convert token amount to USD using price
138
+ * @param tokenAmount - Token amount as bigint in wei
139
+ * @param tokenPriceUSD - Token price in USD
140
+ * @returns Amount in USD
141
+ * @example
142
+ * // 1000 tokens at $0.019 per token
143
+ * tokensToUSD(parseEther('1000'), 0.019) // Returns: 19
144
+ */
145
+ export function tokensToUSD(tokenAmount: bigint, tokenPriceUSD: number): number {
146
+ if (tokenPriceUSD < 0) {
147
+ throw new Error('Token price must be non-negative');
148
+ }
149
+
150
+ const tokens = parseFloat(formatEther(tokenAmount));
151
+ return tokens * tokenPriceUSD;
152
+ }
153
+
154
+ /**
155
+ * Convert leverage number to wei format (1e18 precision)
156
+ * @param leverage - Leverage as number (e.g., 3.5)
157
+ * @returns Leverage as bigint in wei (e.g., 3.5e18)
158
+ * @example
159
+ * leverageToWei(3.5) // Returns: 3500000000000000000n
160
+ */
161
+ export function leverageToWei(leverage: number): bigint {
162
+ if (leverage < 0) {
163
+ throw new Error('Leverage must be non-negative');
164
+ }
165
+
166
+ return parseEther(leverage.toString());
167
+ }
168
+
169
+ /**
170
+ * Convert leverage wei format to number
171
+ * @param leverageWei - Leverage as bigint in wei
172
+ * @returns Leverage as number
173
+ * @example
174
+ * leverageFromWei(3500000000000000000n) // Returns: 3.5
175
+ */
176
+ export function leverageFromWei(leverageWei: bigint): number {
177
+ return parseFloat(formatEther(leverageWei));
178
+ }
179
+
180
+ // ============================================================================
181
+ // PROFIT/LOSS CALCULATIONS
182
+ // ============================================================================
183
+
184
+ /**
185
+ * Calculate P&L percentage from price movement and leverage
186
+ * @param priceChangePercent - Price change in percentage (e.g., 10 for +10%)
187
+ * @param leverage - Leverage multiplier
188
+ * @param isLong - True for long position, false for short
189
+ * @returns P&L percentage
190
+ * @example
191
+ * // Long position, 10x leverage, +10% price move
192
+ * calculatePnLPercent(10, 10, true) // Returns: 100 (100% gain)
193
+ *
194
+ * // Short position, 5x leverage, +10% price move
195
+ * calculatePnLPercent(10, 5, false) // Returns: -50 (50% loss)
196
+ */
197
+ export function calculatePnLPercent(
198
+ priceChangePercent: number,
199
+ leverage: number,
200
+ isLong: boolean,
201
+ ): number {
202
+ if (leverage < 0) {
203
+ throw new Error('Leverage must be non-negative');
204
+ }
205
+
206
+ const direction = isLong ? 1 : -1;
207
+ return priceChangePercent * leverage * direction;
208
+ }
209
+
210
+ /**
211
+ * Calculate absolute P&L amount in USD from price movement
212
+ * @param collateralUSD - Collateral amount in USD
213
+ * @param leverage - Leverage multiplier
214
+ * @param priceChangePercent - Price change in percentage
215
+ * @param isLong - True for long position, false for short
216
+ * @returns P&L amount in USD
217
+ * @example
218
+ * // $100 collateral, 3x leverage, +10% price move, long
219
+ * calculatePnLAmount(100, 3, 10, true) // Returns: 30 (30% of $100)
220
+ */
221
+ export function calculatePnLAmount(
222
+ collateralUSD: number,
223
+ leverage: number,
224
+ priceChangePercent: number,
225
+ isLong: boolean,
226
+ ): number {
227
+ if (leverage <= 0) {
228
+ throw new Error('Leverage must be greater than 0');
229
+ }
230
+ if (collateralUSD < 0) {
231
+ throw new Error('Collateral must be non-negative');
232
+ }
233
+
234
+ const pnlPercent = calculatePnLPercent(priceChangePercent, leverage, isLong);
235
+ return (collateralUSD * pnlPercent) / 100;
236
+ }
237
+
238
+ /**
239
+ * Calculate liquidation price from entry price and leverage
240
+ * @param entryPrice - Entry price of the position
241
+ * @param leverage - Leverage multiplier
242
+ * @param isLong - True for long position, false for short
243
+ * @param maintenanceMarginPercent - Maintenance margin percentage (default 20%)
244
+ * @returns Liquidation price
245
+ * @example
246
+ * // Long position at $100, 3x leverage, 20% maintenance margin
247
+ * // Liquidation at ~26.67% loss from entry (80% / 3)
248
+ * calculateLiquidationPriceFromLeverage(100, 3, true) // Returns: ~73.33
249
+ */
250
+ export function calculateLiquidationPriceFromLeverage(
251
+ entryPrice: number,
252
+ leverage: number,
253
+ isLong: boolean,
254
+ maintenanceMarginPercent: number = 20,
255
+ ): number {
256
+ if (entryPrice <= 0) {
257
+ throw new Error('Entry price must be greater than 0');
258
+ }
259
+ if (leverage <= 0) {
260
+ throw new Error('Leverage must be greater than 0');
261
+ }
262
+ if (maintenanceMarginPercent < 0 || maintenanceMarginPercent >= 100) {
263
+ throw new Error('Maintenance margin must be between 0 and 100');
264
+ }
265
+
266
+ // Initial margin % = 100 / leverage
267
+ // Max loss before liquidation = (initial margin % - maintenance margin %)
268
+ // For long: liquidation = entry * (1 - maxLossPercent / 100)
269
+ // For short: liquidation = entry * (1 + maxLossPercent / 100)
270
+
271
+ const initialMarginPercent = 100 / leverage;
272
+ const maxLossPercent = initialMarginPercent - maintenanceMarginPercent;
273
+
274
+ if (isLong) {
275
+ return entryPrice * (1 - maxLossPercent / 100);
276
+ } else {
277
+ return entryPrice * (1 + maxLossPercent / 100);
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Calculate required margin for a position size and leverage
283
+ * @param positionValueUSD - Position value in USD
284
+ * @param leverage - Leverage multiplier
285
+ * @returns Required margin in USD
286
+ * @example
287
+ * calculateRequiredMargin(350, 3.5) // Returns: 100
288
+ */
289
+ export function calculateRequiredMargin(positionValueUSD: number, leverage: number): number {
290
+ if (leverage <= 0) {
291
+ throw new Error('Leverage must be greater than 0');
292
+ }
293
+ if (positionValueUSD < 0) {
294
+ throw new Error('Position value must be non-negative');
295
+ }
296
+
297
+ return positionValueUSD / leverage;
298
+ }
299
+
300
+ /**
301
+ * Calculate maximum position size from available collateral
302
+ * @param collateralUSD - Available collateral in USD
303
+ * @param leverage - Desired leverage
304
+ * @param tokenPriceUSD - Token price in USD
305
+ * @returns Maximum position size in tokens (as bigint in wei)
306
+ * @example
307
+ * // $100 collateral, 3x leverage, $0.019 token price
308
+ * calculateMaxPositionSize(100, 3, 0.019)
309
+ * // Returns: 15789473684210526315789n (~15789.47 tokens)
310
+ */
311
+ export function calculateMaxPositionSize(
312
+ collateralUSD: number,
313
+ leverage: number,
314
+ tokenPriceUSD: number,
315
+ ): bigint {
316
+ return calculatePositionSizeFromCollateral(collateralUSD, leverage, tokenPriceUSD);
317
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Validation utilities
3
+ */
4
+
5
+ import { isAddress } from 'viem';
6
+ import { ValidationError } from '../errors';
7
+ import { MIN_POSITION_SIZE, MAX_LEVERAGE, PRECISION } from '../constants';
8
+
9
+ /**
10
+ * Validate Ethereum address
11
+ */
12
+ export function validateAddress(address: string, fieldName: string = 'address'): void {
13
+ if (!isAddress(address)) {
14
+ throw new ValidationError(`Invalid Ethereum address: ${address}`, fieldName);
15
+ }
16
+ }
17
+
18
+ /**
19
+ * Validate position size
20
+ */
21
+ export function validatePositionSize(size: bigint): void {
22
+ if (size < MIN_POSITION_SIZE) {
23
+ throw new ValidationError(
24
+ `Position size must be at least ${MIN_POSITION_SIZE / PRECISION} TAO`,
25
+ 'size',
26
+ );
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Validate leverage
32
+ */
33
+ export function validateLeverage(leverage: bigint): void {
34
+ if (leverage < 1n || leverage > MAX_LEVERAGE) {
35
+ throw new ValidationError(`Leverage must be between 1 and ${MAX_LEVERAGE}`, 'leverage');
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Validate positive amount
41
+ */
42
+ export function validatePositiveAmount(amount: bigint, fieldName: string = 'amount'): void {
43
+ if (amount <= 0n) {
44
+ throw new ValidationError(`${fieldName} must be positive`, fieldName);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Validate asset symbol
50
+ */
51
+ export function validateAsset(asset: string): void {
52
+ if (!asset || asset.trim().length === 0) {
53
+ throw new ValidationError('Asset symbol cannot be empty', 'asset');
54
+ }
55
+
56
+ // Basic validation for asset symbol format
57
+ const assetRegex = /^[A-Z0-9]{2,10}$/;
58
+ if (!assetRegex.test(asset)) {
59
+ throw new ValidationError(
60
+ 'Asset symbol must be 2-10 uppercase alphanumeric characters',
61
+ 'asset',
62
+ );
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Validate margin amount
68
+ */
69
+ export function validateMargin(margin: bigint, requiredMargin: bigint): void {
70
+ if (margin < requiredMargin) {
71
+ throw new ValidationError(
72
+ `Insufficient margin. Required: ${requiredMargin}, Provided: ${margin}`,
73
+ 'margin',
74
+ );
75
+ }
76
+ }