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,5 +1,5 @@
1
1
  /**
2
- * 钱包组管理命令组
2
+ * Wallet group management command group
3
3
  *
4
4
  * forgex wallet create-group | list-groups | group-info | delete-group |
5
5
  * generate | add | remove | import | export |
@@ -15,48 +15,48 @@ import { output, success, error, warn, getOutputFormat } from '../../output.js';
15
15
  export function registerWalletCommands(program) {
16
16
  const walletCmd = program
17
17
  .command('wallet')
18
- .description('钱包组管理');
18
+ .description('Wallet group management');
19
19
  // ============================================================
20
20
  // forgex wallet create-group
21
21
  // ============================================================
22
22
  walletCmd
23
23
  .command('create-group')
24
- .description('创建钱包组')
25
- .requiredOption('--name <name>', '钱包组名称')
26
- .option('--type <type>', '类型: local | monitor', 'local')
27
- .option('--remark <remark>', '备注', '')
28
- .option('--monitor-type <type>', '监控类型: normal | top100 | retail', 'normal')
29
- .option('--monitor-ca <ca>', '监控代币 CA')
30
- .option('--filter-group-ids <ids>', '过滤钱包组 ID(逗号分隔)')
24
+ .description('Create wallet group')
25
+ .requiredOption('--name <name>', 'Wallet group name')
26
+ .option('--type <type>', 'Type: local | monitor', 'local')
27
+ .option('--remark <remark>', 'Remark', '')
28
+ .option('--monitor-type <type>', 'Monitor type: normal | top100 | retail', 'normal')
29
+ .option('--monitor-ca <ca>', 'Monitor token CA')
30
+ .option('--filter-group-ids <ids>', 'Filter wallet group IDs (comma-separated)')
31
31
  .action(async (options) => {
32
32
  try {
33
- // 验证 --type 参数
33
+ // Validate --type parameter
34
34
  if (options.type !== 'local' && options.type !== 'monitor') {
35
- error(`无效的类型 "${options.type}",只支持 local monitor`);
35
+ error(`Invalid type "${options.type}", only local or monitor supported`);
36
36
  process.exit(1);
37
37
  }
38
38
  const groupType = options.type;
39
- // 验证 --monitor-type 参数
39
+ // Validate --monitor-type parameter
40
40
  const validMonitorTypes = ['normal', 'top100', 'retail'];
41
41
  if (!validMonitorTypes.includes(options.monitorType)) {
42
- error(`无效的监控类型 "${options.monitorType}",只支持 normaltop100 retail`);
42
+ error(`Invalid monitor type "${options.monitorType}", only normal, top100 or retail supported`);
43
43
  process.exit(1);
44
44
  }
45
45
  const monitorType = options.monitorType;
46
46
  const filterGroupIds = options.filterGroupIds
47
47
  ? options.filterGroupIds.split(',')
48
48
  : [];
49
- // 验证 filterGroupIds 中的值是否为有效数字
49
+ // Validate filterGroupIds values are valid numbers
50
50
  const filterGroupIdsNum = filterGroupIds.map(Number);
51
51
  if (filterGroupIdsNum.some(isNaN)) {
52
- error('--filter-group-ids 包含无效的 ID,请使用逗号分隔的数字');
52
+ error('--filter-group-ids contains invalid IDs, use comma-separated numbers');
53
53
  process.exit(1);
54
54
  }
55
- // 纯本地操作: 生成本地 group ID
55
+ // Local-only: generate local group ID
56
56
  const existingGroups = getAllGroups();
57
57
  const maxId = existingGroups.reduce((max, g) => Math.max(max, g.groupId), 0);
58
58
  const groupId = maxId + 1;
59
- // 本地存储
59
+ // Local storage
60
60
  const newGroup = {
61
61
  name: options.name,
62
62
  groupId,
@@ -76,7 +76,7 @@ export function registerWalletCommands(program) {
76
76
  });
77
77
  }
78
78
  catch (e) {
79
- error('创建钱包组失败', e.message);
79
+ error('Failed to create wallet group', e.message);
80
80
  process.exit(1);
81
81
  }
82
82
  });
@@ -85,13 +85,13 @@ export function registerWalletCommands(program) {
85
85
  // ============================================================
86
86
  walletCmd
87
87
  .command('list-groups')
88
- .description('列出所有钱包组')
89
- .option('--type <type>', '按类型过滤: local | monitor')
88
+ .description('List all wallet groups')
89
+ .option('--type <type>', 'Filter by type: local | monitor')
90
90
  .action((options) => {
91
91
  try {
92
- // 验证 --type 参数
92
+ // Validate --type parameter
93
93
  if (options.type && options.type !== 'local' && options.type !== 'monitor') {
94
- error(`无效的类型 "${options.type}",只支持 local monitor`);
94
+ error(`Invalid type "${options.type}", only local or monitor supported`);
95
95
  process.exit(1);
96
96
  }
97
97
  let groups = getAllGroups();
@@ -99,8 +99,8 @@ export function registerWalletCommands(program) {
99
99
  groups = groups.filter(g => g.groupType === options.type);
100
100
  }
101
101
  if (groups.length === 0) {
102
- const filterMsg = options.type ? `(类型: ${options.type})` : '';
103
- warn(`没有找到钱包组${filterMsg}`);
102
+ const filterMsg = options.type ? `(type: ${options.type})` : '';
103
+ warn(`No wallet groups found${filterMsg}`);
104
104
  }
105
105
  const result = groups.map(g => ({
106
106
  groupId: g.groupId,
@@ -113,16 +113,16 @@ export function registerWalletCommands(program) {
113
113
  output(result, {
114
114
  columns: [
115
115
  { key: 'groupId', header: 'ID' },
116
- { key: 'name', header: '名称' },
117
- { key: 'type', header: '类型' },
118
- { key: 'walletCount', header: '钱包数' },
119
- { key: 'monitorType', header: '监控类型' },
120
- { key: 'note', header: '备注' },
116
+ { key: 'name', header: 'Name' },
117
+ { key: 'type', header: 'Type' },
118
+ { key: 'walletCount', header: 'Wallet Count' },
119
+ { key: 'monitorType', header: 'Monitor Type' },
120
+ { key: 'note', header: 'Note' },
121
121
  ],
122
122
  });
123
123
  }
124
124
  catch (e) {
125
- error('获取钱包组列表失败', e.message);
125
+ error('Failed to list wallet groups', e.message);
126
126
  process.exit(1);
127
127
  }
128
128
  });
@@ -131,24 +131,24 @@ export function registerWalletCommands(program) {
131
131
  // ============================================================
132
132
  walletCmd
133
133
  .command('group-info')
134
- .description('查看钱包组详情')
135
- .requiredOption('--id <groupId>', '钱包组 ID')
136
- .option('--show-keys', '显示私钥(危险操作)', false)
134
+ .description('View wallet group details')
135
+ .requiredOption('--id <groupId>', 'Wallet group ID')
136
+ .option('--show-keys', 'Show private keys (dangerous)', false)
137
137
  .action(async (options) => {
138
138
  try {
139
139
  const groupId = Number(options.id);
140
140
  if (isNaN(groupId)) {
141
- error(`无效的钱包组 ID "${options.id}",请提供数字 ID`);
141
+ error(`Invalid wallet group ID "${options.id}", provide a numeric ID`);
142
142
  process.exit(1);
143
143
  }
144
- // 如果需要显示私钥,先确保密码已设置且正确
144
+ // If showing private keys, ensure password is set and correct first
145
145
  if (options.showKeys) {
146
- warn('警告:即将显示私钥明文,请确保当前环境安全,切勿在公共场所或共享屏幕时使用此命令');
146
+ warn('Warning: About to display plaintext private keys. Ensure a secure environment. Never use this command in public or screen-sharing sessions');
147
147
  await ensurePasswordAndValidate();
148
148
  }
149
149
  const group = getGroup(groupId);
150
150
  if (!group) {
151
- error(`钱包组 ${groupId} 不存在`);
151
+ error(`Wallet group ${groupId} does not exist`);
152
152
  process.exit(1);
153
153
  }
154
154
  const walletsList = group.wallets.map(w => {
@@ -173,7 +173,7 @@ export function registerWalletCommands(program) {
173
173
  output(result);
174
174
  }
175
175
  catch (e) {
176
- error('获取钱包组详情失败', e.message);
176
+ error('Failed to get wallet group details', e.message);
177
177
  process.exit(1);
178
178
  }
179
179
  });
@@ -182,40 +182,40 @@ export function registerWalletCommands(program) {
182
182
  // ============================================================
183
183
  walletCmd
184
184
  .command('delete-group')
185
- .description('删除钱包组')
186
- .requiredOption('--id <groupId>', '钱包组 ID')
187
- .option('--force', '跳过确认', false)
185
+ .description('Delete wallet group')
186
+ .requiredOption('--id <groupId>', 'Wallet group ID')
187
+ .option('--force', 'Skip confirmation', false)
188
188
  .action(async (options) => {
189
189
  try {
190
190
  const groupId = Number(options.id);
191
191
  if (isNaN(groupId)) {
192
- error(`无效的钱包组 ID "${options.id}",请提供数字 ID`);
192
+ error(`Invalid wallet group ID "${options.id}", provide a numeric ID`);
193
193
  process.exit(1);
194
194
  }
195
- // 检查本地是否存在该钱包组
195
+ // Check if wallet group exists locally
196
196
  const group = getGroup(groupId);
197
197
  if (!group) {
198
- error(`钱包组 ${groupId} 不存在`);
198
+ error(`Wallet group ${groupId} does not exist`);
199
199
  process.exit(1);
200
200
  }
201
- // --force 模式下,提示用户确认
201
+ // In non --force mode, prompt user for confirmation
202
202
  if (!options.force) {
203
203
  const { confirm } = await import('@inquirer/prompts');
204
204
  const confirmed = await confirm({
205
- message: `确认删除钱包组 "${group.name}" (ID: ${groupId}, ${group.wallets.length} 个钱包)?此操作不可恢复。`,
205
+ message: `Confirm delete wallet group "${group.name}" (ID: ${groupId}, ${group.wallets.length} wallets)? This action is irreversible.`,
206
206
  default: false,
207
207
  });
208
208
  if (!confirmed) {
209
- warn('已取消删除操作');
209
+ warn('Delete operation cancelled');
210
210
  return;
211
211
  }
212
212
  }
213
- // 纯本地删除
213
+ // Pure local deletion
214
214
  removeGroup(groupId);
215
- output({ success: true, groupId, message: '钱包组已删除' });
215
+ output({ success: true, groupId, message: 'Wallet group deleted' });
216
216
  }
217
217
  catch (e) {
218
- error('删除钱包组失败', e.message);
218
+ error('Failed to delete wallet group', e.message);
219
219
  process.exit(1);
220
220
  }
221
221
  });
@@ -224,40 +224,40 @@ export function registerWalletCommands(program) {
224
224
  // ============================================================
225
225
  walletCmd
226
226
  .command('generate')
227
- .description('生成新钱包')
228
- .requiredOption('--group <groupId>', '目标钱包组 ID')
229
- .option('--count <n>', '生成数量', '1')
227
+ .description('Generate new wallets')
228
+ .requiredOption('--group <groupId>', 'Target wallet group ID')
229
+ .option('--count <n>', 'Count to generate', '1')
230
230
  .action(async (options) => {
231
231
  try {
232
232
  await ensurePasswordAndValidate();
233
233
  const groupId = Number(options.group);
234
234
  if (isNaN(groupId)) {
235
- error(`无效的钱包组 ID "${options.group}",请提供数字 ID`);
235
+ error(`Invalid wallet group ID "${options.group}", provide a numeric ID`);
236
236
  process.exit(1);
237
237
  }
238
238
  const count = Number(options.count);
239
239
  if (isNaN(count) || !Number.isInteger(count) || count <= 0) {
240
- error(`无效的生成数量 "${options.count}",请提供正整数`);
240
+ error(`Invalid count "${options.count}", provide a positive integer`);
241
241
  process.exit(1);
242
242
  }
243
243
  const group = getGroup(groupId);
244
244
  if (!group) {
245
- error(`钱包组 ${groupId} 不存在`);
245
+ error(`Wallet group ${groupId} does not exist`);
246
246
  process.exit(1);
247
247
  }
248
248
  if (group.wallets.length + count > 100) {
249
- error(`钱包数量超限:当前 ${group.wallets.length} 个,最多再生成 ${100 - group.wallets.length} 个(上限 100)`);
249
+ error(`Wallet count exceeded: currently ${group.wallets.length}, max ${100 - group.wallets.length} more (limit 100)`);
250
250
  process.exit(1);
251
251
  }
252
252
  const wallets = generateWallets(count);
253
- // 保存明文私钥用于输出(addWalletsToGroup 可能会加密 wallets 中的 privateKey)
253
+ // Save plaintext private keys for output (addWalletsToGroup may encrypt privateKey in wallets)
254
254
  const plaintextKeys = wallets.map(w => ({
255
255
  address: w.walletAddress,
256
256
  privateKey: w.privateKey,
257
257
  }));
258
- // 纯本地存储(如果存储已加密,此函数会加密 wallets privateKey 字段)
258
+ // Pure local storage (if store is encrypted, this function encrypts privateKey fields)
259
259
  addWalletsToGroup(groupId, wallets);
260
- // Bug 1 fix: JSON 保持原结构; table/minimal columns 展示生成的钱包列表
260
+ // Bug 1 fix: JSON keeps original structure; table/minimal use columns to display generated wallet list
261
261
  const fmt = getOutputFormat();
262
262
  if (fmt === 'json') {
263
263
  output({
@@ -267,7 +267,7 @@ export function registerWalletCommands(program) {
267
267
  });
268
268
  }
269
269
  else {
270
- success(`已生成 ${plaintextKeys.length} 个钱包到组 ${groupId}`);
270
+ success(`Generated ${plaintextKeys.length} wallets to group ${groupId}`);
271
271
  output(plaintextKeys.map((w, i) => ({
272
272
  index: i + 1,
273
273
  address: w.address,
@@ -275,14 +275,14 @@ export function registerWalletCommands(program) {
275
275
  })), {
276
276
  columns: [
277
277
  { key: 'index', header: '#' },
278
- { key: 'address', header: '地址' },
279
- { key: 'privateKey', header: '私钥' },
278
+ { key: 'address', header: 'Address' },
279
+ { key: 'privateKey', header: 'Private Key' },
280
280
  ],
281
281
  });
282
282
  }
283
283
  }
284
284
  catch (e) {
285
- error('生成钱包失败', e.message);
285
+ error('Failed to generate wallets', e.message);
286
286
  process.exit(1);
287
287
  }
288
288
  });
@@ -291,37 +291,37 @@ export function registerWalletCommands(program) {
291
291
  // ============================================================
292
292
  walletCmd
293
293
  .command('add')
294
- .description('添加钱包到钱包组')
295
- .requiredOption('--group <groupId>', '目标钱包组 ID')
296
- .requiredOption('--private-key <key>', '私钥(Base58 编码)')
297
- .option('--note <note>', '备注', '')
294
+ .description('Add wallet to group')
295
+ .requiredOption('--group <groupId>', 'Target wallet group ID')
296
+ .requiredOption('--private-key <key>', 'Private key (Base58 encoded)')
297
+ .option('--note <note>', 'Note', '')
298
298
  .action(async (options) => {
299
299
  try {
300
300
  await ensurePasswordAndValidate();
301
301
  const groupId = Number(options.group);
302
302
  if (isNaN(groupId)) {
303
- error(`无效的钱包组 ID "${options.group}",请提供数字 ID`);
303
+ error(`Invalid wallet group ID "${options.group}", provide a numeric ID`);
304
304
  process.exit(1);
305
305
  }
306
- // 验证钱包组存在
306
+ // Verify wallet group exists
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
+ // Validate private key validity
313
313
  let wallet;
314
314
  try {
315
315
  wallet = walletFromPrivateKey(options.privateKey, options.note);
316
316
  }
317
317
  catch {
318
- error('无效的私钥:请提供有效的 Base58 编码私钥');
318
+ error('Invalid private key: provide a valid Base58 encoded private key');
319
319
  process.exit(1);
320
320
  }
321
- // 检查地址是否已存在于该组
321
+ // Check if address already exists in group
322
322
  const exists = group.wallets.some(w => w.walletAddress === wallet.walletAddress);
323
323
  if (exists) {
324
- warn(`钱包 ${wallet.walletAddress} 已存在于钱包组 ${groupId} 中,跳过重复添加`);
324
+ warn(`Wallet ${wallet.walletAddress} already exists in group ${groupId}, skipping duplicate`);
325
325
  output({
326
326
  success: true,
327
327
  groupId,
@@ -330,7 +330,7 @@ export function registerWalletCommands(program) {
330
330
  });
331
331
  return;
332
332
  }
333
- // 纯本地存储
333
+ // Pure local storage
334
334
  addWalletsToGroup(groupId, [wallet]);
335
335
  output({
336
336
  success: true,
@@ -339,7 +339,7 @@ export function registerWalletCommands(program) {
339
339
  });
340
340
  }
341
341
  catch (e) {
342
- error('添加钱包失败', e.message);
342
+ error('Failed to add wallet', e.message);
343
343
  process.exit(1);
344
344
  }
345
345
  });
@@ -348,29 +348,29 @@ export function registerWalletCommands(program) {
348
348
  // ============================================================
349
349
  walletCmd
350
350
  .command('remove')
351
- .description('从钱包组移除钱包')
352
- .requiredOption('--group <groupId>', '目标钱包组 ID')
353
- .requiredOption('--address <addr>', '钱包地址')
351
+ .description('Remove wallet from group')
352
+ .requiredOption('--group <groupId>', 'Target wallet group ID')
353
+ .requiredOption('--address <addr>', 'Wallet address')
354
354
  .action(async (options) => {
355
355
  try {
356
356
  const groupId = Number(options.group);
357
357
  if (isNaN(groupId)) {
358
- error(`无效的钱包组 ID "${options.group}",请提供数字 ID`);
358
+ error(`Invalid wallet group ID "${options.group}", provide a numeric ID`);
359
359
  process.exit(1);
360
360
  }
361
- // 验证钱包组存在
361
+ // Verify wallet group exists
362
362
  const group = getGroup(groupId);
363
363
  if (!group) {
364
- error(`钱包组 ${groupId} 不存在`);
364
+ error(`Wallet group ${groupId} does not exist`);
365
365
  process.exit(1);
366
366
  }
367
- // 验证地址是否存在于该钱包组中
367
+ // Verify address exists in wallet group
368
368
  const walletExists = group.wallets.some(w => w.walletAddress === options.address);
369
369
  if (!walletExists) {
370
- error(`钱包 ${options.address} 不在钱包组 ${groupId} 中`);
370
+ error(`Wallet ${options.address} is not in wallet group ${groupId}`);
371
371
  process.exit(1);
372
372
  }
373
- // 纯本地移除
373
+ // Pure local removal
374
374
  removeWalletsFromGroup(groupId, [options.address]);
375
375
  output({
376
376
  success: true,
@@ -379,7 +379,7 @@ export function registerWalletCommands(program) {
379
379
  });
380
380
  }
381
381
  catch (e) {
382
- error('移除钱包失败', e.message);
382
+ error('Failed to remove wallet', e.message);
383
383
  process.exit(1);
384
384
  }
385
385
  });
@@ -388,35 +388,35 @@ export function registerWalletCommands(program) {
388
388
  // ============================================================
389
389
  walletCmd
390
390
  .command('import')
391
- .description(' CSV 文件导入钱包')
392
- .requiredOption('--group <groupId>', '目标钱包组 ID')
393
- .requiredOption('--file <csvPath>', 'CSV 文件路径')
391
+ .description('Import wallets from CSV file')
392
+ .requiredOption('--group <groupId>', 'Target wallet group ID')
393
+ .requiredOption('--file <csvPath>', 'CSV file path')
394
394
  .action(async (options) => {
395
395
  try {
396
396
  await ensurePasswordAndValidate();
397
397
  const groupId = Number(options.group);
398
398
  if (isNaN(groupId) || !Number.isInteger(groupId) || groupId <= 0) {
399
- error(`无效的钱包组 ID "${options.group}",请提供正整数`);
399
+ error(`Invalid wallet group ID "${options.group}", provide a positive integer`);
400
400
  process.exit(1);
401
401
  }
402
- // 验证钱包组存在
402
+ // Verify wallet group exists
403
403
  const group = getGroup(groupId);
404
404
  if (!group) {
405
- error(`钱包组 ${groupId} 不存在`);
405
+ error(`Wallet group ${groupId} does not exist`);
406
406
  process.exit(1);
407
407
  }
408
- // 验证文件存在
408
+ // Verify file exists
409
409
  if (!fs.existsSync(options.file)) {
410
- error(`CSV 文件不存在: ${options.file}`);
410
+ error(`CSV file does not exist: ${options.file}`);
411
411
  process.exit(1);
412
412
  }
413
413
  const csvContent = fs.readFileSync(options.file, 'utf-8');
414
414
  const wallets = importWalletsFromCsv(csvContent);
415
415
  if (wallets.length === 0) {
416
- error('CSV 文件中没有有效的钱包数据');
416
+ error('No valid wallet data in CSV file');
417
417
  process.exit(1);
418
418
  }
419
- // 纯本地存储
419
+ // Pure local storage
420
420
  addWalletsToGroup(groupId, wallets);
421
421
  output({
422
422
  success: true,
@@ -426,7 +426,7 @@ export function registerWalletCommands(program) {
426
426
  });
427
427
  }
428
428
  catch (e) {
429
- error('导入钱包失败', e.message);
429
+ error('Failed to import wallets', e.message);
430
430
  process.exit(1);
431
431
  }
432
432
  });
@@ -435,20 +435,20 @@ export function registerWalletCommands(program) {
435
435
  // ============================================================
436
436
  walletCmd
437
437
  .command('export')
438
- .description('导出钱包组为 CSV 文件')
439
- .requiredOption('--group <groupId>', '钱包组 ID')
440
- .requiredOption('--file <csvPath>', '输出文件路径')
438
+ .description('Export wallet group as CSV file')
439
+ .requiredOption('--group <groupId>', 'Wallet group ID')
440
+ .requiredOption('--file <csvPath>', 'Output file path')
441
441
  .action(async (options) => {
442
442
  try {
443
443
  await ensurePasswordAndValidate();
444
444
  const groupId = Number(options.group);
445
445
  if (isNaN(groupId) || !Number.isInteger(groupId) || groupId <= 0) {
446
- error(`无效的钱包组 ID "${options.group}",请提供正整数`);
446
+ error(`Invalid wallet group ID "${options.group}", provide a positive integer`);
447
447
  process.exit(1);
448
448
  }
449
449
  const csv = exportGroupToCsv(groupId);
450
450
  if (!csv) {
451
- error(`钱包组 ${groupId} 不存在`);
451
+ error(`Wallet group ${groupId} does not exist`);
452
452
  process.exit(1);
453
453
  }
454
454
  fs.writeFileSync(options.file, csv, { encoding: 'utf-8', mode: 0o600 });
@@ -459,7 +459,7 @@ export function registerWalletCommands(program) {
459
459
  });
460
460
  }
461
461
  catch (e) {
462
- error('导出钱包失败', e.message);
462
+ error('Failed to export wallets', e.message);
463
463
  process.exit(1);
464
464
  }
465
465
  });
@@ -468,33 +468,33 @@ export function registerWalletCommands(program) {
468
468
  // ============================================================
469
469
  walletCmd
470
470
  .command('import-group')
471
- .description(' JSON 文件导入钱包组')
472
- .requiredOption('--file <jsonPath>', 'JSON 文件路径')
473
- .option('--password <pwd>', '解密密码(加密文件需要)')
471
+ .description('Import wallet groups from JSON file')
472
+ .requiredOption('--file <jsonPath>', 'JSON file path')
473
+ .option('--password <pwd>', 'Decryption password (required for encrypted files)')
474
474
  .action(async (options) => {
475
475
  try {
476
476
  await ensurePasswordAndValidate();
477
- // 验证文件存在
477
+ // Verify file exists
478
478
  if (!fs.existsSync(options.file)) {
479
- error(`JSON 文件不存在: ${options.file}`);
479
+ error(`JSON file does not exist: ${options.file}`);
480
480
  process.exit(1);
481
481
  }
482
482
  const content = fs.readFileSync(options.file, 'utf-8');
483
483
  const groups = importGroupsFromJson(content, options.password);
484
484
  if (!groups || groups.length === 0) {
485
- error('JSON 文件中没有有效的钱包组数据(如果是加密文件,请检查 --password 是否正确)');
485
+ error('No valid wallet group data in JSON file (if encrypted, check --password)');
486
486
  process.exit(1);
487
487
  }
488
- // 纯本地存储(通过 saveGroup + addWalletsToGroup 确保私钥正确加密)
488
+ // Pure local storage (ensure private keys are encrypted via saveGroup + addWalletsToGroup)
489
489
  const existingGroups = getAllGroups();
490
490
  let nextLocalId = existingGroups.reduce((max, g) => Math.max(max, g.groupId), 0) + 1;
491
491
  groups.forEach((g) => {
492
492
  const newId = nextLocalId++;
493
493
  const wallets = g.wallets;
494
494
  g.groupId = newId;
495
- g.wallets = []; // 先保存空钱包组
495
+ g.wallets = []; // Save empty wallet group first
496
496
  saveGroup(g);
497
- // 通过 addWalletsToGroup 添加钱包,该函数会自动处理加密
497
+ // Add wallets via addWalletsToGroup, which handles encryption automatically
498
498
  if (wallets.length > 0) {
499
499
  addWalletsToGroup(newId, wallets);
500
500
  }
@@ -506,7 +506,7 @@ export function registerWalletCommands(program) {
506
506
  });
507
507
  }
508
508
  catch (e) {
509
- error('导入钱包组失败', e.message);
509
+ error('Failed to import wallet groups', e.message);
510
510
  process.exit(1);
511
511
  }
512
512
  });
@@ -515,16 +515,16 @@ export function registerWalletCommands(program) {
515
515
  // ============================================================
516
516
  walletCmd
517
517
  .command('export-group')
518
- .description('导出所有钱包组为 JSON 文件')
519
- .requiredOption('--file <jsonPath>', '输出文件路径')
520
- .option('--encrypt', '加密导出', false)
521
- .option('--password <pwd>', '加密密码')
518
+ .description('Export all wallet groups as JSON file')
519
+ .requiredOption('--file <jsonPath>', 'Output file path')
520
+ .option('--encrypt', 'Encrypted export', false)
521
+ .option('--password <pwd>', 'Encryption password')
522
522
  .action(async (options) => {
523
523
  try {
524
524
  await ensurePasswordAndValidate();
525
525
  const groups = getAllGroups();
526
526
  if (groups.length === 0) {
527
- warn('没有钱包组可导出');
527
+ warn('No wallet groups to export');
528
528
  output({ success: true, file: options.file, encrypted: options.encrypt, groupCount: 0 });
529
529
  return;
530
530
  }
@@ -532,7 +532,7 @@ export function registerWalletCommands(program) {
532
532
  if (options.encrypt) {
533
533
  const password = options.password || process.env.FORGEX_PASSWORD;
534
534
  if (!password) {
535
- error('加密导出需要密码,使用 --password 或设置 FORGEX_PASSWORD 环境变量');
535
+ error('Encrypted export requires password, use --password or set FORGEX_PASSWORD env var');
536
536
  process.exit(1);
537
537
  }
538
538
  content = exportEncryptedGroupsJson(password);
@@ -549,7 +549,7 @@ export function registerWalletCommands(program) {
549
549
  });
550
550
  }
551
551
  catch (e) {
552
- error('导出钱包组失败', e.message);
552
+ error('Failed to export wallet groups', e.message);
553
553
  process.exit(1);
554
554
  }
555
555
  });
@@ -558,30 +558,30 @@ export function registerWalletCommands(program) {
558
558
  // ============================================================
559
559
  walletCmd
560
560
  .command('overview')
561
- .description('查看钱包组概览')
562
- .requiredOption('--groups <ids>', '钱包组 ID(逗号分隔)')
563
- .option('--token <ca>', '按代币过滤')
561
+ .description('View wallet group overview')
562
+ .requiredOption('--groups <ids>', 'Wallet group IDs (comma-separated)')
563
+ .option('--token <ca>', 'Filter by token')
564
564
  .action(async (options) => {
565
565
  try {
566
566
  const groupIds = options.groups.split(',').map(Number);
567
- // 验证所有 groupId 都是有效正整数
567
+ // Validate all groupIds are valid positive integers
568
568
  if (groupIds.some(id => isNaN(id) || !Number.isInteger(id) || id <= 0)) {
569
- error(`无效的钱包组 ID 列表 "${options.groups}",请提供逗号分隔的正整数`);
569
+ error(`Invalid wallet group ID list "${options.groups}", provide comma-separated positive integers`);
570
570
  process.exit(1);
571
571
  }
572
- // 验证本地是否存在这些钱包组
572
+ // Verify wallet groups exist locally
573
573
  const missingIds = groupIds.filter(id => !getGroup(id));
574
574
  if (missingIds.length > 0) {
575
- warn(`以下钱包组在本地不存在: ${missingIds.join(', ')}`);
575
+ warn(`The following wallet groups do not exist locally: ${missingIds.join(', ')}`);
576
576
  }
577
577
  const ds = getDataSource();
578
- // DataStore 本地数据构建概览
578
+ // Build overview from DataStore local data
579
579
  const overviewResults = [];
580
580
  for (const gid of groupIds) {
581
581
  const group = getGroup(gid);
582
582
  if (!group)
583
583
  continue;
584
- // 确定要查询的代币列表
584
+ // Determine token list to query
585
585
  const tokenCAs = options.token ? [options.token] : ds.listTokens();
586
586
  let totalValueSol = 0;
587
587
  let totalRealizedPnl = 0;
@@ -592,14 +592,14 @@ export function registerWalletCommands(program) {
592
592
  const holdings = ds.getHoldings(ca, gid);
593
593
  if (!holdings || holdings.wallets.length === 0)
594
594
  continue;
595
- // 尝试获取代币价格
595
+ // Try to get token price
596
596
  let priceSol = 0;
597
597
  try {
598
598
  const priceData = await ds.getTokenPrice(ca);
599
599
  priceSol = priceData.priceSol;
600
600
  }
601
601
  catch {
602
- // 价格不可用,跳过价值计算
602
+ // Price unavailable, skip value calculation
603
603
  }
604
604
  for (const w of holdings.wallets) {
605
605
  const positionValue = w.tokenBalance * priceSol;
@@ -623,22 +623,22 @@ export function registerWalletCommands(program) {
623
623
  });
624
624
  }
625
625
  if (overviewResults.length === 0) {
626
- warn('暂无钱包组概览数据');
626
+ warn('No wallet group overview data');
627
627
  }
628
628
  output(overviewResults, {
629
629
  columns: [
630
630
  { key: 'groupId', header: 'ID' },
631
- { key: 'groupName', header: '名称' },
632
- { key: 'totalValue', header: '总价值(SOL)' },
633
- { key: 'pnl', header: '总盈亏(SOL)' },
634
- { key: 'donePnl', header: '已实现' },
635
- { key: 'unDonePnl', header: '未实现' },
636
- { key: 'avgCost', header: '均价' },
631
+ { key: 'groupName', header: 'Name' },
632
+ { key: 'totalValue', header: 'Total Value(SOL)' },
633
+ { key: 'pnl', header: 'Total P&L(SOL)' },
634
+ { key: 'donePnl', header: 'Realized' },
635
+ { key: 'unDonePnl', header: 'Unrealized' },
636
+ { key: 'avgCost', header: 'Avg Cost' },
637
637
  ],
638
638
  });
639
639
  }
640
640
  catch (e) {
641
- error('获取钱包组概览失败', e.message);
641
+ error('Failed to get wallet group overview', e.message);
642
642
  process.exit(1);
643
643
  }
644
644
  });
@@ -647,43 +647,43 @@ export function registerWalletCommands(program) {
647
647
  // ============================================================
648
648
  walletCmd
649
649
  .command('grind')
650
- .description('生成靓号地址(自定义后缀/前缀)')
651
- .requiredOption('--suffix <suffix>', '地址后缀(如 pump')
652
- .option('--prefix <prefix>', '地址前缀')
653
- .option('--count <n>', '生成数量', '1')
654
- .option('--threads <n>', 'grind 线程数')
650
+ .description('Generate vanity address (custom suffix/prefix)')
651
+ .requiredOption('--suffix <suffix>', 'Address suffix (e.g. pump)')
652
+ .option('--prefix <prefix>', 'Address prefix')
653
+ .option('--count <n>', 'Count to generate', '1')
654
+ .option('--threads <n>', 'Grind thread count')
655
655
  .action(async (options) => {
656
656
  try {
657
- // 检查 solana-keygen 是否可用
657
+ // Check if solana-keygen is available
658
658
  try {
659
659
  execSync('solana-keygen --version', { stdio: 'ignore' });
660
660
  }
661
661
  catch {
662
- error('未检测到 solana-keygen,请先安装 Solana CLI 工具链', '安装方式: sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)" 或访问 https://docs.solanalabs.com/cli/install');
662
+ error('solana-keygen not found, please install Solana CLI toolchain first', 'Install: sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)" or visit https://docs.solanalabs.com/cli/install');
663
663
  process.exit(1);
664
664
  }
665
665
  const count = Number(options.count);
666
666
  if (isNaN(count) || !Number.isInteger(count) || count <= 0) {
667
- error(`无效的生成数量 "${options.count}",请提供正整数`);
667
+ error(`Invalid count "${options.count}", provide a positive integer`);
668
668
  process.exit(1);
669
669
  }
670
670
  const suffix = options.suffix.trim();
671
671
  if (!suffix) {
672
- error('--suffix 不能为空');
672
+ error('--suffix cannot be empty');
673
673
  process.exit(1);
674
674
  }
675
- // 验证后缀只包含 Base58 字符
675
+ // Verify suffix contains only Base58 characters
676
676
  const base58Chars = /^[1-9A-HJ-NP-Za-km-z]+$/;
677
677
  if (!base58Chars.test(suffix)) {
678
- error(`无效的后缀 "${suffix}",只支持 Base58 字符(不含 0, O, I, l)`);
678
+ error(`Invalid suffix "${suffix}", only Base58 characters supported (no 0, O, I, l)`);
679
679
  process.exit(1);
680
680
  }
681
681
  if (options.prefix && !base58Chars.test(options.prefix)) {
682
- error(`无效的前缀 "${options.prefix}",只支持 Base58 字符(不含 0, O, I, l)`);
682
+ error(`Invalid prefix "${options.prefix}", only Base58 characters supported (no 0, O, I, l)`);
683
683
  process.exit(1);
684
684
  }
685
685
  ensureConfigDir();
686
- // 构建 solana-keygen grind 参数
686
+ // Build solana-keygen grind arguments
687
687
  const args = ['grind'];
688
688
  if (options.prefix) {
689
689
  args.push('--starts-and-ends-with', `${options.prefix}:${suffix}`);
@@ -694,19 +694,19 @@ export function registerWalletCommands(program) {
694
694
  if (options.threads) {
695
695
  const threads = Number(options.threads);
696
696
  if (isNaN(threads) || !Number.isInteger(threads) || threads <= 0) {
697
- error(`无效的线程数 "${options.threads}",请提供正整数`);
697
+ error(`Invalid thread count "${options.threads}", provide a positive integer`);
698
698
  process.exit(1);
699
699
  }
700
700
  args.push('--num-threads', String(threads));
701
701
  }
702
702
  const fmt = getOutputFormat();
703
703
  if (fmt !== 'json') {
704
- success(`正在生成 ${count} 个以 "${suffix}" 结尾的靓号地址,请耐心等待...`);
704
+ success(`Generating ${count} vanity addresses ending with "${suffix}", please wait...`);
705
705
  if (suffix.length >= 5) {
706
- warn('后缀较长,生成时间可能需要数分钟甚至更久');
706
+ warn('Long suffix, generation may take minutes or longer');
707
707
  }
708
708
  }
709
- // VANITY_DIR 下执行 grind,生成的 .json 文件会落在该目录
709
+ // Execute grind in VANITY_DIR, generated .json files will be in that directory
710
710
  const child = spawn('solana-keygen', args, {
711
711
  cwd: VANITY_DIR,
712
712
  stdio: ['ignore', 'pipe', 'pipe'],
@@ -719,10 +719,10 @@ export function registerWalletCommands(program) {
719
719
  child.on('close', resolve);
720
720
  });
721
721
  if (exitCode !== 0) {
722
- error('solana-keygen grind 执行失败', stderr || stdout);
722
+ error('solana-keygen grind execution failed', stderr || stdout);
723
723
  process.exit(1);
724
724
  }
725
- // 解析生成结果,solana-keygen 输出格式: "Wrote keypair to <address>.json"
725
+ // Parse generation results, solana-keygen output format: "Wrote keypair to <address>.json"
726
726
  const generatedFiles = [];
727
727
  const lines = stdout.split('\n');
728
728
  for (const line of lines) {
@@ -734,12 +734,12 @@ export function registerWalletCommands(program) {
734
734
  generatedFiles.push({ address, path: filePath });
735
735
  }
736
736
  }
737
- // --starts-and-ends-with 模式下需要手动控制 count
737
+ // In --starts-and-ends-with mode, need to manually control count
738
738
  if (options.prefix && generatedFiles.length > count) {
739
739
  generatedFiles.splice(count);
740
740
  }
741
741
  if (generatedFiles.length === 0) {
742
- warn('未能解析到生成的密钥文件');
742
+ warn('Unable to parse generated key files');
743
743
  process.exit(1);
744
744
  }
745
745
  if (fmt === 'json') {
@@ -753,7 +753,7 @@ export function registerWalletCommands(program) {
753
753
  });
754
754
  }
755
755
  else {
756
- success(`成功生成 ${generatedFiles.length} 个靓号地址,已保存到 ${VANITY_DIR}`);
756
+ success(`Successfully generated ${generatedFiles.length} vanity addresses, saved to ${VANITY_DIR}`);
757
757
  output(generatedFiles.map((f, i) => ({
758
758
  index: i + 1,
759
759
  address: f.address,
@@ -761,14 +761,14 @@ export function registerWalletCommands(program) {
761
761
  })), {
762
762
  columns: [
763
763
  { key: 'index', header: '#' },
764
- { key: 'address', header: '地址' },
765
- { key: 'path', header: '文件路径' },
764
+ { key: 'address', header: 'Address' },
765
+ { key: 'path', header: 'File Path' },
766
766
  ],
767
767
  });
768
768
  }
769
769
  }
770
770
  catch (e) {
771
- error('生成靓号地址失败', e.message);
771
+ error('Failed to generate vanity addresses', e.message);
772
772
  process.exit(1);
773
773
  }
774
774
  });
@@ -777,19 +777,19 @@ export function registerWalletCommands(program) {
777
777
  // ============================================================
778
778
  walletCmd
779
779
  .command('grind-list')
780
- .description('列出已生成的靓号地址')
781
- .option('--suffix <suffix>', '按后缀过滤')
780
+ .description('List generated vanity addresses')
781
+ .option('--suffix <suffix>', 'Filter by suffix')
782
782
  .action((options) => {
783
783
  try {
784
784
  ensureConfigDir();
785
785
  if (!fs.existsSync(VANITY_DIR)) {
786
- warn('暂无靓号地址,请先运行 forgex wallet grind 生成');
786
+ warn('No vanity addresses yet, run forgex wallet grind first');
787
787
  output([]);
788
788
  return;
789
789
  }
790
790
  const files = fs.readdirSync(VANITY_DIR).filter(f => f.endsWith('.json'));
791
791
  if (files.length === 0) {
792
- warn('暂无靓号地址,请先运行 forgex wallet grind 生成');
792
+ warn('No vanity addresses yet, run forgex wallet grind first');
793
793
  output([]);
794
794
  return;
795
795
  }
@@ -808,19 +808,19 @@ export function registerWalletCommands(program) {
808
808
  results = results.filter(r => r.address.toLowerCase().endsWith(suffixLower));
809
809
  }
810
810
  if (results.length === 0) {
811
- const filterMsg = options.suffix ? `(后缀: ${options.suffix})` : '';
812
- warn(`没有找到匹配的靓号地址${filterMsg}`);
811
+ const filterMsg = options.suffix ? `(suffix: ${options.suffix})` : '';
812
+ warn(`No matching vanity addresses found${filterMsg}`);
813
813
  }
814
814
  output(results, {
815
815
  columns: [
816
- { key: 'address', header: '地址' },
817
- { key: 'createdAt', header: '创建时间' },
818
- { key: 'path', header: '文件路径' },
816
+ { key: 'address', header: 'Address' },
817
+ { key: 'createdAt', header: 'Created At' },
818
+ { key: 'path', header: 'File Path' },
819
819
  ],
820
820
  });
821
821
  }
822
822
  catch (e) {
823
- error('获取靓号地址列表失败', e.message);
823
+ error('Failed to list vanity addresses', e.message);
824
824
  process.exit(1);
825
825
  }
826
826
  });