@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.
- package/.env.example +37 -0
- package/CHANGELOG.md +19 -0
- package/README.md +199 -0
- package/bin/alf +4 -0
- package/cli/README.md +198 -0
- package/cli/TEST_MANUAL.md +577 -0
- package/cli/commands/account.ts +545 -0
- package/cli/commands/funding.ts +481 -0
- package/cli/commands/liquidation.ts +523 -0
- package/cli/commands/market.ts +590 -0
- package/cli/commands/orders.ts +395 -0
- package/cli/commands/position.ts +1085 -0
- package/cli/commands/shared/positionUtils.ts +239 -0
- package/cli/commands/trading.ts +483 -0
- package/cli/commands/utils.ts +281 -0
- package/cli/commands/vault.ts +522 -0
- package/cli/index.ts +169 -0
- package/cli/interactive.ts +530 -0
- package/cli/utils/client.ts +457 -0
- package/cli/utils/config.ts +226 -0
- package/cli/utils/display.ts +258 -0
- package/cli/utils/index.ts +10 -0
- package/cli/utils/prompts.ts +364 -0
- package/config.example.json +23 -0
- package/dist/AlphaFuturesClient.d.ts +36 -0
- package/dist/AlphaFuturesClient.d.ts.map +1 -0
- package/dist/AlphaFuturesClient.js +116 -0
- package/dist/AlphaFuturesClient.js.map +1 -0
- package/dist/abi/Alpha.json +5987 -0
- package/dist/abi/abis.d.ts +319 -0
- package/dist/abi/abis.d.ts.map +1 -0
- package/dist/abi/abis.js +128 -0
- package/dist/abi/abis.js.map +1 -0
- package/dist/abi/index.d.ts +11 -0
- package/dist/abi/index.d.ts.map +1 -0
- package/dist/abi/index.js +15 -0
- package/dist/abi/index.js.map +1 -0
- package/dist/config/contracts.config.d.ts +70 -0
- package/dist/config/contracts.config.d.ts.map +1 -0
- package/dist/config/contracts.config.js +137 -0
- package/dist/config/contracts.config.js.map +1 -0
- package/dist/config/environments/alpha.config.d.ts +17 -0
- package/dist/config/environments/alpha.config.d.ts.map +1 -0
- package/dist/config/environments/alpha.config.js +140 -0
- package/dist/config/environments/alpha.config.js.map +1 -0
- package/dist/config/environments/beta.config.d.ts +16 -0
- package/dist/config/environments/beta.config.d.ts.map +1 -0
- package/dist/config/environments/beta.config.js +131 -0
- package/dist/config/environments/beta.config.js.map +1 -0
- package/dist/config/environments/dev.config.d.ts +13 -0
- package/dist/config/environments/dev.config.d.ts.map +1 -0
- package/dist/config/environments/dev.config.js +123 -0
- package/dist/config/environments/dev.config.js.map +1 -0
- package/dist/config/environments/index.d.ts +48 -0
- package/dist/config/environments/index.d.ts.map +1 -0
- package/dist/config/environments/index.js +81 -0
- package/dist/config/environments/index.js.map +1 -0
- package/dist/config/environments/localhost.config.d.ts +16 -0
- package/dist/config/environments/localhost.config.d.ts.map +1 -0
- package/dist/config/environments/localhost.config.js +152 -0
- package/dist/config/environments/localhost.config.js.map +1 -0
- package/dist/config/environments/prod.config.d.ts +20 -0
- package/dist/config/environments/prod.config.d.ts.map +1 -0
- package/dist/config/environments/prod.config.js +143 -0
- package/dist/config/environments/prod.config.js.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +41 -0
- package/dist/config/index.js.map +1 -0
- package/dist/constants/assets.d.ts +76 -0
- package/dist/constants/assets.d.ts.map +1 -0
- package/dist/constants/assets.js +277 -0
- package/dist/constants/assets.js.map +1 -0
- package/dist/constants/contracts.d.ts +41 -0
- package/dist/constants/contracts.d.ts.map +1 -0
- package/dist/constants/contracts.js +57 -0
- package/dist/constants/contracts.js.map +1 -0
- package/dist/constants/index.d.ts +36 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +75 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/constants/networks.d.ts +32 -0
- package/dist/constants/networks.d.ts.map +1 -0
- package/dist/constants/networks.js +174 -0
- package/dist/constants/networks.js.map +1 -0
- package/dist/contracts/index.d.ts +5 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +21 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/viem/AlphaViem.d.ts +518 -0
- package/dist/contracts/viem/AlphaViem.d.ts.map +1 -0
- package/dist/contracts/viem/AlphaViem.js +1287 -0
- package/dist/contracts/viem/AlphaViem.js.map +1 -0
- package/dist/contracts/viem/PriceOracleViem.d.ts +71 -0
- package/dist/contracts/viem/PriceOracleViem.d.ts.map +1 -0
- package/dist/contracts/viem/PriceOracleViem.js +212 -0
- package/dist/contracts/viem/PriceOracleViem.js.map +1 -0
- package/dist/contracts/viem/index.d.ts +9 -0
- package/dist/contracts/viem/index.d.ts.map +1 -0
- package/dist/contracts/viem/index.js +17 -0
- package/dist/contracts/viem/index.js.map +1 -0
- package/dist/errors/index.d.ts +44 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +83 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/types/alpha.d.ts +299 -0
- package/dist/types/alpha.d.ts.map +1 -0
- package/dist/types/alpha.js +6 -0
- package/dist/types/alpha.js.map +1 -0
- package/dist/types/client.d.ts +24 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/client.js +13 -0
- package/dist/types/client.js.map +1 -0
- package/dist/types/contracts.d.ts +48 -0
- package/dist/types/contracts.d.ts.map +1 -0
- package/dist/types/contracts.js +6 -0
- package/dist/types/contracts.js.map +1 -0
- package/dist/types/funding.d.ts +27 -0
- package/dist/types/funding.d.ts.map +1 -0
- package/dist/types/funding.js +6 -0
- package/dist/types/funding.js.map +1 -0
- package/dist/types/index.d.ts +92 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +47 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/liquidation.d.ts +20 -0
- package/dist/types/liquidation.d.ts.map +1 -0
- package/dist/types/liquidation.js +6 -0
- package/dist/types/liquidation.js.map +1 -0
- package/dist/types/margin.d.ts +29 -0
- package/dist/types/margin.d.ts.map +1 -0
- package/dist/types/margin.js +6 -0
- package/dist/types/margin.js.map +1 -0
- package/dist/types/oracle.d.ts +21 -0
- package/dist/types/oracle.d.ts.map +1 -0
- package/dist/types/oracle.js +6 -0
- package/dist/types/oracle.js.map +1 -0
- package/dist/types/positions.d.ts +43 -0
- package/dist/types/positions.d.ts.map +1 -0
- package/dist/types/positions.js +13 -0
- package/dist/types/positions.js.map +1 -0
- package/dist/utils/calculations.d.ts +84 -0
- package/dist/utils/calculations.d.ts.map +1 -0
- package/dist/utils/calculations.js +155 -0
- package/dist/utils/calculations.js.map +1 -0
- package/dist/utils/errors.d.ts +24 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +129 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/events.d.ts +40 -0
- package/dist/utils/events.d.ts.map +1 -0
- package/dist/utils/events.js +73 -0
- package/dist/utils/events.js.map +1 -0
- package/dist/utils/format.d.ts +40 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +86 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +26 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/network.d.ts +52 -0
- package/dist/utils/network.d.ts.map +1 -0
- package/dist/utils/network.js +192 -0
- package/dist/utils/network.js.map +1 -0
- package/dist/utils/positionCalculations.d.ts +145 -0
- package/dist/utils/positionCalculations.d.ts.map +1 -0
- package/dist/utils/positionCalculations.js +278 -0
- package/dist/utils/positionCalculations.js.map +1 -0
- package/dist/utils/validation.d.ts +28 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +68 -0
- package/dist/utils/validation.js.map +1 -0
- package/docs/README.md +40 -0
- package/docs/api/API.md +831 -0
- package/docs/guides/GETTING_STARTED.md +316 -0
- package/docs/guides/TRADING_GUIDE.md +677 -0
- package/docs/integration/INTEGRATION_GUIDE.md +1679 -0
- package/docs/integration/VIEM_INTEGRATION.md +294 -0
- package/docs/reference/CLI_QUICK_REFERENCE.md +197 -0
- package/docs/reference/TROUBLESHOOTING.md +922 -0
- package/package.json +113 -0
- package/src/AlphaFuturesClient.ts +158 -0
- package/src/abi/.gitkeep +1 -0
- package/src/abi/Alpha.json +5987 -0
- package/src/abi/README.md +99 -0
- package/src/abi/abis.ts +131 -0
- package/src/abi/index.ts +13 -0
- package/src/config/contracts.config.ts +186 -0
- package/src/config/environments/alpha.config.ts +139 -0
- package/src/config/environments/beta.config.ts +130 -0
- package/src/config/environments/dev.config.ts +122 -0
- package/src/config/environments/index.ts +87 -0
- package/src/config/environments/localhost.config.ts +153 -0
- package/src/config/environments/prod.config.ts +142 -0
- package/src/config/index.ts +29 -0
- package/src/constants/assets.ts +299 -0
- package/src/constants/contracts.ts +64 -0
- package/src/constants/index.ts +69 -0
- package/src/constants/networks.ts +182 -0
- package/src/contracts/index.ts +5 -0
- package/src/contracts/viem/AlphaViem.ts +1615 -0
- package/src/contracts/viem/PriceOracleViem.ts +272 -0
- package/src/contracts/viem/index.ts +11 -0
- package/src/errors/index.ts +87 -0
- package/src/index.ts +59 -0
- package/src/types/VIEM_TYPES_README.md +70 -0
- package/src/types/alpha.ts +358 -0
- package/src/types/client.ts +27 -0
- package/src/types/contracts.ts +74 -0
- package/src/types/funding.ts +31 -0
- package/src/types/index.ts +108 -0
- package/src/types/liquidation.ts +23 -0
- package/src/types/margin.ts +34 -0
- package/src/types/oracle.ts +24 -0
- package/src/types/positions.ts +48 -0
- package/src/utils/calculations.ts +175 -0
- package/src/utils/errors.ts +147 -0
- package/src/utils/events.ts +98 -0
- package/src/utils/format.ts +84 -0
- package/src/utils/index.ts +10 -0
- package/src/utils/network.ts +212 -0
- package/src/utils/positionCalculations.ts +317 -0
- 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
|
+
}
|