forgex-cli 1.0.59 → 1.0.61

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 (158) hide show
  1. package/README.md +375 -368
  2. package/dist/bin/forgex.d.ts +5 -5
  3. package/dist/bin/forgex.js +14 -14
  4. package/dist/bin/forgex.js.map +1 -1
  5. package/dist/src/adapters/codex-adapter.d.ts +90 -90
  6. package/dist/src/adapters/codex-adapter.d.ts.map +1 -1
  7. package/dist/src/adapters/codex-adapter.js +76 -76
  8. package/dist/src/adapters/codex-adapter.js.map +1 -1
  9. package/dist/src/adapters/connection.d.ts +6 -6
  10. package/dist/src/adapters/connection.js +8 -8
  11. package/dist/src/adapters/connection.js.map +1 -1
  12. package/dist/src/adapters/ipfs.d.ts +3 -3
  13. package/dist/src/adapters/ipfs.js +3 -3
  14. package/dist/src/adapters/jito-adapter.d.ts +85 -85
  15. package/dist/src/adapters/jito-adapter.d.ts.map +1 -1
  16. package/dist/src/adapters/jito-adapter.js +111 -111
  17. package/dist/src/adapters/jito-adapter.js.map +1 -1
  18. package/dist/src/adapters/rpc-adapter.d.ts +53 -53
  19. package/dist/src/adapters/rpc-adapter.d.ts.map +1 -1
  20. package/dist/src/adapters/rpc-adapter.js +69 -69
  21. package/dist/src/adapters/rpc-adapter.js.map +1 -1
  22. package/dist/src/adapters/sdk-adapter.d.ts +21 -21
  23. package/dist/src/adapters/sdk-adapter.d.ts.map +1 -1
  24. package/dist/src/adapters/sdk-adapter.js +79 -79
  25. package/dist/src/adapters/sdk-adapter.js.map +1 -1
  26. package/dist/src/commands/config/index.d.ts +1 -1
  27. package/dist/src/commands/config/index.js +15 -15
  28. package/dist/src/commands/config/index.js.map +1 -1
  29. package/dist/src/commands/query/index.d.ts +2 -2
  30. package/dist/src/commands/query/index.js +82 -82
  31. package/dist/src/commands/query/index.js.map +1 -1
  32. package/dist/src/commands/token/index.d.ts +8 -8
  33. package/dist/src/commands/token/index.js +73 -73
  34. package/dist/src/commands/token/index.js.map +1 -1
  35. package/dist/src/commands/tools/index.d.ts +9 -9
  36. package/dist/src/commands/tools/index.js +137 -137
  37. package/dist/src/commands/tools/index.js.map +1 -1
  38. package/dist/src/commands/trade/index.d.ts +2 -2
  39. package/dist/src/commands/trade/index.js +82 -82
  40. package/dist/src/commands/trade/index.js.map +1 -1
  41. package/dist/src/commands/transfer/index.d.ts +8 -8
  42. package/dist/src/commands/transfer/index.js +106 -106
  43. package/dist/src/commands/transfer/index.js.map +1 -1
  44. package/dist/src/commands/wallet/index.d.ts +1 -1
  45. package/dist/src/commands/wallet/index.js +175 -175
  46. package/dist/src/commands/wallet/index.js.map +1 -1
  47. package/dist/src/config.d.ts +26 -26
  48. package/dist/src/config.d.ts.map +1 -1
  49. package/dist/src/config.js +28 -28
  50. package/dist/src/config.js.map +1 -1
  51. package/dist/src/const/index.js +1 -1
  52. package/dist/src/const/index.js.map +1 -1
  53. package/dist/src/data-source.d.ts +81 -81
  54. package/dist/src/data-source.d.ts.map +1 -1
  55. package/dist/src/data-source.js +149 -149
  56. package/dist/src/data-source.js.map +1 -1
  57. package/dist/src/data-store/index.d.ts +22 -22
  58. package/dist/src/data-store/index.d.ts.map +1 -1
  59. package/dist/src/data-store/index.js +46 -46
  60. package/dist/src/data-store/index.js.map +1 -1
  61. package/dist/src/data-store/types.d.ts +3 -3
  62. package/dist/src/data-store/types.js +3 -3
  63. package/dist/src/index.d.ts +2 -2
  64. package/dist/src/index.js +10 -10
  65. package/dist/src/index.js.map +1 -1
  66. package/dist/src/output.d.ts +18 -18
  67. package/dist/src/output.d.ts.map +1 -1
  68. package/dist/src/output.js +34 -34
  69. package/dist/src/output.js.map +1 -1
  70. package/dist/src/shims/store.d.ts +3 -2
  71. package/dist/src/shims/store.d.ts.map +1 -1
  72. package/dist/src/shims/store.js +6 -5
  73. package/dist/src/shims/store.js.map +1 -1
  74. package/dist/src/sol-sdk/batch/create.d.ts +4 -1
  75. package/dist/src/sol-sdk/batch/create.d.ts.map +1 -1
  76. package/dist/src/sol-sdk/batch/create.js +44 -44
  77. package/dist/src/sol-sdk/batch/create.js.map +1 -1
  78. package/dist/src/sol-sdk/batch/index.js +135 -135
  79. package/dist/src/sol-sdk/batch/index.js.map +1 -1
  80. package/dist/src/sol-sdk/calc.d.ts +63 -63
  81. package/dist/src/sol-sdk/calc.d.ts.map +1 -1
  82. package/dist/src/sol-sdk/calc.js +120 -120
  83. package/dist/src/sol-sdk/calc.js.map +1 -1
  84. package/dist/src/sol-sdk/jito/index.js +12 -12
  85. package/dist/src/sol-sdk/jito/index.js.map +1 -1
  86. package/dist/src/sol-sdk/launchlab/instructions/create.js +10 -10
  87. package/dist/src/sol-sdk/launchlab/instructions/create.js.map +1 -1
  88. package/dist/src/sol-sdk/meteora/index.d.ts +5 -5
  89. package/dist/src/sol-sdk/meteora/index.js +11 -11
  90. package/dist/src/sol-sdk/meteora/index.js.map +1 -1
  91. package/dist/src/sol-sdk/meteora/instructions/buy.js +8 -8
  92. package/dist/src/sol-sdk/meteora/instructions/buy.js.map +1 -1
  93. package/dist/src/sol-sdk/meteora/instructions/sell.js +6 -6
  94. package/dist/src/sol-sdk/meteora/instructions/sell.js.map +1 -1
  95. package/dist/src/sol-sdk/pump/index.js +3 -3
  96. package/dist/src/sol-sdk/pump/index.js.map +1 -1
  97. package/dist/src/sol-sdk/pump/instructions/buy.d.ts +12 -12
  98. package/dist/src/sol-sdk/pump/instructions/buy.d.ts.map +1 -1
  99. package/dist/src/sol-sdk/pump/instructions/buy.js +26 -26
  100. package/dist/src/sol-sdk/pump/instructions/buy.js.map +1 -1
  101. package/dist/src/sol-sdk/pump/instructions/createAndBuy.d.ts +13 -13
  102. package/dist/src/sol-sdk/pump/instructions/createAndBuy.js +17 -17
  103. package/dist/src/sol-sdk/pump/instructions/createAndBuy.js.map +1 -1
  104. package/dist/src/sol-sdk/pump/instructions/sell.d.ts +2 -2
  105. package/dist/src/sol-sdk/pump/instructions/sell.d.ts.map +1 -1
  106. package/dist/src/sol-sdk/pump/instructions/sell.js +7 -7
  107. package/dist/src/sol-sdk/pump/instructions/sell.js.map +1 -1
  108. package/dist/src/sol-sdk/pumpswap/index.d.ts +4 -4
  109. package/dist/src/sol-sdk/pumpswap/index.js +5 -5
  110. package/dist/src/sol-sdk/pumpswap/index.js.map +1 -1
  111. package/dist/src/sol-sdk/pumpswap/instructions/buy.d.ts +8 -8
  112. package/dist/src/sol-sdk/pumpswap/instructions/buy.js +19 -19
  113. package/dist/src/sol-sdk/pumpswap/instructions/buy.js.map +1 -1
  114. package/dist/src/sol-sdk/pumpswap/instructions/migrate.js +2 -2
  115. package/dist/src/sol-sdk/pumpswap/instructions/migrate.js.map +1 -1
  116. package/dist/src/sol-sdk/pumpswap/instructions/sell.js +4 -4
  117. package/dist/src/sol-sdk/pumpswap/instructions/sell.js.map +1 -1
  118. package/dist/src/sol-sdk/pumpswap/rpc/index.js +1 -1
  119. package/dist/src/sol-sdk/pumpswap/rpc/index.js.map +1 -1
  120. package/dist/src/sol-sdk/raydium/instructions/cpmmSell.js +3 -3
  121. package/dist/src/sol-sdk/raydium/instructions/cpmmSell.js.map +1 -1
  122. package/dist/src/sol-sdk/raydium/instructions/sell.d.ts +40 -8520
  123. package/dist/src/sol-sdk/raydium/instructions/sell.d.ts.map +1 -1
  124. package/dist/src/sol-sdk/raydium/instructions/sell.js +6 -6
  125. package/dist/src/sol-sdk/raydium/instructions/sell.js.map +1 -1
  126. package/dist/src/sol-sdk/raydium/rpc/index.d.ts +4 -4
  127. package/dist/src/sol-sdk/rpc/index.d.ts +14 -14
  128. package/dist/src/sol-sdk/rpc/index.d.ts.map +1 -1
  129. package/dist/src/sol-sdk/rpc/index.js +17 -17
  130. package/dist/src/sol-sdk/rpc/index.js.map +1 -1
  131. package/dist/src/sol-sdk/transfer/index.js +5 -5
  132. package/dist/src/sol-sdk/transfer/index.js.map +1 -1
  133. package/dist/src/sol-sdk/turnover/index.d.ts +3 -3
  134. package/dist/src/sol-sdk/turnover/index.js +56 -56
  135. package/dist/src/sol-sdk/turnover/index.js.map +1 -1
  136. package/dist/src/telemetry.d.ts +8 -8
  137. package/dist/src/telemetry.d.ts.map +1 -1
  138. package/dist/src/telemetry.js +25 -25
  139. package/dist/src/telemetry.js.map +1 -1
  140. package/dist/src/tx-tracker/detail-adapter.d.ts +53 -53
  141. package/dist/src/tx-tracker/detail-adapter.d.ts.map +1 -1
  142. package/dist/src/tx-tracker/detail-adapter.js +68 -68
  143. package/dist/src/tx-tracker/detail-adapter.js.map +1 -1
  144. package/dist/src/tx-tracker/index.d.ts +67 -67
  145. package/dist/src/tx-tracker/index.d.ts.map +1 -1
  146. package/dist/src/tx-tracker/index.js +103 -103
  147. package/dist/src/tx-tracker/index.js.map +1 -1
  148. package/dist/src/types/index.d.ts +10 -10
  149. package/dist/src/types/index.d.ts.map +1 -1
  150. package/dist/src/types/websocket.js +1 -1
  151. package/dist/src/types/websocket.js.map +1 -1
  152. package/dist/src/utils/index.js +20 -20
  153. package/dist/src/utils/index.js.map +1 -1
  154. package/dist/src/wallet-store.d.ts +51 -51
  155. package/dist/src/wallet-store.d.ts.map +1 -1
  156. package/dist/src/wallet-store.js +104 -104
  157. package/dist/src/wallet-store.js.map +1 -1
  158. package/package.json +1 -1
