httpcat-cli 0.3.0 → 0.3.1

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 (322) hide show
  1. package/.github/workflows/ci.yml +3 -0
  2. package/.github/workflows/rc-publish.yml +6 -0
  3. package/.github/workflows/release.yml +102 -0
  4. package/.github/workflows/sync-version.yml +31 -2
  5. package/README.md +1408 -109
  6. package/additions.txt +3 -0
  7. package/bun.lock +260 -25
  8. package/dist/agent/autonomous-trader.d.ts.map +1 -0
  9. package/dist/agent/autonomous-trader.js +362 -0
  10. package/dist/agent/autonomous-trader.js.map +1 -0
  11. package/dist/agent/ax-agent.d.ts.map +1 -1
  12. package/dist/agent/ax-agent.js +356 -18
  13. package/dist/agent/ax-agent.js.map +1 -1
  14. package/dist/agent/event-client.d.ts.map +1 -0
  15. package/dist/agent/event-client.js +82 -0
  16. package/dist/agent/event-client.js.map +1 -0
  17. package/dist/agent/log-stream.d.ts.map +1 -0
  18. package/dist/agent/log-stream.js +95 -0
  19. package/dist/agent/log-stream.js.map +1 -0
  20. package/dist/agent/memory/conversation-session.d.ts.map +1 -0
  21. package/dist/agent/memory/conversation-session.js +232 -0
  22. package/dist/agent/memory/conversation-session.js.map +1 -0
  23. package/dist/agent/memory/conversation-store.d.ts.map +1 -0
  24. package/dist/agent/memory/conversation-store.js +214 -0
  25. package/dist/agent/memory/conversation-store.js.map +1 -0
  26. package/dist/agent/memory/database-schema.d.ts.map +1 -0
  27. package/dist/agent/memory/database-schema.js +355 -0
  28. package/dist/agent/memory/database-schema.js.map +1 -0
  29. package/dist/agent/memory/decision-tracker.d.ts.map +1 -0
  30. package/dist/agent/memory/decision-tracker.js +274 -0
  31. package/dist/agent/memory/decision-tracker.js.map +1 -0
  32. package/dist/agent/memory/memory-manager.d.ts.map +1 -0
  33. package/dist/agent/memory/memory-manager.js +187 -0
  34. package/dist/agent/memory/memory-manager.js.map +1 -0
  35. package/dist/agent/memory/types.d.ts.map +1 -0
  36. package/dist/agent/memory/types.js +5 -0
  37. package/dist/agent/memory/types.js.map +1 -0
  38. package/dist/agent/message-formatter.d.ts.map +1 -0
  39. package/dist/agent/message-formatter.js +76 -0
  40. package/dist/agent/message-formatter.js.map +1 -0
  41. package/dist/agent/position-db.d.ts.map +1 -0
  42. package/dist/agent/position-db.js +154 -0
  43. package/dist/agent/position-db.js.map +1 -0
  44. package/dist/agent/simple-chat-ui-static.d.ts.map +1 -0
  45. package/dist/agent/simple-chat-ui-static.js +129 -0
  46. package/dist/agent/simple-chat-ui-static.js.map +1 -0
  47. package/dist/agent/simple-chat-ui.d.ts.map +1 -0
  48. package/dist/agent/simple-chat-ui.js +90 -0
  49. package/dist/agent/simple-chat-ui.js.map +1 -0
  50. package/dist/agent/tools.d.ts.map +1 -1
  51. package/dist/agent/tools.js +297 -4
  52. package/dist/agent/tools.js.map +1 -1
  53. package/dist/agent/ui.d.ts.map +1 -0
  54. package/dist/agent/ui.js +84 -0
  55. package/dist/agent/ui.js.map +1 -0
  56. package/dist/agent/unified-runtime.d.ts.map +1 -0
  57. package/dist/agent/unified-runtime.js +397 -0
  58. package/dist/agent/unified-runtime.js.map +1 -0
  59. package/dist/client.d.ts.map +1 -1
  60. package/dist/client.js +272 -21
  61. package/dist/client.js.map +1 -1
  62. package/dist/commands/account.d.ts.map +1 -1
  63. package/dist/commands/account.js +187 -33
  64. package/dist/commands/account.js.map +1 -1
  65. package/dist/commands/agent.d.ts.map +1 -0
  66. package/dist/commands/agent.js +125 -0
  67. package/dist/commands/agent.js.map +1 -0
  68. package/dist/commands/approve.d.ts.map +1 -0
  69. package/dist/commands/approve.js +505 -0
  70. package/dist/commands/approve.js.map +1 -0
  71. package/dist/commands/automation.d.ts.map +1 -0
  72. package/dist/commands/automation.js +346 -0
  73. package/dist/commands/automation.js.map +1 -0
  74. package/dist/commands/balances.d.ts.map +1 -1
  75. package/dist/commands/balances.js +226 -73
  76. package/dist/commands/balances.js.map +1 -1
  77. package/dist/commands/buy.d.ts.map +1 -1
  78. package/dist/commands/buy.js +149 -146
  79. package/dist/commands/buy.js.map +1 -1
  80. package/dist/commands/call.d.ts.map +1 -0
  81. package/dist/commands/call.js +51 -0
  82. package/dist/commands/call.js.map +1 -0
  83. package/dist/commands/cex.d.ts.map +1 -0
  84. package/dist/commands/cex.js +958 -0
  85. package/dist/commands/cex.js.map +1 -0
  86. package/dist/commands/chat.d.ts.map +1 -1
  87. package/dist/commands/chat.js +169 -411
  88. package/dist/commands/chat.js.map +1 -1
  89. package/dist/commands/claim.d.ts.map +1 -1
  90. package/dist/commands/claim.js +313 -29
  91. package/dist/commands/claim.js.map +1 -1
  92. package/dist/commands/create.d.ts.map +1 -1
  93. package/dist/commands/create.js +151 -43
  94. package/dist/commands/create.js.map +1 -1
  95. package/dist/commands/gasless-swap.d.ts.map +1 -0
  96. package/dist/commands/gasless-swap.js +232 -0
  97. package/dist/commands/gasless-swap.js.map +1 -0
  98. package/dist/commands/health.d.ts.map +1 -1
  99. package/dist/commands/health.js +63 -7
  100. package/dist/commands/health.js.map +1 -1
  101. package/dist/commands/info.d.ts.map +1 -1
  102. package/dist/commands/info.js +131 -47
  103. package/dist/commands/info.js.map +1 -1
  104. package/dist/commands/launchpad.d.ts.map +1 -0
  105. package/dist/commands/launchpad.js +708 -0
  106. package/dist/commands/launchpad.js.map +1 -0
  107. package/dist/commands/list.d.ts.map +1 -1
  108. package/dist/commands/list.js +57 -23
  109. package/dist/commands/list.js.map +1 -1
  110. package/dist/commands/market.d.ts.map +1 -0
  111. package/dist/commands/market.js +960 -0
  112. package/dist/commands/market.js.map +1 -0
  113. package/dist/commands/mcp-install.d.ts.map +1 -0
  114. package/dist/commands/mcp-install.js +387 -0
  115. package/dist/commands/mcp-install.js.map +1 -0
  116. package/dist/commands/opps.d.ts.map +1 -0
  117. package/dist/commands/opps.js +409 -0
  118. package/dist/commands/opps.js.map +1 -0
  119. package/dist/commands/perps.d.ts.map +1 -0
  120. package/dist/commands/perps.js +248 -0
  121. package/dist/commands/perps.js.map +1 -0
  122. package/dist/commands/portfolio.d.ts.map +1 -0
  123. package/dist/commands/portfolio.js +679 -0
  124. package/dist/commands/portfolio.js.map +1 -0
  125. package/dist/commands/positions.d.ts.map +1 -1
  126. package/dist/commands/positions.js +76 -47
  127. package/dist/commands/positions.js.map +1 -1
  128. package/dist/commands/predict.d.ts.map +1 -0
  129. package/dist/commands/predict.js +280 -0
  130. package/dist/commands/predict.js.map +1 -0
  131. package/dist/commands/predictions.d.ts.map +1 -0
  132. package/dist/commands/predictions.js +486 -0
  133. package/dist/commands/predictions.js.map +1 -0
  134. package/dist/commands/risk.d.ts.map +1 -0
  135. package/dist/commands/risk.js +225 -0
  136. package/dist/commands/risk.js.map +1 -0
  137. package/dist/commands/security.d.ts.map +1 -0
  138. package/dist/commands/security.js +244 -0
  139. package/dist/commands/security.js.map +1 -0
  140. package/dist/commands/sell.d.ts.map +1 -1
  141. package/dist/commands/sell.js +67 -34
  142. package/dist/commands/sell.js.map +1 -1
  143. package/dist/commands/send.d.ts.map +1 -0
  144. package/dist/commands/send.js +733 -0
  145. package/dist/commands/send.js.map +1 -0
  146. package/dist/commands/sign.d.ts.map +1 -0
  147. package/dist/commands/sign.js +1048 -0
  148. package/dist/commands/sign.js.map +1 -0
  149. package/dist/commands/swap.d.ts.map +1 -0
  150. package/dist/commands/swap.js +744 -0
  151. package/dist/commands/swap.js.map +1 -0
  152. package/dist/commands/system.d.ts.map +1 -0
  153. package/dist/commands/system.js +417 -0
  154. package/dist/commands/system.js.map +1 -0
  155. package/dist/commands/tools/index.d.ts.map +1 -0
  156. package/dist/commands/tools/index.js +2040 -0
  157. package/dist/commands/tools/index.js.map +1 -0
  158. package/dist/commands/trade.d.ts.map +1 -0
  159. package/dist/commands/trade.js +237 -0
  160. package/dist/commands/trade.js.map +1 -0
  161. package/dist/commands/transactions.d.ts.map +1 -1
  162. package/dist/commands/transactions.js +29 -17
  163. package/dist/commands/transactions.js.map +1 -1
  164. package/dist/commands/update.d.ts.map +1 -0
  165. package/dist/commands/update.js +429 -0
  166. package/dist/commands/update.js.map +1 -0
  167. package/dist/config.d.ts.map +1 -1
  168. package/dist/config.js +351 -40
  169. package/dist/config.js.map +1 -1
  170. package/dist/index.js +4524 -924
  171. package/dist/index.js.map +1 -1
  172. package/dist/interactive/art.d.ts.map +1 -1
  173. package/dist/interactive/art.js +33 -1
  174. package/dist/interactive/art.js.map +1 -1
  175. package/dist/interactive/shell.d.ts.map +1 -1
  176. package/dist/interactive/shell.js +467 -2652
  177. package/dist/interactive/shell.js.map +1 -1
  178. package/dist/mcp/context.d.ts.map +1 -0
  179. package/dist/mcp/context.js +211 -0
  180. package/dist/mcp/context.js.map +1 -0
  181. package/dist/mcp/onboarding.d.ts.map +1 -0
  182. package/dist/mcp/onboarding.js +266 -0
  183. package/dist/mcp/onboarding.js.map +1 -0
  184. package/dist/mcp/resources.d.ts.map +1 -0
  185. package/dist/mcp/resources.js +222 -0
  186. package/dist/mcp/resources.js.map +1 -0
  187. package/dist/mcp/server.d.ts.map +1 -1
  188. package/dist/mcp/server.js +51 -1
  189. package/dist/mcp/server.js.map +1 -1
  190. package/dist/mcp/tools.d.ts.map +1 -1
  191. package/dist/mcp/tools.js +4119 -169
  192. package/dist/mcp/tools.js.map +1 -1
  193. package/dist/mcp/types.d.ts.map +1 -1
  194. package/dist/types/agent-info.d.ts.map +1 -0
  195. package/dist/types/agent-info.js +11 -0
  196. package/dist/types/agent-info.js.map +1 -0
  197. package/dist/ui/components/ScrollableList.d.ts.map +1 -0
  198. package/dist/ui/components/ScrollableList.js +72 -0
  199. package/dist/ui/components/ScrollableList.js.map +1 -0
  200. package/dist/ui/components/ThemeProvider.d.ts.map +1 -0
  201. package/dist/ui/components/ThemeProvider.js +87 -0
  202. package/dist/ui/components/ThemeProvider.js.map +1 -0
  203. package/dist/ui/components/ThemedBox.d.ts.map +1 -0
  204. package/dist/ui/components/ThemedBox.js +24 -0
  205. package/dist/ui/components/ThemedBox.js.map +1 -0
  206. package/dist/ui/components/agent/ChatHeader.d.ts.map +1 -0
  207. package/dist/ui/components/agent/ChatHeader.js +39 -0
  208. package/dist/ui/components/agent/ChatHeader.js.map +1 -0
  209. package/dist/ui/components/agent/Header.d.ts.map +1 -0
  210. package/dist/ui/components/agent/Header.js +14 -0
  211. package/dist/ui/components/agent/Header.js.map +1 -0
  212. package/dist/ui/components/agent/Input.d.ts.map +1 -0
  213. package/dist/ui/components/agent/Input.js +23 -0
  214. package/dist/ui/components/agent/Input.js.map +1 -0
  215. package/dist/ui/components/agent/Output.d.ts.map +1 -0
  216. package/dist/ui/components/agent/Output.js +23 -0
  217. package/dist/ui/components/agent/Output.js.map +1 -0
  218. package/dist/ui/components/chat/TokenChatUI.d.ts.map +1 -0
  219. package/dist/ui/components/chat/TokenChatUI.js +133 -0
  220. package/dist/ui/components/chat/TokenChatUI.js.map +1 -0
  221. package/dist/ui/components/shell/ShellHeader.d.ts.map +1 -0
  222. package/dist/ui/components/shell/ShellHeader.js +31 -0
  223. package/dist/ui/components/shell/ShellHeader.js.map +1 -0
  224. package/dist/ui/components/shell/ShellInput.d.ts.map +1 -0
  225. package/dist/ui/components/shell/ShellInput.js +151 -0
  226. package/dist/ui/components/shell/ShellInput.js.map +1 -0
  227. package/dist/ui/components/shell/ShellOutput.d.ts.map +1 -0
  228. package/dist/ui/components/shell/ShellOutput.js +8 -0
  229. package/dist/ui/components/shell/ShellOutput.js.map +1 -0
  230. package/dist/ui/hooks/useChatWebSocket.d.ts.map +1 -0
  231. package/dist/ui/hooks/useChatWebSocket.js +76 -0
  232. package/dist/ui/hooks/useChatWebSocket.js.map +1 -0
  233. package/dist/ui/hooks/useCommandHistory.d.ts.map +1 -0
  234. package/dist/ui/hooks/useCommandHistory.js +70 -0
  235. package/dist/ui/hooks/useCommandHistory.js.map +1 -0
  236. package/dist/ui/hooks/useDebounce.d.ts.map +1 -0
  237. package/dist/ui/hooks/useDebounce.js +17 -0
  238. package/dist/ui/hooks/useDebounce.js.map +1 -0
  239. package/dist/ui/hooks/useLogStream.d.ts.map +1 -0
  240. package/dist/ui/hooks/useLogStream.js +20 -0
  241. package/dist/ui/hooks/useLogStream.js.map +1 -0
  242. package/dist/ui/hooks/useVirtualScroll.d.ts.map +1 -0
  243. package/dist/ui/hooks/useVirtualScroll.js +70 -0
  244. package/dist/ui/hooks/useVirtualScroll.js.map +1 -0
  245. package/dist/utils/admin.d.ts.map +1 -0
  246. package/dist/utils/admin.js +144 -0
  247. package/dist/utils/admin.js.map +1 -0
  248. package/dist/utils/autoSetup.d.ts.map +1 -0
  249. package/dist/utils/autoSetup.js +252 -0
  250. package/dist/utils/autoSetup.js.map +1 -0
  251. package/dist/utils/build-constants.d.ts.map +1 -0
  252. package/dist/utils/build-constants.js +10 -0
  253. package/dist/utils/build-constants.js.map +1 -0
  254. package/dist/utils/constants.d.ts.map +1 -1
  255. package/dist/utils/errors.d.ts.map +1 -1
  256. package/dist/utils/errors.js +10 -1
  257. package/dist/utils/errors.js.map +1 -1
  258. package/dist/utils/formatting.d.ts.map +1 -1
  259. package/dist/utils/formatting.js +46 -9
  260. package/dist/utils/formatting.js.map +1 -1
  261. package/dist/utils/llm-cli-config.d.ts.map +1 -0
  262. package/dist/utils/llm-cli-config.js +963 -0
  263. package/dist/utils/llm-cli-config.js.map +1 -0
  264. package/dist/utils/llm-cli-detector.d.ts.map +1 -0
  265. package/dist/utils/llm-cli-detector.js +202 -0
  266. package/dist/utils/llm-cli-detector.js.map +1 -0
  267. package/dist/utils/loading.d.ts.map +1 -1
  268. package/dist/utils/loading.js +25 -3
  269. package/dist/utils/loading.js.map +1 -1
  270. package/dist/utils/maintenance.d.ts.map +1 -0
  271. package/dist/utils/maintenance.js +17 -0
  272. package/dist/utils/maintenance.js.map +1 -0
  273. package/dist/utils/mcp-config.d.ts.map +1 -0
  274. package/dist/utils/mcp-config.js +77 -0
  275. package/dist/utils/mcp-config.js.map +1 -0
  276. package/dist/utils/privateKeyPrompt.d.ts.map +1 -1
  277. package/dist/utils/privateKeyPrompt.js +308 -129
  278. package/dist/utils/privateKeyPrompt.js.map +1 -1
  279. package/dist/utils/process-cleanup.d.ts.map +1 -0
  280. package/dist/utils/process-cleanup.js +136 -0
  281. package/dist/utils/process-cleanup.js.map +1 -0
  282. package/dist/utils/retry.d.ts.map +1 -0
  283. package/dist/utils/retry.js +56 -0
  284. package/dist/utils/retry.js.map +1 -0
  285. package/dist/utils/rpc-helpers.d.ts.map +1 -0
  286. package/dist/utils/rpc-helpers.js +70 -0
  287. package/dist/utils/rpc-helpers.js.map +1 -0
  288. package/dist/utils/rpc-transport.d.ts.map +1 -0
  289. package/dist/utils/rpc-transport.js +87 -0
  290. package/dist/utils/rpc-transport.js.map +1 -0
  291. package/dist/utils/shell-setup.d.ts.map +1 -0
  292. package/dist/utils/shell-setup.js +531 -0
  293. package/dist/utils/shell-setup.js.map +1 -0
  294. package/dist/utils/status.d.ts.map +1 -1
  295. package/dist/utils/status.js +34 -5
  296. package/dist/utils/status.js.map +1 -1
  297. package/dist/utils/token-resolver.d.ts.map +1 -1
  298. package/dist/utils/token-resolver.js +51 -8
  299. package/dist/utils/token-resolver.js.map +1 -1
  300. package/dist/utils/x402-caller.d.ts.map +1 -0
  301. package/dist/utils/x402-caller.js +17 -0
  302. package/dist/utils/x402-caller.js.map +1 -0
  303. package/docs/README.md +28 -0
  304. package/docs/agent/README.md +18 -0
  305. package/docs/api/README.md +41 -0
  306. package/docs/cli/README.md +42 -0
  307. package/docs/guides/README.md +26 -0
  308. package/docs/implementation/README.md +18 -0
  309. package/docs/planning/README.md +19 -0
  310. package/docs/testing/README.md +15 -0
  311. package/docs/ux/README.md +16 -0
  312. package/issues.txt +2 -0
  313. package/package.json +24 -9
  314. package/scripts/cat-spin.sh +417 -0
  315. package/scripts/deprecate-rc-versions.js +58 -0
  316. package/scripts/inject-build-constants.js +43 -0
  317. package/scripts/monitor-foobar.js +117 -0
  318. package/swap.logs +61 -0
  319. package/swapping.txt +108 -0
  320. package/test.txt +12 -0
  321. package/tests/fixtures/test-data.json +16 -0
  322. package/Screenshot 2025-12-21 at 8.56.02/342/200/257PM.png +0 -0
