@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,481 @@
1
+ /**
2
+ * Funding Rate Commands - Manage and view funding rates
3
+ */
4
+
5
+ import { Command } from 'commander';
6
+ import { parseEther, Address } from 'viem';
7
+ import chalk from 'chalk';
8
+ import ora from 'ora';
9
+ import Table from 'cli-table3';
10
+ import { formatPercentage, formatTAO } from '../../src/utils';
11
+ import { getClient, handleError, getMarketAddress } from '../utils/client';
12
+ import { confirmAction } from '../utils/prompts';
13
+
14
+ /**
15
+ * Validates position ID format and provides helpful error message
16
+ * @param positionId Position ID to validate
17
+ * @throws Error with helpful message if format is incorrect
18
+ */
19
+ function validatePositionIdFormat(positionId: string): void {
20
+ if (!positionId) {
21
+ throw new Error(`Position ID cannot be empty.
22
+
23
+ Expected format: 32 hex characters after 0x (bytes16)
24
+ Example: 0x00000000000000000000000000000001`);
25
+ }
26
+
27
+ const hex = positionId.startsWith('0x') ? positionId.slice(2) : positionId;
28
+
29
+ // Check if contains only hex characters
30
+ if (!/^[0-9a-fA-F]*$/.test(hex)) {
31
+ throw new Error(`Invalid position ID format: "${positionId}"
32
+
33
+ Position ID must contain only hexadecimal characters (0-9, a-f, A-F).
34
+ Expected format: 32 hex characters after 0x (bytes16)
35
+ Example: 0x00000000000000000000000000000001`);
36
+ }
37
+
38
+ // Warn if too long (would be truncated)
39
+ if (hex.length > 32) {
40
+ console.warn(
41
+ chalk.yellow(`āš ļø Position ID is longer than 32 hex characters and will be truncated.
42
+ Given: ${positionId}
43
+ Will use: 0x${hex.slice(0, 32)}`),
44
+ );
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Convert position ID to proper bytes16 format (32 hex characters)
50
+ * @param positionId Raw position ID (could be short like 0x123 or full bytes16)
51
+ * @returns Properly formatted bytes16 position ID
52
+ */
53
+ function normalizePositionId(positionId: string): `0x${string}` {
54
+ // Validate format first
55
+ validatePositionIdFormat(positionId);
56
+
57
+ // Remove 0x prefix if present
58
+ const hex = positionId.startsWith('0x') ? positionId.slice(2) : positionId;
59
+
60
+ // Pad to 32 characters (16 bytes) or truncate if too long
61
+ const normalizedHex = hex.length > 32 ? hex.slice(0, 32) : hex.padEnd(32, '0');
62
+
63
+ return `0x${normalizedHex}` as `0x${string}`;
64
+ }
65
+
66
+ export function fundingCommands(program: Command) {
67
+ const funding = program
68
+ .command('funding')
69
+ .alias('fund')
70
+ .description('Manage and view funding rates');
71
+
72
+ // Get current funding rate
73
+ funding
74
+ .command('rate [asset]')
75
+ .description('Get current funding rate for an asset')
76
+ .option('-a, --all', 'Show funding rates for all available assets')
77
+ .action(async (asset, options) => {
78
+ const spinner = ora('Fetching funding rates...').start();
79
+
80
+ try {
81
+ const client = await getClient(program.opts());
82
+ const alpha = client.getAlpha();
83
+
84
+ if (options.all) {
85
+ // Get funding rates for all available assets
86
+ const { listAvailableMarketAddresses } = await import('../utils/client');
87
+ const availableMarkets = listAvailableMarketAddresses();
88
+
89
+ if (Object.keys(availableMarkets).length === 0) {
90
+ spinner.fail('No market addresses configured');
91
+ console.log(chalk.yellow('Available subnet markets: BITMIND, CHUTES, AFFINE, RIDGES'));
92
+ return;
93
+ }
94
+
95
+ const fundingData: Array<[string, string, string, string]> = [];
96
+
97
+ for (const [assetSymbol, marketAddress] of Object.entries(availableMarkets)) {
98
+ try {
99
+ const marketInfo = await alpha.getMarketInfo(marketAddress as Address);
100
+ const openInterest = await alpha.getMarketOpenInterest(marketAddress as Address);
101
+
102
+ const fundingRate = marketInfo.fundingRate;
103
+ const longOI = openInterest.longOI;
104
+ const shortOI = openInterest.shortOI;
105
+ const totalOI = longOI + shortOI;
106
+ const skew = totalOI > 0n ? ((longOI - shortOI) * 10000n) / totalOI : 0n;
107
+
108
+ fundingData.push([
109
+ assetSymbol,
110
+ formatPercentage(fundingRate) + ' per 8h',
111
+ formatTAO(totalOI) + ' TAO',
112
+ (Number(skew) / 100).toFixed(2) + '%',
113
+ ]);
114
+ } catch (error) {
115
+ fundingData.push([
116
+ assetSymbol,
117
+ chalk.red('Error'),
118
+ chalk.red('Error'),
119
+ chalk.red('Error'),
120
+ ]);
121
+ }
122
+ }
123
+
124
+ spinner.stop();
125
+
126
+ console.log(chalk.bold('\nšŸ“Š Funding Rates Overview:'));
127
+ const table = new Table({
128
+ head: ['Asset', 'Funding Rate', 'Total OI', 'Long Bias'],
129
+ colWidths: [10, 20, 15, 12],
130
+ });
131
+
132
+ fundingData.forEach((row) => table.push(row));
133
+ console.log(table.toString());
134
+ } else {
135
+ // Get funding rate for specific asset
136
+ const targetAsset = asset || 'BITMIND';
137
+ const marketAddress = getMarketAddress(targetAsset);
138
+
139
+ const marketInfo = await alpha.getMarketInfo(marketAddress);
140
+ const openInterest = await alpha.getMarketOpenInterest(marketAddress);
141
+ const fundingState = await alpha.getFundingState(marketAddress);
142
+
143
+ spinner.stop();
144
+
145
+ console.log(chalk.bold(`\nšŸ“ˆ Funding Rate: ${chalk.cyan(targetAsset.toUpperCase())}`));
146
+ console.log(` Current Rate: ${formatPercentage(marketInfo.fundingRate)} per 8h`);
147
+ console.log(
148
+ ` Last Update: ${new Date(Number(marketInfo.lastFundingUpdate) * 1000).toLocaleString()}`,
149
+ );
150
+
151
+ // Open Interest breakdown
152
+ console.log(chalk.bold('\nšŸ“Š Open Interest:'));
153
+ console.log(` Long OI: ${formatTAO(openInterest.longOI)} TAO`);
154
+ console.log(` Short OI: ${formatTAO(openInterest.shortOI)} TAO`);
155
+ console.log(` Total OI: ${formatTAO(openInterest.longOI + openInterest.shortOI)} TAO`);
156
+
157
+ // Calculate and show bias
158
+ const totalOI = openInterest.longOI + openInterest.shortOI;
159
+ if (totalOI > 0n) {
160
+ const longBias = (Number(openInterest.longOI) * 100) / Number(totalOI);
161
+ const shortBias = 100 - longBias;
162
+ console.log(` Long Bias: ${chalk.green(longBias.toFixed(1) + '%')}`);
163
+ console.log(` Short Bias: ${chalk.red(shortBias.toFixed(1) + '%')}`);
164
+ }
165
+
166
+ // Rate direction indicator
167
+ const rateNumber = Number(marketInfo.fundingRate);
168
+ let direction = 'Neutral';
169
+ let directionColor = chalk.gray;
170
+
171
+ if (rateNumber > 0) {
172
+ direction = 'Longs pay Shorts';
173
+ directionColor = chalk.red;
174
+ } else if (rateNumber < 0) {
175
+ direction = 'Shorts pay Longs';
176
+ directionColor = chalk.green;
177
+ }
178
+
179
+ console.log(` Direction: ${directionColor(direction)}`);
180
+ }
181
+ } catch (error) {
182
+ spinner.fail('Failed to fetch funding rates');
183
+ handleError(error, program.opts());
184
+ }
185
+ });
186
+
187
+ // Calculate funding payment for a position
188
+ funding
189
+ .command('payment <positionId>')
190
+ .description('Calculate funding payment for a position')
191
+ .addHelpText(
192
+ 'after',
193
+ `
194
+ Examples:
195
+ $ alpha-futures funding payment 0x00000000000000000000000000000001
196
+ $ alpha-futures funding payment 0x12345000000000000000000000000000
197
+
198
+ Note: Position ID must be in bytes16 format (32 hex characters after 0x).
199
+ Use "alpha-futures position find" to get your position IDs.`,
200
+ )
201
+ .action(async (positionId) => {
202
+ const spinner = ora('Calculating funding payment...').start();
203
+
204
+ try {
205
+ const client = await getClient(program.opts());
206
+ const alpha = client.getAlpha();
207
+
208
+ // Get position details
209
+ const normalizedPositionId = normalizePositionId(positionId);
210
+ const position = await alpha.getPosition(normalizedPositionId);
211
+
212
+ if (position.notionalValue === 0n) {
213
+ spinner.fail('Position not found or inactive');
214
+ return;
215
+ }
216
+
217
+ // For this calculation, we'll use BITMIND as default market
218
+ // In a real implementation, you'd need to track which market the position belongs to
219
+ const marketAddress = getMarketAddress('BITMIND');
220
+ const marketInfo = await alpha.getMarketInfo(marketAddress);
221
+
222
+ spinner.stop();
223
+
224
+ // Calculate funding payment
225
+ const fundingRate = marketInfo.fundingRate;
226
+ const positionValue = position.notionalValue;
227
+ // Both positionValue and fundingRate are in wei (18 decimals), so division by BASIS_POINTS is correct
228
+ const fundingPayment = (positionValue * fundingRate) / 10000n;
229
+
230
+ console.log(chalk.bold(`\nšŸ’° Funding Payment Calculation:`));
231
+ console.log(` Position ID: ${chalk.cyan(positionId)}`);
232
+ console.log(` Position Size: ${formatTAO(position.notionalValue)} TAO`);
233
+ console.log(` Direction: ${position.isLong ? chalk.green('LONG') : chalk.red('SHORT')}`);
234
+ console.log(` Current Rate: ${formatPercentage(fundingRate)} per 8h`);
235
+
236
+ const isPayment =
237
+ (position.isLong && fundingRate > 0n) || (!position.isLong && fundingRate < 0n);
238
+ const paymentAmount = fundingPayment < 0n ? -fundingPayment : fundingPayment;
239
+
240
+ if (isPayment) {
241
+ console.log(
242
+ ` Payment: ${chalk.red('-' + formatTAO(paymentAmount) + ' TAO')} ${chalk.gray('(you pay)')}`,
243
+ );
244
+ } else {
245
+ console.log(
246
+ ` Receive: ${chalk.green('+' + formatTAO(paymentAmount) + ' TAO')} ${chalk.gray('(you receive)')}`,
247
+ );
248
+ }
249
+
250
+ // Show next funding time
251
+ const fundingInterval = 8 * 60 * 60; // 8 hours in seconds
252
+ const lastUpdate = Number(marketInfo.lastFundingUpdate);
253
+ const nextFunding = lastUpdate + fundingInterval;
254
+ const timeUntilNext = nextFunding - Math.floor(Date.now() / 1000);
255
+
256
+ if (timeUntilNext > 0) {
257
+ const hours = Math.floor(timeUntilNext / 3600);
258
+ const minutes = Math.floor((timeUntilNext % 3600) / 60);
259
+ console.log(` Next Payment: ${chalk.yellow(`${hours}h ${minutes}m`)}`);
260
+ } else {
261
+ console.log(` Next Payment: ${chalk.yellow('Due now')}`);
262
+ }
263
+ } catch (error) {
264
+ spinner.fail('Failed to calculate funding payment');
265
+ handleError(error, program.opts());
266
+ }
267
+ });
268
+
269
+ // Funding rate history (placeholder for future implementation)
270
+ funding
271
+ .command('history [asset]')
272
+ .description('Show funding rate history')
273
+ .option('-d, --days <days>', 'Number of days to show', '7')
274
+ .action(async (asset, options) => {
275
+ console.log(chalk.yellow('\nāš ļø Funding rate history is coming soon!'));
276
+ console.log('This feature will show historical funding rates and payments.');
277
+
278
+ const targetAsset = asset || 'BITMIND';
279
+ const days = parseInt(options.days);
280
+
281
+ console.log(chalk.bold('\nšŸ“… Parameters:'));
282
+ console.log(` Asset: ${chalk.cyan(targetAsset.toUpperCase())}`);
283
+ console.log(` Period: ${days} days`);
284
+ console.log(` Would fetch: Hourly funding rates, payment events, OI changes`);
285
+ });
286
+
287
+ // Simulate funding rate impact
288
+ funding
289
+ .command('simulate')
290
+ .description('Simulate funding rate scenarios')
291
+ .option('-a, --asset <asset>', 'Asset to simulate', 'BITMIND')
292
+ .option('-s, --size <size>', 'Position size in TAO', '1000')
293
+ .option('-l, --long', 'Simulate long position')
294
+ .option('-r, --rate <rate>', 'Funding rate to simulate (in %)', '0.01')
295
+ .action(async (options) => {
296
+ try {
297
+ const asset = options.asset.toUpperCase();
298
+ const positionSize = parseEther(options.size);
299
+ const isLong = options.long || false;
300
+ const simulatedRate = parseEther((parseFloat(options.rate) / 100).toString());
301
+
302
+ console.log(chalk.bold('\nšŸŽÆ Funding Rate Simulation:'));
303
+ console.log(` Asset: ${chalk.cyan(asset)}`);
304
+ console.log(
305
+ ` Position: ${formatTAO(positionSize)} TAO ${isLong ? chalk.green('LONG') : chalk.red('SHORT')}`,
306
+ );
307
+ console.log(` Simulated Rate: ${formatPercentage(simulatedRate)} per 8h`);
308
+
309
+ // Calculate payment scenarios
310
+ const scenarios = [
311
+ { period: '8 hours', multiplier: 1 },
312
+ { period: '1 day', multiplier: 3 },
313
+ { period: '1 week', multiplier: 21 },
314
+ { period: '1 month', multiplier: 90 },
315
+ ];
316
+
317
+ console.log(chalk.bold('\nšŸ“Š Payment Scenarios:'));
318
+ const table = new Table({
319
+ head: ['Period', 'Total Payment', 'Direction'],
320
+ colWidths: [15, 20, 15],
321
+ });
322
+
323
+ scenarios.forEach((scenario) => {
324
+ const totalPayment =
325
+ (positionSize * simulatedRate * BigInt(scenario.multiplier)) / 10000n;
326
+ const isPayment = (isLong && simulatedRate > 0n) || (!isLong && simulatedRate < 0n);
327
+ const paymentAmount = totalPayment < 0n ? -totalPayment : totalPayment;
328
+
329
+ const paymentStr = isPayment
330
+ ? chalk.red('-' + formatTAO(paymentAmount) + ' TAO')
331
+ : chalk.green('+' + formatTAO(paymentAmount) + ' TAO');
332
+
333
+ const directionStr = isPayment ? 'Pay' : 'Receive';
334
+
335
+ table.push([scenario.period, paymentStr, directionStr]);
336
+ });
337
+
338
+ console.log(table.toString());
339
+
340
+ console.log(chalk.bold('\nšŸ’” Tips:'));
341
+ console.log(' • Positive rates = Longs pay Shorts');
342
+ console.log(' • Negative rates = Shorts pay Longs');
343
+ console.log(' • Rates are typically 0.01% to 0.1% per 8h');
344
+ console.log(' • High OI imbalance increases funding rates');
345
+ } catch (error) {
346
+ handleError(error, program.opts());
347
+ }
348
+ });
349
+
350
+ // Update funding rates (keeper function)
351
+ funding
352
+ .command('update [assets...]')
353
+ .description('Update funding rates for assets (requires keeper role)')
354
+ .option('-a, --all', 'Update funding rates for all available assets')
355
+ .option('-y, --yes', 'Skip confirmation')
356
+ .action(async (assets, options) => {
357
+ const spinner = ora('Preparing funding rate update...').start();
358
+
359
+ try {
360
+ const client = await getClient(program.opts());
361
+ const alpha = client.getAlpha();
362
+
363
+ // Get assets to update
364
+ let assetsToUpdate: Address[] = [];
365
+
366
+ if (options.all) {
367
+ // Update all available assets
368
+ const { listAvailableMarketAddresses } = await import('../utils/client');
369
+ const availableMarkets = listAvailableMarketAddresses();
370
+ assetsToUpdate = Object.values(availableMarkets) as Address[];
371
+
372
+ if (assetsToUpdate.length === 0) {
373
+ spinner.fail('No market addresses configured');
374
+ console.log(chalk.yellow('Available subnet markets: BITMIND, CHUTES, AFFINE, RIDGES'));
375
+ return;
376
+ }
377
+ } else if (assets && assets.length > 0) {
378
+ // Update specific assets
379
+ for (const asset of assets) {
380
+ const marketAddress = getMarketAddress(asset);
381
+ assetsToUpdate.push(marketAddress);
382
+ }
383
+ } else {
384
+ spinner.fail('Please specify assets or use --all flag');
385
+ return;
386
+ }
387
+
388
+ spinner.stop();
389
+
390
+ // Show what we're updating
391
+ console.log(chalk.bold('\nšŸ“Š Funding Rate Update:'));
392
+ console.log(` Assets to update: ${chalk.cyan(assetsToUpdate.length)}`);
393
+
394
+ if (!options.all && assets) {
395
+ console.log(` Assets: ${assets.map((a) => chalk.cyan(a.toUpperCase())).join(', ')}`);
396
+ }
397
+
398
+ // Get current rates for display
399
+ const currentRates: string[] = [];
400
+ for (const assetAddress of assetsToUpdate) {
401
+ try {
402
+ const marketInfo = await alpha.getMarketInfo(assetAddress);
403
+ currentRates.push(formatPercentage(marketInfo.fundingRate));
404
+ } catch {
405
+ currentRates.push('N/A');
406
+ }
407
+ }
408
+
409
+ if (!options.all && assets && currentRates.length > 0) {
410
+ console.log(` Current rates: ${currentRates.join(', ')}`);
411
+ }
412
+
413
+ // Confirmation
414
+ if (!options.yes) {
415
+ const confirmed = await confirmAction(
416
+ 'Update funding rates?',
417
+ 'This will calculate and apply new funding rates based on current market conditions.',
418
+ );
419
+
420
+ if (!confirmed) {
421
+ console.log(chalk.yellow('Update cancelled'));
422
+ return;
423
+ }
424
+ }
425
+
426
+ // Update funding rates
427
+ const updateSpinner = ora('Updating funding rates...').start();
428
+
429
+ try {
430
+ const tx = await alpha.updateFundingRates(assetsToUpdate);
431
+
432
+ updateSpinner.text = 'Waiting for confirmation...';
433
+ const receipt = await tx.wait();
434
+
435
+ updateSpinner.succeed('Funding rates updated successfully!');
436
+
437
+ console.log(chalk.green(`\nāœ… Transaction confirmed`));
438
+ console.log(` Hash: ${chalk.cyan(receipt.transactionHash)}`);
439
+ console.log(` Gas used: ${chalk.yellow(receipt.gasUsed.toLocaleString())}`);
440
+
441
+ // Show new rates
442
+ console.log(chalk.bold('\nšŸ“ˆ New Funding Rates:'));
443
+ const table = new Table({
444
+ head: ['Asset', 'New Rate', 'Direction'],
445
+ colWidths: [10, 15, 25],
446
+ });
447
+
448
+ for (let i = 0; i < assetsToUpdate.length; i++) {
449
+ const assetAddress = assetsToUpdate[i];
450
+ const assetName = assets ? assets[i].toUpperCase() : `Asset ${i + 1}`;
451
+
452
+ try {
453
+ const marketInfo = await alpha.getMarketInfo(assetAddress);
454
+ const rate = Number(marketInfo.fundingRate);
455
+
456
+ let direction = 'Neutral';
457
+ if (rate > 0) direction = 'Longs pay Shorts';
458
+ else if (rate < 0) direction = 'Shorts pay Longs';
459
+
460
+ table.push([assetName, formatPercentage(marketInfo.fundingRate), direction]);
461
+ } catch {
462
+ table.push([assetName, 'Error', '-']);
463
+ }
464
+ }
465
+
466
+ console.log(table.toString());
467
+ } catch (error: any) {
468
+ updateSpinner.fail('Failed to update funding rates');
469
+ if (error.message?.includes('Unauthorized') || error.message?.includes('KEEPER_ROLE')) {
470
+ console.log(chalk.red('\nāŒ Error: This operation requires keeper role'));
471
+ console.log(chalk.yellow('Only authorized keepers can update funding rates'));
472
+ } else {
473
+ throw error;
474
+ }
475
+ }
476
+ } catch (error) {
477
+ spinner.fail('Failed to prepare funding rate update');
478
+ handleError(error, program.opts());
479
+ }
480
+ });
481
+ }