@@ -1,16 +1,16 @@
1
1
  /**
2
- * 做市工具命令组 - 真实交易执行
2
+ * Market making tools command group - real transaction execution
3
3
  *
4
4
  * forgex tools turnover | volume | robot-price
5
5
  *
6
- * Phase 3 升级:
7
- * - 使用 DataSource 门面代替直接 sdk-adapter 余额查询
8
- * - 集成 TxTracker 自动追踪交易结果
9
- * - 添加 ensurePasswordAndValidate 密码验证
10
- * - 添加 suppressConsole/restoreConsole 防止 JSON 输出污染
11
- * - 完善输入参数验证
12
- * - 完善 --daemon 模式支持
13
- * - 修复 slippage bps -> 百分比转换
6
+ * Phase 3 upgrade:
7
+ * - Use DataSource facade instead of direct sdk-adapter balance queries
8
+ * - Integrate TxTracker for automatic transaction result tracking
9
+ * - Add ensurePasswordAndValidate for password verification
10
+ * - Add suppressConsole/restoreConsole to prevent JSON output pollution
11
+ * - Improve input parameter validation
12
+ * - Improve --daemon mode support
13
+ * - Fix slippage bps -> percentage conversion
14
14
  */
15
15
  import { getGroup, getDecryptedPrivateKey, ensurePasswordAndValidate } from '../../wallet-store.js';