@@ -0,0 +1,733 @@
1
+ import chalk from "chalk";
2
+ import { printBox, formatAddress, } from "../utils/formatting.js";
3
+ import { printSuccess } from "../interactive/art.js";
4
+ import { resolveTokenId } from "../utils/token-resolver.js";
5
+ import { createPublicClient, createWalletClient, parseAbi, formatUnits, parseUnits, parseEther, formatEther, isAddress, getAddress, encodeFunctionData, } from "viem";
6
+ import { privateKeyToAccount } from "viem/accounts";
7
+ import { config } from "../config.js";
8
+ import { createRpcTransport } from "../utils/rpc-transport.js";
9
+ import { getNetworkConfig } from "../utils/constants.js";
10
+ import inquirer from "inquirer";
11
+ import { readFileSync } from "fs";
12
+ import Table from "cli-table3";
13
+ // ERC20 ABI for token operations
14
+ const ERC20_ABI = parseAbi([
15
+ "function transfer(address to, uint256 amount) returns (bool)",
16
+ "function balanceOf(address owner) view returns (uint256)",
17
+ "function decimals() view returns (uint8)",
18
+ "function symbol() view returns (string)",
19
+ "function name() view returns (string)",
20
+ ]);
21
+ /**
22
+ * Validate and checksum an Ethereum address
23
+ */
24
+ function validateAddress(address) {
25
+ if (!isAddress(address)) {
26
+ throw new Error(`Invalid address format: ${address}`);
27
+ }
28
+ return getAddress(address);
29
+ }
30
+ /**
31
+ * Parse amount string to bigint
32
+ */
33
+ function parseAmount(amount, decimals = 18) {
34
+ try {
35
+ return parseUnits(amount, decimals);
36
+ }
37
+ catch (error) {
38
+ throw new Error(`Invalid amount format: ${amount}. ${error instanceof Error ? error.message : ""}`);
39
+ }
40
+ }
41
+ /**
42
+ * Get gas price configuration
43
+ */
44
+ async function getGasConfig(publicClient, options) {
45
+ const gasConfig = {};
46
+ // Legacy gas price
47
+ if (options.gasPrice) {
48
+ const gwei = parseFloat(options.gasPrice);
49
+ gasConfig.gasPrice = parseUnits(gwei.toString(), 9); // Convert gwei to wei
50
+ }
51
+ // EIP-1559 fees
52
+ if (options.maxFeePerGas || options.maxPriorityFeePerGas) {
53
+ if (options.maxFeePerGas) {
54
+ const gwei = parseFloat(options.maxFeePerGas);
55
+ gasConfig.maxFeePerGas = parseUnits(gwei.toString(), 9);
56
+ }
57
+ if (options.maxPriorityFeePerGas) {
58
+ const gwei = parseFloat(options.maxPriorityFeePerGas);
59
+ gasConfig.maxPriorityFeePerGas = parseUnits(gwei.toString(), 9);
60
+ }
61
+ }
62
+ else if (!options.gasPrice) {
63
+ // Auto-estimate EIP-1559 fees if not provided
64
+ try {
65
+ const feeData = await publicClient.estimateFeesPerGas();
66
+ if (feeData) {
67
+ gasConfig.maxFeePerGas = feeData.maxFeePerGas;
68
+ gasConfig.maxPriorityFeePerGas =
69
+ feeData.maxPriorityFeePerGas || feeData.maxFeePerGas / 2n;
70
+ }
71
+ }
72
+ catch {
73
+ // Fallback to legacy gas price
74
+ try {
75
+ const gasPrice = await publicClient.getGasPrice();
76
+ gasConfig.gasPrice = gasPrice;
77
+ }
78
+ catch {
79
+ // Use default if all else fails
80
+ }
81
+ }
82
+ }
83
+ // Gas limit
84
+ if (options.gasLimit) {
85
+ gasConfig.gas = options.gasLimit;
86
+ }
87
+ return gasConfig;
88
+ }
89
+ /**
90
+ * Prompt for confirmation before sending
91
+ */
92
+ async function promptConfirmation(summary, options) {
93
+ if (options.noConfirm || options.silent) {
94
+ return true;
95
+ }
96
+ if (options.confirm || !process.stdin.isTTY) {
97
+ // Force confirmation prompt
98
+ const answers = await inquirer.prompt([
99
+ {
100
+ type: "confirm",
101
+ name: "confirm",
102
+ message: `Send ${summary.amount} ${summary.tokenSymbol || "ETH"} to ${summary.to}?`,
103
+ default: false,
104
+ },
105
+ ]);
106
+ return answers.confirm;
107
+ }
108
+ // Default: show summary but don't require confirmation in non-interactive mode
109
+ return true;
110
+ }
111
+ /**
112
+ * Send native currency (ETH)
113
+ */
114
+ export async function sendNativeCurrency(client, to, amount, privateKey, options = {}) {
115
+ const account = privateKeyToAccount(privateKey);
116
+ const from = account.address;
117
+ const network = client.getNetwork();
118
+ const { chain } = await getNetworkConfig(network);
119
+ const publicClient = createPublicClient({
120
+ chain,
121
+ transport: createRpcTransport({
122
+ chainId: chain.id,
123
+ httpcatClient: client,
124
+ }),
125
+ });
126
+ const walletClient = createWalletClient({
127
+ account,
128
+ chain,
129
+ transport: createRpcTransport({
130
+ chainId: chain.id,
131
+ httpcatClient: client,
132
+ }),
133
+ });
134
+ // Validate address
135
+ const toAddress = validateAddress(to);
136
+ // Parse amount
137
+ const amountWei = parseEther(amount);
138
+ // Check balance
139
+ const balance = await publicClient.getBalance({ address: from });
140
+ if (balance < amountWei) {
141
+ throw new Error(`Insufficient balance. Have ${formatEther(balance)} ETH, need ${amount} ETH`);
142
+ }
143
+ // Get gas configuration
144
+ const gasConfig = await getGasConfig(publicClient, options);
145
+ // Estimate gas (don't include gas config in estimation)
146
+ let estimatedGas;
147
+ try {
148
+ estimatedGas = await publicClient.estimateGas({
149
+ account: from,
150
+ to: toAddress,
151
+ value: amountWei,
152
+ });
153
+ }
154
+ catch (error) {
155
+ throw new Error(`Failed to estimate gas: ${error instanceof Error ? error.message : "Unknown error"}`);
156
+ }
157
+ // Check if we have enough for gas
158
+ const totalNeeded = amountWei +
159
+ (gasConfig.gas || estimatedGas) *
160
+ (gasConfig.gasPrice || gasConfig.maxFeePerGas || 0n);
161
+ if (balance < totalNeeded) {
162
+ throw new Error(`Insufficient balance for transaction. Need ${formatEther(totalNeeded)} ETH (${amount} ETH + gas)`);
163
+ }
164
+ // Show summary and prompt for confirmation
165
+ const estimatedGasEth = formatEther((gasConfig.gas || estimatedGas) *
166
+ (gasConfig.gasPrice || gasConfig.maxFeePerGas || 0n));
167
+ const confirmed = await promptConfirmation({
168
+ type: "native",
169
+ from,
170
+ to: toAddress,
171
+ amount,
172
+ estimatedGas: estimatedGasEth,
173
+ }, options);
174
+ if (!confirmed) {
175
+ throw new Error("Transaction cancelled by user");
176
+ }
177
+ // Dry run mode
178
+ if (options.dryRun) {
179
+ if (!options.silent) {
180
+ console.log(chalk.yellow("\nšŸ” Dry Run Mode - Transaction not broadcast\n"));
181
+ console.log(chalk.cyan(`From: ${from}`));
182
+ console.log(chalk.cyan(`To: ${toAddress}`));
183
+ console.log(chalk.cyan(`Amount: ${amount} ETH`));
184
+ console.log(chalk.cyan(`Estimated Gas: ${estimatedGas.toString()}`));
185
+ console.log(chalk.cyan(`Estimated Gas Cost: ${estimatedGasEth} ETH`));
186
+ }
187
+ return {
188
+ success: true,
189
+ type: "native",
190
+ from,
191
+ to: toAddress,
192
+ amount,
193
+ gasUsed: estimatedGas,
194
+ };
195
+ }
196
+ // Build transaction
197
+ // Note: Don't include 'account' field - walletClient already has it configured
198
+ const txRequest = {
199
+ to: toAddress,
200
+ value: amountWei,
201
+ gas: gasConfig.gas || estimatedGas,
202
+ ...gasConfig,
203
+ };
204
+ // Set nonce if provided
205
+ if (options.nonce !== undefined) {
206
+ txRequest.nonce = options.nonce;
207
+ }
208
+ // Send transaction
209
+ if (!options.silent) {
210
+ console.log(chalk.cyan(`\nšŸ“¤ Sending ${amount} ETH to ${toAddress}...`));
211
+ }
212
+ const txHash = await walletClient.sendTransaction(txRequest);
213
+ let result = {
214
+ success: true,
215
+ type: "native",
216
+ txHash,
217
+ from,
218
+ to: toAddress,
219
+ amount,
220
+ };
221
+ // Wait for confirmation if requested
222
+ if (options.wait) {
223
+ if (!options.silent) {
224
+ console.log(chalk.cyan(`ā³ Waiting for confirmation...`));
225
+ }
226
+ const receipt = await publicClient.waitForTransactionReceipt({
227
+ hash: txHash,
228
+ timeout: (options.timeout || 300) * 1000,
229
+ });
230
+ result.gasUsed = receipt.gasUsed;
231
+ result.blockNumber = receipt.blockNumber;
232
+ result.confirmations = 1; // At least 1 confirmation
233
+ if (!options.silent) {
234
+ console.log(chalk.green(`āœ… Transaction confirmed in block ${receipt.blockNumber.toString()}`));
235
+ }
236
+ }
237
+ return result;
238
+ }
239
+ /**
240
+ * Send ERC20 token
241
+ */
242
+ export async function sendToken(client, tokenIdentifier, to, amount, privateKey, options = {}) {
243
+ const account = privateKeyToAccount(privateKey);
244
+ const from = account.address;
245
+ const network = client.getNetwork();
246
+ const { chain } = await getNetworkConfig(network);
247
+ const publicClient = createPublicClient({
248
+ chain,
249
+ transport: createRpcTransport({
250
+ chainId: chain.id,
251
+ httpcatClient: client,
252
+ }),
253
+ });
254
+ const walletClient = createWalletClient({
255
+ account,
256
+ chain,
257
+ transport: createRpcTransport({
258
+ chainId: chain.id,
259
+ httpcatClient: client,
260
+ }),
261
+ });
262
+ // Validate address
263
+ const toAddress = validateAddress(to);
264
+ // Resolve token identifier to address
265
+ let tokenAddress;
266
+ try {
267
+ const resolved = await resolveTokenId(tokenIdentifier, client, options.silent);
268
+ tokenAddress = validateAddress(resolved);
269
+ }
270
+ catch (error) {
271
+ // If resolution fails, try treating it as an address directly
272
+ if (isAddress(tokenIdentifier)) {
273
+ tokenAddress = getAddress(tokenIdentifier);
274
+ }
275
+ else {
276
+ throw new Error(`Failed to resolve token "${tokenIdentifier}": ${error instanceof Error ? error.message : "Unknown error"}`);
277
+ }
278
+ }
279
+ // Get token metadata
280
+ let tokenDecimals = options.decimals;
281
+ let tokenSymbol = "TOKEN";
282
+ let tokenName = "Token";
283
+ try {
284
+ if (!tokenDecimals) {
285
+ tokenDecimals = (await publicClient.readContract({
286
+ address: tokenAddress,
287
+ abi: ERC20_ABI,
288
+ functionName: "decimals",
289
+ }));
290
+ }
291
+ tokenSymbol = (await publicClient.readContract({
292
+ address: tokenAddress,
293
+ abi: ERC20_ABI,
294
+ functionName: "symbol",
295
+ }));
296
+ tokenName = (await publicClient.readContract({
297
+ address: tokenAddress,
298
+ abi: ERC20_ABI,
299
+ functionName: "name",
300
+ }));
301
+ }
302
+ catch (error) {
303
+ if (!tokenDecimals) {
304
+ throw new Error(`Failed to get token decimals. Use --decimals flag or ensure token contract is valid. ${error instanceof Error ? error.message : ""}`);
305
+ }
306
+ // Use provided decimals if available
307
+ }
308
+ // Parse amount
309
+ const amountWei = parseAmount(amount, tokenDecimals);
310
+ // Check balance
311
+ const balance = (await publicClient.readContract({
312
+ address: tokenAddress,
313
+ abi: ERC20_ABI,
314
+ functionName: "balanceOf",
315
+ args: [from],
316
+ }));
317
+ if (balance < amountWei) {
318
+ throw new Error(`Insufficient token balance. Have ${formatUnits(balance, tokenDecimals)} ${tokenSymbol}, need ${amount} ${tokenSymbol}`);
319
+ }
320
+ // Get gas configuration
321
+ const gasConfig = await getGasConfig(publicClient, options);
322
+ // Estimate gas for transfer (don't include gas config in estimation)
323
+ let estimatedGas;
324
+ try {
325
+ estimatedGas = await publicClient.estimateGas({
326
+ account: from,
327
+ to: tokenAddress,
328
+ data: encodeFunctionData({
329
+ abi: ERC20_ABI,
330
+ functionName: "transfer",
331
+ args: [toAddress, amountWei],
332
+ }),
333
+ });
334
+ }
335
+ catch (error) {
336
+ throw new Error(`Failed to estimate gas: ${error instanceof Error ? error.message : "Unknown error"}`);
337
+ }
338
+ // Check ETH balance for gas
339
+ const ethBalance = await publicClient.getBalance({ address: from });
340
+ const estimatedGasCost = (gasConfig.gas || estimatedGas) *
341
+ (gasConfig.gasPrice || gasConfig.maxFeePerGas || 0n);
342
+ if (ethBalance < estimatedGasCost) {
343
+ throw new Error(`Insufficient ETH for gas. Need ${formatEther(estimatedGasCost)} ETH`);
344
+ }
345
+ // Show summary and prompt for confirmation
346
+ const estimatedGasEth = formatEther(estimatedGasCost);
347
+ const confirmed = await promptConfirmation({
348
+ type: "token",
349
+ from,
350
+ to: toAddress,
351
+ amount,
352
+ tokenSymbol,
353
+ estimatedGas: estimatedGasEth,
354
+ }, options);
355
+ if (!confirmed) {
356
+ throw new Error("Transaction cancelled by user");
357
+ }
358
+ // Dry run mode
359
+ if (options.dryRun) {
360
+ if (!options.silent) {
361
+ console.log(chalk.yellow("\nšŸ” Dry Run Mode - Transaction not broadcast\n"));
362
+ console.log(chalk.cyan(`From: ${from}`));
363
+ console.log(chalk.cyan(`To: ${toAddress}`));
364
+ console.log(chalk.cyan(`Token: ${tokenName} (${tokenSymbol})`));
365
+ console.log(chalk.cyan(`Token Address: ${tokenAddress}`));
366
+ console.log(chalk.cyan(`Amount: ${amount} ${tokenSymbol}`));
367
+ console.log(chalk.cyan(`Estimated Gas: ${estimatedGas.toString()}`));
368
+ console.log(chalk.cyan(`Estimated Gas Cost: ${estimatedGasEth} ETH`));
369
+ }
370
+ return {
371
+ success: true,
372
+ type: "token",
373
+ from,
374
+ to: toAddress,
375
+ amount,
376
+ tokenAddress,
377
+ tokenSymbol,
378
+ gasUsed: estimatedGas,
379
+ };
380
+ }
381
+ // Build transaction
382
+ // Note: Don't include 'account' field - walletClient already has it configured
383
+ const txRequest = {
384
+ to: tokenAddress,
385
+ data: encodeFunctionData({
386
+ abi: ERC20_ABI,
387
+ functionName: "transfer",
388
+ args: [toAddress, amountWei],
389
+ }),
390
+ gas: gasConfig.gas || estimatedGas,
391
+ ...gasConfig,
392
+ };
393
+ // Set nonce if provided
394
+ if (options.nonce !== undefined) {
395
+ txRequest.nonce = options.nonce;
396
+ }
397
+ // Send transaction
398
+ if (!options.silent) {
399
+ console.log(chalk.cyan(`\nšŸ“¤ Sending ${amount} ${tokenSymbol} to ${toAddress}...`));
400
+ }
401
+ const txHash = await walletClient.sendTransaction(txRequest);
402
+ let result = {
403
+ success: true,
404
+ type: "token",
405
+ txHash,
406
+ from,
407
+ to: toAddress,
408
+ amount,
409
+ tokenAddress,
410
+ tokenSymbol,
411
+ };
412
+ // Wait for confirmation if requested
413
+ if (options.wait) {
414
+ if (!options.silent) {
415
+ console.log(chalk.cyan(`ā³ Waiting for confirmation...`));
416
+ }
417
+ const receipt = await publicClient.waitForTransactionReceipt({
418
+ hash: txHash,
419
+ timeout: (options.timeout || 300) * 1000,
420
+ });
421
+ result.gasUsed = receipt.gasUsed;
422
+ result.blockNumber = receipt.blockNumber;
423
+ result.confirmations = 1;
424
+ if (!options.silent) {
425
+ console.log(chalk.green(`āœ… Transaction confirmed in block ${receipt.blockNumber.toString()}`));
426
+ }
427
+ }
428
+ return result;
429
+ }
430
+ /**
431
+ * Resolve recipient (account index or address) - helper for batch sends
432
+ */
433
+ function resolveRecipientForBatch(to) {
434
+ // Check if it's a number (account index)
435
+ const accountIndex = parseInt(to, 10);
436
+ if (!isNaN(accountIndex) && to.trim() === accountIndex.toString()) {
437
+ // It's a number - try to resolve to account address
438
+ const accounts = config.getAllAccounts();
439
+ const account = accounts.find((acc) => acc.index === accountIndex);
440
+ if (!account) {
441
+ throw new Error(`Account at index ${accountIndex} not found. Use "httpcat account list" to see available accounts.`);
442
+ }
443
+ return account.address;
444
+ }
445
+ // Not a number - treat as address
446
+ return to;
447
+ }
448
+ /**
449
+ * Send batch transactions
450
+ */
451
+ export async function sendBatch(client, batchInput, privateKey, options = {}) {
452
+ // Parse batch input if it's a string (file path or JSON)
453
+ let batch;
454
+ if (typeof batchInput === "string") {
455
+ try {
456
+ // Try to read as file first
457
+ try {
458
+ const fileContent = readFileSync(batchInput, "utf-8");
459
+ batch = JSON.parse(fileContent);
460
+ }
461
+ catch {
462
+ // If file read fails, try parsing as JSON string
463
+ batch = JSON.parse(batchInput);
464
+ }
465
+ }
466
+ catch (error) {
467
+ throw new Error(`Failed to parse batch input: ${error instanceof Error ? error.message : "Unknown error"}`);
468
+ }
469
+ }
470
+ else {
471
+ batch = batchInput;
472
+ }
473
+ const results = [];
474
+ // Process native currency sends
475
+ if (batch.native && batch.native.length > 0) {
476
+ if (!options.silent) {
477
+ console.log(chalk.cyan(`\nšŸ“¦ Processing ${batch.native.length} native currency send(s)...`));
478
+ }
479
+ for (const item of batch.native) {
480
+ try {
481
+ // Resolve recipient (account index or address)
482
+ const recipientAddress = resolveRecipientForBatch(item.to);
483
+ const result = await sendNativeCurrency(client, recipientAddress, item.amount, privateKey, { ...options, silent: true } // Suppress individual confirmations in batch
484
+ );
485
+ results.push(result);
486
+ if (!options.silent) {
487
+ console.log(chalk.green(` āœ… Sent ${item.amount} ETH to ${recipientAddress}`));
488
+ }
489
+ }
490
+ catch (error) {
491
+ results.push({
492
+ success: false,
493
+ type: "native",
494
+ from: privateKeyToAccount(privateKey).address,
495
+ to: item.to,
496
+ amount: item.amount,
497
+ error: error instanceof Error ? error.message : "Unknown error",
498
+ });
499
+ if (!options.silent) {
500
+ console.log(chalk.red(` āŒ Failed to send ${item.amount} ETH to ${item.to}: ${error instanceof Error ? error.message : "Unknown error"}`));
501
+ }
502
+ }
503
+ }
504
+ }
505
+ // Process token sends
506
+ if (batch.tokens && batch.tokens.length > 0) {
507
+ if (!options.silent) {
508
+ console.log(chalk.cyan(`\nšŸ“¦ Processing ${batch.tokens.length} token send(s)...`));
509
+ }
510
+ for (const item of batch.tokens) {
511
+ if (!item.token) {
512
+ results.push({
513
+ success: false,
514
+ type: "token",
515
+ from: privateKeyToAccount(privateKey).address,
516
+ to: item.to,
517
+ amount: item.amount,
518
+ error: "Token identifier required",
519
+ });
520
+ continue;
521
+ }
522
+ try {
523
+ // Resolve recipient (account index or address)
524
+ const recipientAddress = resolveRecipientForBatch(item.to);
525
+ const result = await sendToken(client, item.token, recipientAddress, item.amount, privateKey, { ...options, silent: true } // Suppress individual confirmations in batch
526
+ );
527
+ results.push(result);
528
+ if (!options.silent) {
529
+ console.log(chalk.green(` āœ… Sent ${item.amount} ${result.tokenSymbol || "tokens"} to ${recipientAddress}`));
530
+ }
531
+ }
532
+ catch (error) {
533
+ results.push({
534
+ success: false,
535
+ type: "token",
536
+ from: privateKeyToAccount(privateKey).address,
537
+ to: item.to,
538
+ amount: item.amount,
539
+ tokenAddress: item.token,
540
+ error: error instanceof Error ? error.message : "Unknown error",
541
+ });
542
+ if (!options.silent) {
543
+ console.log(chalk.red(` āŒ Failed to send ${item.amount} ${item.token} to ${item.to}: ${error instanceof Error ? error.message : "Unknown error"}`));
544
+ }
545
+ }
546
+ }
547
+ }
548
+ return results;
549
+ }
550
+ /**
551
+ * Send custom transaction
552
+ */
553
+ export async function sendCustomTransaction(client, to, data, privateKey, value = 0n, options = {}) {
554
+ const account = privateKeyToAccount(privateKey);
555
+ const from = account.address;
556
+ const network = client.getNetwork();
557
+ const { chain } = await getNetworkConfig(network);
558
+ const publicClient = createPublicClient({
559
+ chain,
560
+ transport: createRpcTransport({
561
+ chainId: chain.id,
562
+ httpcatClient: client,
563
+ }),
564
+ });
565
+ const walletClient = createWalletClient({
566
+ account,
567
+ chain,
568
+ transport: createRpcTransport({
569
+ chainId: chain.id,
570
+ httpcatClient: client,
571
+ }),
572
+ });
573
+ // Validate address
574
+ const toAddress = validateAddress(to);
575
+ // Validate data
576
+ if (!data || !data.startsWith("0x")) {
577
+ throw new Error("Invalid transaction data. Must be hex string starting with 0x");
578
+ }
579
+ // Get gas configuration
580
+ const gasConfig = await getGasConfig(publicClient, options);
581
+ // Estimate gas (don't include gas config in estimation)
582
+ let estimatedGas;
583
+ try {
584
+ estimatedGas = await publicClient.estimateGas({
585
+ account: from,
586
+ to: toAddress,
587
+ data,
588
+ value,
589
+ });
590
+ }
591
+ catch (error) {
592
+ throw new Error(`Failed to estimate gas: ${error instanceof Error ? error.message : "Unknown error"}`);
593
+ }
594
+ // Check ETH balance for gas + value
595
+ const ethBalance = await publicClient.getBalance({ address: from });
596
+ const estimatedGasCost = (gasConfig.gas || estimatedGas) *
597
+ (gasConfig.gasPrice || gasConfig.maxFeePerGas || 0n);
598
+ const totalNeeded = value + estimatedGasCost;
599
+ if (ethBalance < totalNeeded) {
600
+ throw new Error(`Insufficient ETH. Need ${formatEther(totalNeeded)} ETH (${formatEther(value)} value + ${formatEther(estimatedGasCost)} gas)`);
601
+ }
602
+ // Show summary
603
+ if (!options.silent) {
604
+ console.log(chalk.cyan(`\nšŸ“¤ Sending custom transaction to ${toAddress}...`));
605
+ console.log(chalk.dim(`Data: ${data.substring(0, 66)}...`));
606
+ console.log(chalk.dim(`Value: ${formatEther(value)} ETH`));
607
+ console.log(chalk.dim(`Estimated Gas: ${estimatedGas.toString()}`));
608
+ }
609
+ // Dry run mode
610
+ if (options.dryRun) {
611
+ if (!options.silent) {
612
+ console.log(chalk.yellow("\nšŸ” Dry Run Mode - Transaction not broadcast\n"));
613
+ }
614
+ return {
615
+ success: true,
616
+ type: "custom",
617
+ from,
618
+ to: toAddress,
619
+ gasUsed: estimatedGas,
620
+ };
621
+ }
622
+ // Build transaction
623
+ // Note: Don't include 'account' field - walletClient already has it configured
624
+ const txRequest = {
625
+ to: toAddress,
626
+ data,
627
+ value,
628
+ gas: gasConfig.gas || estimatedGas,
629
+ ...gasConfig,
630
+ };
631
+ // Set nonce if provided
632
+ if (options.nonce !== undefined) {
633
+ txRequest.nonce = options.nonce;
634
+ }
635
+ const txHash = await walletClient.sendTransaction(txRequest);
636
+ let result = {
637
+ success: true,
638
+ type: "custom",
639
+ txHash,
640
+ from,
641
+ to: toAddress,
642
+ };
643
+ // Wait for confirmation if requested
644
+ if (options.wait) {
645
+ if (!options.silent) {
646
+ console.log(chalk.cyan(`ā³ Waiting for confirmation...`));
647
+ }
648
+ const receipt = await publicClient.waitForTransactionReceipt({
649
+ hash: txHash,
650
+ timeout: (options.timeout || 300) * 1000,
651
+ });
652
+ result.gasUsed = receipt.gasUsed;
653
+ result.blockNumber = receipt.blockNumber;
654
+ result.confirmations = 1;
655
+ if (!options.silent) {
656
+ console.log(chalk.green(`āœ… Transaction confirmed in block ${receipt.blockNumber.toString()}`));
657
+ }
658
+ }
659
+ return result;
660
+ }
661
+ /**
662
+ * Display send result for interactive mode
663
+ */
664
+ export function displaySendResult(result) {
665
+ console.log();
666
+ printSuccess(result.success ? "Transaction sent successfully!" : "Transaction failed");
667
+ const boxData = {
668
+ Type: chalk.cyan(result.type.toUpperCase()),
669
+ From: formatAddress(result.from),
670
+ To: formatAddress(result.to),
671
+ };
672
+ if (result.amount) {
673
+ boxData["Amount"] = chalk.green(result.amount + (result.tokenSymbol ? ` ${result.tokenSymbol}` : " ETH"));
674
+ }
675
+ if (result.tokenAddress) {
676
+ boxData["Token"] = formatAddress(result.tokenAddress);
677
+ }
678
+ if (result.txHash) {
679
+ boxData["Transaction"] = chalk.blue(result.txHash);
680
+ }
681
+ if (result.gasUsed) {
682
+ boxData["Gas Used"] = result.gasUsed.toString();
683
+ }
684
+ if (result.blockNumber) {
685
+ boxData["Block"] = result.blockNumber.toString();
686
+ }
687
+ if (result.error) {
688
+ boxData["Error"] = chalk.red(result.error);
689
+ }
690
+ printBox("Send Result", boxData);
691
+ console.log();
692
+ }
693
+ /**
694
+ * Display batch send results
695
+ */
696
+ export function displayBatchResults(results) {
697
+ const successful = results.filter((r) => r.success);
698
+ const failed = results.filter((r) => !r.success);
699
+ console.log();
700
+ printSuccess(`Batch send completed: ${successful.length} successful, ${failed.length} failed`);
701
+ console.log(chalk.magenta.bold("šŸ“¦ Batch Send Summary"));
702
+ console.log();
703
+ const table = new Table({
704
+ head: [
705
+ chalk.blue.bold("Status"),
706
+ chalk.cyan.bold("Type"),
707
+ chalk.green.bold("To"),
708
+ chalk.yellow.bold("Amount"),
709
+ chalk.magenta.bold("Tx Hash"),
710
+ ],
711
+ colAligns: ["left", "left", "left", "right", "left"],
712
+ });
713
+ for (const result of results) {
714
+ const status = result.success ? chalk.green("āœ…") : chalk.red("āŒ");
715
+ const type = result.type.toUpperCase();
716
+ const to = formatAddress(result.to);
717
+ const amount = result.amount
718
+ ? result.amount + (result.tokenSymbol ? ` ${result.tokenSymbol}` : " ETH")
719
+ : "-";
720
+ const txHash = result.txHash
721
+ ? chalk.blue(result.txHash.substring(0, 20) + "...")
722
+ : chalk.red(result.error || "-");
723
+ table.push([status, type, to, amount, txHash]);
724
+ }
725
+ console.log(table.toString());
726
+ console.log();
727
+ console.log(chalk.green(`āœ… Successful: ${successful.length}`));
728
+ if (failed.length > 0) {
729
+ console.log(chalk.red(`āŒ Failed: ${failed.length}`));
730
+ }
731
+ console.log();
732
+ }
733
+ //# sourceMappingURL=send.js.map