16
16
  import { loadConfig } from '../../config.js';
@@ -20,80 +20,80 @@ import { getDataSource } from '../../data-source.js';
20
20
  export function registerToolsCommands(program) {
21
21
  const toolsCmd = program
22
22
  .command('tools')
23
- .description('专业做市工具');
23
+ .description('Professional market making tools');
24
24
  // ============================================================
25
- // forgex tools turnover - 真实换手交易
25
+ // forgex tools turnover - real turnover trade
26
26
  // ============================================================
27
27
  toolsCmd
28
28
  .command('turnover')
29
- .description('换手交易(钱包间零滑点换手,使用 Jito Bundle')
30
- .requiredOption('--from-group <id>', '源钱包组 ID')
31
- .requiredOption('--to-group <id>', '目标钱包组 ID')
32
- .requiredOption('--token <ca>', '代币合约地址')
33
- .option('--amount <value>', '金额: 固定代币数量 | 百分比(50%) | all(全部)', 'all')
34
- .option('--interval <ms>', '转账间隔 (毫秒)', '1000')
35
- .option('--priority-fee <sol>', '优先费 (SOL)')
36
- .option('--slippage <bps>', '滑点 (bps, 例如 100 = 1%)', '100')
37
- .option('--dry-run', '仅模拟,不实际执行', false)
38
- .option('--fallback-send-tx', 'Bundle 失败时降级为 sendTransaction(不保证同区块)', false)
39
- .option('--daemon', '后台长驻模式(持续执行)', false)
40
- .option('--rounds <n>', '执行轮数(0=无限)', '1')
29
+ .description('Turnover trade (zero-slippage turnover between wallets, using Jito Bundle)')
30
+ .requiredOption('--from-group <id>', 'Source wallet group ID')
31
+ .requiredOption('--to-group <id>', 'Target wallet group ID')
32
+ .requiredOption('--token <ca>', 'Token contract address')
33
+ .option('--amount <value>', 'Amount: fixed token count | percentage (e.g. 50%) | all', 'all')
34
+ .option('--interval <ms>', 'Transfer interval (ms)', '1000')
35
+ .option('--priority-fee <sol>', 'Priority fee (SOL)')
36
+ .option('--slippage <bps>', 'Slippage (bps, e.g. 100 = 1%)', '100')
37
+ .option('--dry-run', 'Simulate only, do not execute', false)
38
+ .option('--fallback-send-tx', 'Fallback to sendTransaction when Bundle fails (no same-block guarantee)', false)
39
+ .option('--daemon', 'Daemon mode (continuous execution)', false)
40
+ .option('--rounds <n>', 'Number of rounds (0=infinite)', '1')
41
41
  .action(async (options) => {
42
42
  try {
43
- // 密码验证
43
+ // Password verification
44
44
  await ensurePasswordAndValidate();
45
45
  const config = loadConfig();
46
46
  const fromGroupId = Number(options.fromGroup);
47
47
  const toGroupId = Number(options.toGroup);
48
- // 参数验证
48
+ // Parameter validation
49
49
  if (isNaN(fromGroupId)) {
50
- error('--from-group 必须是有效数字');
50
+ error('--from-group must be a valid number');
51
51
  process.exit(1);
52
52
  }
53
53
  if (isNaN(toGroupId)) {
54
- error('--to-group 必须是有效数字');
54
+ error('--to-group must be a valid number');
55
55
  process.exit(1);
56
56
  }
57
57
  const fromGroup = getGroup(fromGroupId);
58
58
  const toGroup = getGroup(toGroupId);
59
59
  if (!fromGroup) {
60
- error(`源钱包组 ${fromGroupId} 不存在`);
60
+ error(`Source wallet group ${fromGroupId} does not exist`);
61
61
  process.exit(1);
62
62
  }
63
63
  if (!toGroup) {
64
- error(`目标钱包组 ${toGroupId} 不存在`);
64
+ error(`Target wallet group ${toGroupId} does not exist`);
65
65
  process.exit(1);
66
66
  }
67
67
  if (fromGroup.wallets.length === 0) {
68
- error('源钱包组中没有钱包');
68
+ error('Source wallet group has no wallets');
69
69
  process.exit(1);
70
70
  }
71
71
  if (toGroup.wallets.length === 0) {
72
- error('目标钱包组中没有钱包');
72
+ error('Target wallet group has no wallets');
73
73
  process.exit(1);
74
74
  }
75
75
  const slippageBps = Number(options.slippage);
76
76
  if (isNaN(slippageBps) || slippageBps <= 0 || slippageBps > 5000) {
77
- error('--slippage 必须是 1-5000 之间的数字 (bps, 5000 = 50%)');
77
+ error('--slippage must be a number between 1-5000 (bps, 5000 = 50%)');
78
78
  process.exit(1);
79
79
  }
80
80
  const priorityFee = options.priorityFee
81
81
  ? Number(options.priorityFee)
82
82
  : config.defaultPriorityFee;
83
83
  if (isNaN(priorityFee) || priorityFee < 0) {
84
- error('--priority-fee 必须是非负数字');
84
+ error('--priority-fee must be a non-negative number');
85
85
  process.exit(1);
86
86
  }
87
87
  const maxRounds = Number(options.rounds);
88
88
  if (isNaN(maxRounds) || maxRounds < 0) {
89
- error('--rounds 必须是非负整数');
89
+ error('--rounds must be a non-negative integer');
90
90
  process.exit(1);
91
91
  }
92
- // 滑点转换: bps -> 小数 (SDK 期望 0.03 表示 3%, 300bps / 10000)
92
+ // Slippage conversion: bps -> decimal (SDK expects 0.03 for 3%, i.e. 300bps / 10000)
93
93
  const slippage = slippageBps / 10000;
94
- info(`获取代币 ${options.token} 的交易上下文...`);
94
+ info(`Fetching trade context for token ${options.token}...`);
95
95
  const context = await fetchTradeContext(options.token);
96
- info(`DEX: ${context.exchangeName}, 价格: ${context.priceInSol} SOL`);
96
+ info(`DEX: ${context.exchangeName}, price: ${context.priceInSol} SOL`);
97
97
  if (options.dryRun) {
98
98
  const estimatedFee = 0;
99
99
  output({
@@ -103,25 +103,25 @@ export function registerToolsCommands(program) {
103
103
  fromWallets: fromGroup.wallets.length,
104
104
  toWallets: toGroup.wallets.length,
105
105
  estimatedFee,
106
- message: '模拟成功',
106
+ message: 'Simulation successful',
107
107
  });
108
108
  return;
109
109
  }
110
- // DataSource 用于余额查询
110
+ // DataSource for balance queries
111
111
  const ds = getDataSource();
112
112
  let currentRound = 0;
113
113
  const executeOneRound = async () => {
114
114
  currentRound++;
115
- info(`执行第 ${currentRound} 轮换手...`);
116
- // JSON 模式下抑制 SDK console.log
115
+ info(`Executing round ${currentRound} turnover...`);
116
+ // Suppress SDK console.log in JSON mode
117
117
  if (getOutputFormat() === 'json')
118
118
  suppressConsole();
119
- // 构建换手配对
120
- // 支持 1→多: 如果 from 钱包少于 to 钱包,自动将代币按比例拆分到多个目标
119
+ // Build turnover pairs
120
+ // Support 1-to-many: if from wallets < to wallets, split tokens proportionally to multiple targets
121
121
  const turnoverItems = [];
122
122
  const fromWalletAddresses = [];
123
123
  if (fromGroup.wallets.length >= toGroup.wallets.length) {
124
- // from >= to: 1:1 配对(原有逻辑)
124
+ // from >= to: 1:1 pairing (original logic)
125
125
  const pairCount = Math.min(fromGroup.wallets.length, toGroup.wallets.length);
126
126
  for (let i = 0; i < pairCount; i++) {
127
127
  const fromWallet = fromGroup.wallets[i];
@@ -147,8 +147,8 @@ export function registerToolsCommands(program) {
147
147
  }
148
148
  }
149
149
  else {
150
- // from < to: 1→多 拆分模式
151
- // 每个 from 钱包的代币平均分配到若干个 to 钱包
150
+ // from < to: 1-to-many split mode
151
+ // Each from wallet's tokens are evenly distributed to multiple to wallets
152
152
  const toPerFrom = Math.ceil(toGroup.wallets.length / fromGroup.wallets.length);
153
153
  let toIndex = 0;
154
154
  for (let i = 0; i < fromGroup.wallets.length; i++) {
@@ -165,7 +165,7 @@ export function registerToolsCommands(program) {
165
165
  }
166
166
  if (totalAmount <= 0)
167
167
  continue;
168
- // 计算这个 from 钱包需要分配给多少个 to 钱包
168
+ // Calculate how many to wallets this from wallet should distribute to
169
169
  const remainingTo = toGroup.wallets.length - toIndex;
170
170
  const remainingFrom = fromGroup.wallets.length - i;
171
171
  const allocCount = Math.min(Math.ceil(remainingTo / remainingFrom), remainingTo);
@@ -184,8 +184,8 @@ export function registerToolsCommands(program) {
184
184
  }
185
185
  if (turnoverItems.length === 0) {
186
186
  restoreConsole();
187
- info('没有可用的换手配对(余额为0');
188
- output({ round: currentRound, total: 0, success: 0, failed: 0, message: '没有可用的换手配对(余额为0' });
187
+ info('No available turnover pairs (balance is 0)');
188
+ output({ round: currentRound, total: 0, success: 0, failed: 0, message: 'No available turnover pairs (balance is 0)' });
189
189
  return;
190
190
  }
191
191
  const results = await executeBatchTurnoverTrade({
@@ -217,20 +217,20 @@ export function registerToolsCommands(program) {
217
217
  const scheduleNext = () => {
218
218
  setTimeout(async () => {
219
219
  try {
220
- // 刷新交易上下文(价格可能变化)
220
+ // Refresh trade context (price may have changed)
221
221
  const newContext = await fetchTradeContext(options.token);
222
222
  Object.assign(context, newContext);
223
223
  await executeOneRound();
224
224
  }
225
225
  catch (e) {
226
226
  restoreConsole();
227
- error(`轮次 ${currentRound + 1} 执行出错: ${e.message}`);
227
+ error(`Round ${currentRound + 1} execution error: ${e.message}`);
228
228
  }
229
229
  if (maxRounds === 0 || currentRound < maxRounds) {
230
230
  scheduleNext();
231
231
  }
232
232
  else {
233
- info(`换手已完成 ${maxRounds} 轮`);
233
+ info(`Turnover completed ${maxRounds} rounds`);
234
234
  process.exit(0);
235
235
  }
236
236
  }, Number(options.interval));
@@ -238,36 +238,36 @@ export function registerToolsCommands(program) {
238
238
  scheduleNext();
239
239
  process.on('SIGINT', () => {
240
240
  restoreConsole();
241
- info(`换手已停止,共执行 ${currentRound} 轮`);
241
+ info(`Turnover stopped, executed ${currentRound} rounds total`);
242
242
  process.exit(0);
243
243
  });
244
244
  }
245
245
  }
246
246
  catch (e) {
247
247
  restoreConsole();
248
- error('换手失败', e.message);
248
+ error('Turnover failed', e.message);
249
249
  process.exit(1);
250
250
  }
251
251
  });
252
252
  // ============================================================
253
- // forgex tools volume - 真实刷量交易
253
+ // forgex tools volume - real volume trading
254
254
  // ============================================================
255
255
  toolsCmd
256
256
  .command('volume')
257
- .description('刷量工具(零损失提升交易量)')
258
- .requiredOption('--group <groupId>', '钱包组 ID')
259
- .requiredOption('--token <ca>', '代币合约地址')
260
- .option('--mode <mode>', '刷量模式: 1b1s | 1b2s | 1b3s | 2b1s | 3b1s', '1b1s')
261
- .option('--amount <sol>', '交易金额 (SOL)', '0.01')
262
- .option('--interval <ms>', '交易间隔 (毫秒)', '5000')
263
- .option('--rounds <n>', '执行轮数(0=无限)', '1')
264
- .option('--slippage <bps>', '滑点 (bps)', '300')
265
- .option('--priority-fee <sol>', '优先费 (SOL)')
266
- .option('--dry-run', '仅模拟,不实际执行', false)
267
- .option('--daemon', '后台长驻模式', false)
257
+ .description('Volume tool (zero-loss volume boost)')
258
+ .requiredOption('--group <groupId>', 'Wallet group ID')
259
+ .requiredOption('--token <ca>', 'Token contract address')
260
+ .option('--mode <mode>', 'Volume mode: 1b1s | 1b2s | 1b3s | 2b1s | 3b1s', '1b1s')
261
+ .option('--amount <sol>', 'Trade amount (SOL)', '0.01')
262
+ .option('--interval <ms>', 'Trade interval (ms)', '5000')
263
+ .option('--rounds <n>', 'Number of rounds (0=infinite)', '1')
264
+ .option('--slippage <bps>', 'Slippage (bps)', '300')
265
+ .option('--priority-fee <sol>', 'Priority fee (SOL)')
266
+ .option('--dry-run', 'Simulate only, do not execute', false)
267
+ .option('--daemon', 'Daemon mode', false)
268
268
  .action(async (options) => {
269
269
  try {
270
- // 密码验证
270
+ // Password verification
271
271
  await ensurePasswordAndValidate();
272
272
  const config = loadConfig();
273
273
  const groupId = Number(options.group);
@@ -276,48 +276,48 @@ export function registerToolsCommands(program) {
276
276
  const priorityFee = options.priorityFee
277
277
  ? Number(options.priorityFee)
278
278
  : config.defaultPriorityFee;
279
- // 参数验证
279
+ // Parameter validation
280
280
  if (isNaN(groupId)) {
281
- error('--group 必须是有效数字');
281
+ error('--group must be a valid number');
282
282
  process.exit(1);
283
283
  }
284
284
  if (isNaN(amount) || amount <= 0) {
285
- error('--amount 必须是大于 0 的数字');
285
+ error('--amount must be a number greater than 0');
286
286
  process.exit(1);
287
287
  }
288
288
  if (isNaN(slippageBps) || slippageBps <= 0 || slippageBps > 5000) {
289
- error('--slippage 必须是 1-5000 之间的数字 (bps, 5000 = 50%)');
289
+ error('--slippage must be a number between 1-5000 (bps, 5000 = 50%)');
290
290
  process.exit(1);
291
291
  }
292
292
  if (isNaN(priorityFee) || priorityFee < 0) {
293
- error('--priority-fee 必须是非负数字');
293
+ error('--priority-fee must be a non-negative number');
294
294
  process.exit(1);
295
295
  }
296
- // --mode 校验
296
+ // --mode validation
297
297
  const validModes = ['1b1s', '1b2s', '1b3s', '2b1s', '3b1s'];
298
298
  if (!validModes.includes(options.mode)) {
299
- error(`--mode 无效: "${options.mode}",必须是 1b1s | 1b2s | 1b3s | 2b1s | 3b1s`);
299
+ error(`--mode invalid: "${options.mode}", must be 1b1s | 1b2s | 1b3s | 2b1s | 3b1s`);
300
300
  process.exit(1);
301
301
  }
302
302
  const maxRounds = Number(options.rounds);
303
303
  if (isNaN(maxRounds) || maxRounds < 0) {
304
- error('--rounds 必须是非负整数');
304
+ error('--rounds must be a non-negative integer');
305
305
  process.exit(1);
306
306
  }
307
307
  const group = getGroup(groupId);
308
308
  if (!group) {
309
- error(`钱包组 ${groupId} 不存在`);
309
+ error(`Wallet group ${groupId} does not exist`);
310
310
  process.exit(1);
311
311
  }
312
312
  if (group.wallets.length === 0) {
313
- error('钱包组中没有钱包');
313
+ error('Wallet group has no wallets');
314
314
  process.exit(1);
315
315
  }
316
- // 滑点转换: bps -> 百分比
316
+ // Slippage conversion: bps -> percentage
317
317
  const slippage = slippageBps / 10;
318
- info(`获取代币 ${options.token} 的交易上下文...`);
318
+ info(`Fetching trade context for token ${options.token}...`);
319
319
  let context = await fetchTradeContext(options.token);
320
- info(`DEX: ${context.exchangeName}, 价格: ${context.priceInSol} SOL`);
320
+ info(`DEX: ${context.exchangeName}, price: ${context.priceInSol} SOL`);
321
321
  if (options.dryRun) {
322
322
  output({
323
323
  dryRun: true,
@@ -326,7 +326,7 @@ export function registerToolsCommands(program) {
326
326
  wallets: group.wallets.length,
327
327
  mode: options.mode,
328
328
  estimatedFeePerRound: 0,
329
- message: '模拟成功',
329
+ message: 'Simulation successful',
330
330
  });
331
331
  return;
332
332
  }
@@ -336,13 +336,13 @@ export function registerToolsCommands(program) {
336
336
  let currentRound = 0;
337
337
  const executeOneRound = async () => {
338
338
  currentRound++;
339
- info(`执行第 ${currentRound} 轮刷量 (模式: ${options.mode})...`);
339
+ info(`Executing round ${currentRound} volume (mode: ${options.mode})...`);
340
340
  try {
341
- // 每轮刷新上下文获取最新价格
341
+ // Refresh context each round for latest price
342
342
  if (currentRound > 1) {
343
343
  context = await fetchTradeContext(options.token);
344
344
  }
345
- // JSON 模式下抑制 SDK console.log
345
+ // Suppress SDK console.log in JSON mode
346
346
  if (getOutputFormat() === 'json')
347
347
  suppressConsole();
348
348
  const result = await executeTrades({
@@ -363,7 +363,7 @@ export function registerToolsCommands(program) {
363
363
  }
364
364
  catch (e) {
365
365
  restoreConsole();
366
- error(`轮次 ${currentRound} 失败: ${e.message}`);
366
+ error(`Round ${currentRound} failed: ${e.message}`);
367
367
  }
368
368
  };
369
369
  await executeOneRound();
@@ -375,7 +375,7 @@ export function registerToolsCommands(program) {
375
375
  scheduleNext();
376
376
  }
377
377
  else {
378
- info(`刷量已完成 ${maxRounds} 轮`);
378
+ info(`Volume completed ${maxRounds} rounds`);
379
379
  process.exit(0);
380
380
  }
381
381
  }, Number(options.interval));
@@ -383,39 +383,39 @@ export function registerToolsCommands(program) {
383
383
  scheduleNext();
384
384
  process.on('SIGINT', () => {
385
385
  restoreConsole();
386
- info(`刷量已停止,共执行 ${currentRound} 轮`);
386
+ info(`Volume trading stopped, executed ${currentRound} rounds`);
387
387
  process.exit(0);
388
388
  });
389
389
  }
390
390
  }
391
391
  catch (e) {
392
392
  restoreConsole();
393
- error('刷量失败', e.message);
393
+ error('Volume trading failed', e.message);
394
394
  process.exit(1);
395
395
  }
396
396
  });
397
397
  // ============================================================
398
- // forgex tools robot-price - 真实价格机器人
398
+ // forgex tools robot-price - real price robot
399
399
  // ============================================================
400
400
  toolsCmd
401
401
  .command('robot-price')
402
- .description('价格机器人(自动拉升/砸盘到目标价格)')
403
- .requiredOption('--group <groupId>', '钱包组 ID')
404
- .requiredOption('--token <ca>', '代币合约地址')
405
- .requiredOption('--direction <dir>', '方向: up(拉升) | down(砸盘)')
406
- .requiredOption('--target-price <price>', '目标价格 (SOL)')
407
- .option('--amount <sol>', '单次交易金额 (SOL)', '0.01')
408
- .option('--amount-type <type>', '金额类型: fixed | random', 'fixed')
409
- .option('--amount-max <sol>', '最大金额(random 模式)')
410
- .option('--interval <ms>', '交易间隔 (毫秒)', '5000')
411
- .option('--max-cost <sol>', '总消耗上限 (SOL)')
412
- .option('--slippage <bps>', '滑点 (bps)', '300')
413
- .option('--priority-fee <sol>', '优先费 (SOL)')
414
- .option('--dry-run', '仅模拟,不实际执行', false)
415
- .option('--daemon', '后台长驻模式(默认开启,价格机器人本身即为持续执行)', true)
402
+ .description('Price robot (auto pump/dump to target price)')
403
+ .requiredOption('--group <groupId>', 'Wallet group ID')
404
+ .requiredOption('--token <ca>', 'Token contract address')
405
+ .requiredOption('--direction <dir>', 'Direction: up (pump) | down (dump)')
406
+ .requiredOption('--target-price <price>', 'Target price (SOL)')
407
+ .option('--amount <sol>', 'Amount per trade (SOL)', '0.01')
408
+ .option('--amount-type <type>', 'Amount type: fixed | random', 'fixed')
409
+ .option('--amount-max <sol>', 'Max amount (random mode)')
410
+ .option('--interval <ms>', 'Trade interval (ms)', '5000')
411
+ .option('--max-cost <sol>', 'Max total cost (SOL)')
412
+ .option('--slippage <bps>', 'Slippage (bps)', '300')
413
+ .option('--priority-fee <sol>', 'Priority fee (SOL)')
414
+ .option('--dry-run', 'Dry run only, no actual execution', false)
415
+ .option('--daemon', 'Daemon mode (enabled by default, price robot runs continuously)', true)
416
416
  .action(async (options) => {
417
417
  try {
418
- // 密码验证
418
+ // Password verification
419
419
  await ensurePasswordAndValidate();
420
420
  const config = loadConfig();
421
421
  const groupId = Number(options.group);
@@ -425,47 +425,47 @@ export function registerToolsCommands(program) {
425
425
  const priorityFee = options.priorityFee
426
426
  ? Number(options.priorityFee)
427
427
  : config.defaultPriorityFee;
428
- // 参数验证
428
+ // Parameter validation
429
429
  if (isNaN(groupId)) {
430
- error('--group 必须是有效数字');
430
+ error('--group must be a valid number');
431
431
  process.exit(1);
432
432
  }
433
433
  if (isNaN(amountVal) || amountVal <= 0) {
434
- error('--amount 必须是大于 0 的数字');
434
+ error('--amount must be a number greater than 0');
435
435
  process.exit(1);
436
436
  }
437
437
  if (isNaN(slippageBps) || slippageBps <= 0 || slippageBps > 5000) {
438
- error('--slippage 必须是 1-5000 之间的数字 (bps, 5000 = 50%)');
438
+ error('--slippage must be a number between 1-5000 (bps, 5000 = 50%)');
439
439
  process.exit(1);
440
440
  }
441
441
  if (isNaN(priorityFee) || priorityFee < 0) {
442
- error('--priority-fee 必须是非负数字');
442
+ error('--priority-fee must be a non-negative number');
443
443
  process.exit(1);
444
444
  }
445
445
  if (isNaN(targetPrice) || targetPrice <= 0) {
446
- error('--target-price 必须是大于 0 的数字');
446
+ error('--target-price must be a number greater than 0');
447
447
  process.exit(1);
448
448
  }
449
- // --direction 校验
449
+ // --direction validation
450
450
  const validDirections = ['up', 'down'];
451
451
  if (!validDirections.includes(options.direction)) {
452
- error(`--direction 无效: "${options.direction}",必须是 up | down`);
452
+ error(`--direction invalid: "${options.direction}", must be up | down`);
453
453
  process.exit(1);
454
454
  }
455
455
  const group = getGroup(groupId);
456
456
  if (!group) {
457
- error(`钱包组 ${groupId} 不存在`);
457
+ error(`Wallet group ${groupId} does not exist`);
458
458
  process.exit(1);
459
459
  }
460
460
  if (group.wallets.length === 0) {
461
- error('钱包组中没有钱包');
461
+ error('No wallets in wallet group');
462
462
  process.exit(1);
463
463
  }
464
- // 滑点转换: bps -> 百分比
464
+ // Slippage conversion: bps -> percentage
465
465
  const slippage = slippageBps / 10;
466
- info(`获取代币 ${options.token} 的交易上下文...`);
466
+ info(`Fetching trade context for token ${options.token}...`);
467
467
  let context = await fetchTradeContext(options.token);
468
- info(`DEX: ${context.exchangeName}, 当前价格: ${context.priceInSol} SOL, 目标: ${targetPrice} SOL`);
468
+ info(`DEX: ${context.exchangeName}, Current price: ${context.priceInSol} SOL, Target: ${targetPrice} SOL`);
469
469
  if (options.dryRun) {
470
470
  output({
471
471
  dryRun: true,
@@ -473,7 +473,7 @@ export function registerToolsCommands(program) {
473
473
  currentPrice: context.priceInSol,
474
474
  targetPrice,
475
475
  direction: options.direction,
476
- message: '模拟成功',
476
+ message: 'Simulation successful',
477
477
  });
478
478
  return;
479
479
  }
@@ -485,22 +485,22 @@ export function registerToolsCommands(program) {
485
485
  if (options.maxCost) {
486
486
  maxCost = Number(options.maxCost);
487
487
  if (isNaN(maxCost) || maxCost <= 0) {
488
- error('--max-cost 必须是大于 0 的数字');
488
+ error('--max-cost must be a number greater than 0');
489
489
  process.exit(1);
490
490
  }
491
491
  }
492
- info(`价格机器人启动: ${options.direction === 'up' ? '拉升' : '砸盘'} -> ${targetPrice} SOL`);
492
+ info(`Price robot started: ${options.direction === 'up' ? 'pump' : 'dump'} -> ${targetPrice} SOL`);
493
493
  const execute = async () => {
494
494
  executeCount++;
495
- // 刷新价格
495
+ // Refresh price
496
496
  context = await fetchTradeContext(options.token);
497
497
  const currentPrice = Number(context.priceInSol);
498
- // 检查是否达到目标
498
+ // Check if target reached
499
499
  if (options.direction === 'up' && currentPrice >= targetPrice) {
500
500
  output({
501
501
  success: true, direction: options.direction,
502
502
  targetPrice, currentPrice, totalCost, executeCount,
503
- message: '已达到目标价格',
503
+ message: 'Target price reached',
504
504
  });
505
505
  process.exit(0);
506
506
  }
@@ -508,40 +508,40 @@ export function registerToolsCommands(program) {
508
508
  output({
509
509
  success: true, direction: options.direction,
510
510
  targetPrice, currentPrice, totalCost, executeCount,
511
- message: '已达到目标价格',
511
+ message: 'Target price reached',
512
512
  });
513
513
  process.exit(0);
514
514
  }
515
- // 检查成本上限
515
+ // Check cost limit
516
516
  if (totalCost >= maxCost) {
517
517
  output({
518
518
  success: false, direction: options.direction,
519
519
  targetPrice, currentPrice, totalCost, executeCount,
520
- message: `已达到成本上限 ${maxCost} SOL`,
520
+ message: `Cost limit ${maxCost} SOL reached`,
521
521
  });
522
522
  process.exit(0);
523
523
  }
524
- // 计算交易金额
524
+ // Calculate trade amount
525
525
  let amount = Number(options.amount);
526
526
  if (options.amountType === 'random' && options.amountMax) {
527
527
  amount = Math.random() * (Number(options.amountMax) - amount) + amount;
528
528
  }
529
529
  const tradeType = options.direction === 'up' ? 'buy' : 'sell';
530
530
  const currentWallet = wallets[executeCount % wallets.length];
531
- // sell 方向: amount SOL 值,需要转换为等价代币数量
532
- // buy 方向: 直接传 SOL 金额
531
+ // sell direction: amount is SOL value, convert to equivalent token amount
532
+ // buy direction: pass SOL amount directly
533
533
  let amountForTrade;
534
534
  if (tradeType === 'sell') {
535
- // SOL -> 代币数量: amount(SOL) / pricePerToken(SOL)
535
+ // SOL -> token amount: amount(SOL) / pricePerToken(SOL)
536
536
  const tokenAmount = currentPrice > 0 ? amount / currentPrice : 0;
537
537
  amountForTrade = String(tokenAmount);
538
- info(`第 ${executeCount} ${tradeType}, 金额: ${amount.toFixed(6)} SOL ≈ ${tokenAmount.toFixed(2)} 代币, 当前价: ${currentPrice}`);
538
+ info(`Trade #${executeCount} ${tradeType}, amount: ${amount.toFixed(6)} SOL ≈ ${tokenAmount.toFixed(2)} tokens, current price: ${currentPrice}`);
539
539
  }
540
540
  else {
541
541
  amountForTrade = String(amount);
542
- info(`第 ${executeCount} ${tradeType}, 金额: ${amount.toFixed(6)} SOL, 当前价: ${currentPrice}`);
542
+ info(`Trade #${executeCount} ${tradeType}, amount: ${amount.toFixed(6)} SOL, current price: ${currentPrice}`);
543
543
  }
544
- // JSON 模式下抑制 SDK console.log
544
+ // Suppress SDK console.log in JSON mode
545
545
  if (getOutputFormat() === 'json')
546
546
  suppressConsole();
547
547
  try {
@@ -568,11 +568,11 @@ export function registerToolsCommands(program) {
568
568
  }
569
569
  catch (e) {
570
570
  restoreConsole();
571
- error(`执行出错: ${e.message}`);
571
+ error(`Execution error: ${e.message}`);
572
572
  }
573
573
  };
574
574
  await execute();
575
- // 持续执行 (daemon 模式)
575
+ // Continuous execution (daemon mode)
576
576
  const getInterval = () => {
577
577
  if (options.intervalType === 'random' && options.intervalMax) {
578
578
  const min = Number(options.interval);
@@ -590,13 +590,13 @@ export function registerToolsCommands(program) {
590
590
  scheduleNext();
591
591
  process.on('SIGINT', () => {
592
592
  restoreConsole();
593
- info(`价格机器人已停止,共执行 ${executeCount} 次,总消耗 ${totalCost.toFixed(6)} SOL`);
593
+ info(`Price robot stopped, executed ${executeCount} times, total cost ${totalCost.toFixed(6)} SOL`);
594
594
  process.exit(0);
595
595
  });
596
596
  }
597
597
  catch (e) {
598
598
  restoreConsole();
599
- error('价格机器人启动失败', e.message);
599
+ error('Price robot start failed', e.message);
600
600
  process.exit(1);
601
601
  }
602
602
  });