outsmart 2.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +568 -0
  3. package/dist/cli.d.ts +44 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +1251 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/dex/byreal-clmm.d.ts +16 -0
  8. package/dist/dex/byreal-clmm.d.ts.map +1 -0
  9. package/dist/dex/byreal-clmm.js +39 -0
  10. package/dist/dex/byreal-clmm.js.map +1 -0
  11. package/dist/dex/dflow.d.ts +27 -0
  12. package/dist/dex/dflow.d.ts.map +1 -0
  13. package/dist/dex/dflow.js +200 -0
  14. package/dist/dex/dflow.js.map +1 -0
  15. package/dist/dex/fusion-amm.d.ts +44 -0
  16. package/dist/dex/fusion-amm.d.ts.map +1 -0
  17. package/dist/dex/fusion-amm.js +546 -0
  18. package/dist/dex/fusion-amm.js.map +1 -0
  19. package/dist/dex/futarchy-amm.d.ts +32 -0
  20. package/dist/dex/futarchy-amm.d.ts.map +1 -0
  21. package/dist/dex/futarchy-amm.js +443 -0
  22. package/dist/dex/futarchy-amm.js.map +1 -0
  23. package/dist/dex/futarchy-idl.d.ts +2568 -0
  24. package/dist/dex/futarchy-idl.d.ts.map +1 -0
  25. package/dist/dex/futarchy-idl.js +2570 -0
  26. package/dist/dex/futarchy-idl.js.map +1 -0
  27. package/dist/dex/futarchy-launchpad.d.ts +68 -0
  28. package/dist/dex/futarchy-launchpad.d.ts.map +1 -0
  29. package/dist/dex/futarchy-launchpad.js +377 -0
  30. package/dist/dex/futarchy-launchpad.js.map +1 -0
  31. package/dist/dex/index.d.ts +88 -0
  32. package/dist/dex/index.d.ts.map +1 -0
  33. package/dist/dex/index.js +159 -0
  34. package/dist/dex/index.js.map +1 -0
  35. package/dist/dex/jupiter-ultra.d.ts +27 -0
  36. package/dist/dex/jupiter-ultra.d.ts.map +1 -0
  37. package/dist/dex/jupiter-ultra.js +369 -0
  38. package/dist/dex/jupiter-ultra.js.map +1 -0
  39. package/dist/dex/meteora-damm-v1.d.ts +36 -0
  40. package/dist/dex/meteora-damm-v1.d.ts.map +1 -0
  41. package/dist/dex/meteora-damm-v1.js +314 -0
  42. package/dist/dex/meteora-damm-v1.js.map +1 -0
  43. package/dist/dex/meteora-damm-v2.d.ts +103 -0
  44. package/dist/dex/meteora-damm-v2.d.ts.map +1 -0
  45. package/dist/dex/meteora-damm-v2.js +1146 -0
  46. package/dist/dex/meteora-damm-v2.js.map +1 -0
  47. package/dist/dex/meteora-dbc.d.ts +38 -0
  48. package/dist/dex/meteora-dbc.d.ts.map +1 -0
  49. package/dist/dex/meteora-dbc.js +374 -0
  50. package/dist/dex/meteora-dbc.js.map +1 -0
  51. package/dist/dex/meteora-dlmm.d.ts +79 -0
  52. package/dist/dex/meteora-dlmm.d.ts.map +1 -0
  53. package/dist/dex/meteora-dlmm.js +735 -0
  54. package/dist/dex/meteora-dlmm.js.map +1 -0
  55. package/dist/dex/orca.d.ts +31 -0
  56. package/dist/dex/orca.d.ts.map +1 -0
  57. package/dist/dex/orca.js +536 -0
  58. package/dist/dex/orca.js.map +1 -0
  59. package/dist/dex/pancakeswap-clmm.d.ts +16 -0
  60. package/dist/dex/pancakeswap-clmm.d.ts.map +1 -0
  61. package/dist/dex/pancakeswap-clmm.js +39 -0
  62. package/dist/dex/pancakeswap-clmm.js.map +1 -0
  63. package/dist/dex/pumpfun-amm.d.ts +46 -0
  64. package/dist/dex/pumpfun-amm.d.ts.map +1 -0
  65. package/dist/dex/pumpfun-amm.js +692 -0
  66. package/dist/dex/pumpfun-amm.js.map +1 -0
  67. package/dist/dex/pumpfun.d.ts +41 -0
  68. package/dist/dex/pumpfun.d.ts.map +1 -0
  69. package/dist/dex/pumpfun.js +555 -0
  70. package/dist/dex/pumpfun.js.map +1 -0
  71. package/dist/dex/raydium-amm-v4.d.ts +11 -0
  72. package/dist/dex/raydium-amm-v4.d.ts.map +1 -0
  73. package/dist/dex/raydium-amm-v4.js +649 -0
  74. package/dist/dex/raydium-amm-v4.js.map +1 -0
  75. package/dist/dex/raydium-clmm.d.ts +12 -0
  76. package/dist/dex/raydium-clmm.d.ts.map +1 -0
  77. package/dist/dex/raydium-clmm.js +675 -0
  78. package/dist/dex/raydium-clmm.js.map +1 -0
  79. package/dist/dex/raydium-cpmm.d.ts +10 -0
  80. package/dist/dex/raydium-cpmm.d.ts.map +1 -0
  81. package/dist/dex/raydium-cpmm.js +613 -0
  82. package/dist/dex/raydium-cpmm.js.map +1 -0
  83. package/dist/dex/raydium-launchlab.d.ts +12 -0
  84. package/dist/dex/raydium-launchlab.d.ts.map +1 -0
  85. package/dist/dex/raydium-launchlab.js +530 -0
  86. package/dist/dex/raydium-launchlab.js.map +1 -0
  87. package/dist/dex/shared/clmm-base.d.ts +58 -0
  88. package/dist/dex/shared/clmm-base.d.ts.map +1 -0
  89. package/dist/dex/shared/clmm-base.js +891 -0
  90. package/dist/dex/shared/clmm-base.js.map +1 -0
  91. package/dist/dex/types.d.ts +601 -0
  92. package/dist/dex/types.d.ts.map +1 -0
  93. package/dist/dex/types.js +137 -0
  94. package/dist/dex/types.js.map +1 -0
  95. package/dist/dexscreener/index.d.ts +2 -0
  96. package/dist/dexscreener/index.d.ts.map +1 -0
  97. package/dist/dexscreener/index.js +18 -0
  98. package/dist/dexscreener/index.js.map +1 -0
  99. package/dist/dexscreener/info.d.ts +22 -0
  100. package/dist/dexscreener/info.d.ts.map +1 -0
  101. package/dist/dexscreener/info.js +104 -0
  102. package/dist/dexscreener/info.js.map +1 -0
  103. package/dist/helpers/check_balance.d.ts +10 -0
  104. package/dist/helpers/check_balance.d.ts.map +1 -0
  105. package/dist/helpers/check_balance.js +34 -0
  106. package/dist/helpers/check_balance.js.map +1 -0
  107. package/dist/helpers/config.d.ts +51 -0
  108. package/dist/helpers/config.d.ts.map +1 -0
  109. package/dist/helpers/config.js +118 -0
  110. package/dist/helpers/config.js.map +1 -0
  111. package/dist/helpers/index.d.ts +8 -0
  112. package/dist/helpers/index.d.ts.map +1 -0
  113. package/dist/helpers/index.js +29 -0
  114. package/dist/helpers/index.js.map +1 -0
  115. package/dist/helpers/logger.d.ts +27 -0
  116. package/dist/helpers/logger.d.ts.map +1 -0
  117. package/dist/helpers/logger.js +39 -0
  118. package/dist/helpers/logger.js.map +1 -0
  119. package/dist/helpers/token-2022.d.ts +32 -0
  120. package/dist/helpers/token-2022.d.ts.map +1 -0
  121. package/dist/helpers/token-2022.js +48 -0
  122. package/dist/helpers/token-2022.js.map +1 -0
  123. package/dist/helpers/unwrap_sol.d.ts +2 -0
  124. package/dist/helpers/unwrap_sol.d.ts.map +1 -0
  125. package/dist/helpers/unwrap_sol.js +67 -0
  126. package/dist/helpers/unwrap_sol.js.map +1 -0
  127. package/dist/helpers/util.d.ts +698 -0
  128. package/dist/helpers/util.d.ts.map +1 -0
  129. package/dist/helpers/util.js +181 -0
  130. package/dist/helpers/util.js.map +1 -0
  131. package/dist/helpers/utils.d.ts +10 -0
  132. package/dist/helpers/utils.d.ts.map +1 -0
  133. package/dist/helpers/utils.js +97 -0
  134. package/dist/helpers/utils.js.map +1 -0
  135. package/dist/helpers/wrap_sol.d.ts +3 -0
  136. package/dist/helpers/wrap_sol.d.ts.map +1 -0
  137. package/dist/helpers/wrap_sol.js +88 -0
  138. package/dist/helpers/wrap_sol.js.map +1 -0
  139. package/dist/index.d.ts +14 -0
  140. package/dist/index.d.ts.map +1 -0
  141. package/dist/index.js +32 -0
  142. package/dist/index.js.map +1 -0
  143. package/dist/transactions/bloXroute_tips_tx_executor.d.ts +4 -0
  144. package/dist/transactions/bloXroute_tips_tx_executor.d.ts.map +1 -0
  145. package/dist/transactions/bloXroute_tips_tx_executor.js +70 -0
  146. package/dist/transactions/bloXroute_tips_tx_executor.js.map +1 -0
  147. package/dist/transactions/index.d.ts +6 -0
  148. package/dist/transactions/index.d.ts.map +1 -0
  149. package/dist/transactions/index.js +30 -0
  150. package/dist/transactions/index.js.map +1 -0
  151. package/dist/transactions/jito_tips_tx_executor.d.ts +15 -0
  152. package/dist/transactions/jito_tips_tx_executor.d.ts.map +1 -0
  153. package/dist/transactions/jito_tips_tx_executor.js +99 -0
  154. package/dist/transactions/jito_tips_tx_executor.js.map +1 -0
  155. package/dist/transactions/landing/index.d.ts +30 -0
  156. package/dist/transactions/landing/index.d.ts.map +1 -0
  157. package/dist/transactions/landing/index.js +60 -0
  158. package/dist/transactions/landing/index.js.map +1 -0
  159. package/dist/transactions/landing/nonce-manager.d.ts +116 -0
  160. package/dist/transactions/landing/nonce-manager.d.ts.map +1 -0
  161. package/dist/transactions/landing/nonce-manager.js +393 -0
  162. package/dist/transactions/landing/nonce-manager.js.map +1 -0
  163. package/dist/transactions/landing/orchestrator.d.ts +104 -0
  164. package/dist/transactions/landing/orchestrator.d.ts.map +1 -0
  165. package/dist/transactions/landing/orchestrator.js +329 -0
  166. package/dist/transactions/landing/orchestrator.js.map +1 -0
  167. package/dist/transactions/landing/providers/astralane.d.ts +12 -0
  168. package/dist/transactions/landing/providers/astralane.d.ts.map +1 -0
  169. package/dist/transactions/landing/providers/astralane.js +132 -0
  170. package/dist/transactions/landing/providers/astralane.js.map +1 -0
  171. package/dist/transactions/landing/providers/blockrazor.d.ts +11 -0
  172. package/dist/transactions/landing/providers/blockrazor.d.ts.map +1 -0
  173. package/dist/transactions/landing/providers/blockrazor.js +134 -0
  174. package/dist/transactions/landing/providers/blockrazor.js.map +1 -0
  175. package/dist/transactions/landing/providers/bloxroute.d.ts +12 -0
  176. package/dist/transactions/landing/providers/bloxroute.d.ts.map +1 -0
  177. package/dist/transactions/landing/providers/bloxroute.js +102 -0
  178. package/dist/transactions/landing/providers/bloxroute.js.map +1 -0
  179. package/dist/transactions/landing/providers/flashblock.d.ts +10 -0
  180. package/dist/transactions/landing/providers/flashblock.d.ts.map +1 -0
  181. package/dist/transactions/landing/providers/flashblock.js +102 -0
  182. package/dist/transactions/landing/providers/flashblock.js.map +1 -0
  183. package/dist/transactions/landing/providers/helius-sender.d.ts +11 -0
  184. package/dist/transactions/landing/providers/helius-sender.d.ts.map +1 -0
  185. package/dist/transactions/landing/providers/helius-sender.js +101 -0
  186. package/dist/transactions/landing/providers/helius-sender.js.map +1 -0
  187. package/dist/transactions/landing/providers/jito.d.ts +16 -0
  188. package/dist/transactions/landing/providers/jito.d.ts.map +1 -0
  189. package/dist/transactions/landing/providers/jito.js +110 -0
  190. package/dist/transactions/landing/providers/jito.js.map +1 -0
  191. package/dist/transactions/landing/providers/nextblock.d.ts +11 -0
  192. package/dist/transactions/landing/providers/nextblock.d.ts.map +1 -0
  193. package/dist/transactions/landing/providers/nextblock.js +109 -0
  194. package/dist/transactions/landing/providers/nextblock.js.map +1 -0
  195. package/dist/transactions/landing/providers/node1.d.ts +11 -0
  196. package/dist/transactions/landing/providers/node1.d.ts.map +1 -0
  197. package/dist/transactions/landing/providers/node1.js +101 -0
  198. package/dist/transactions/landing/providers/node1.js.map +1 -0
  199. package/dist/transactions/landing/providers/nozomi.d.ts +11 -0
  200. package/dist/transactions/landing/providers/nozomi.d.ts.map +1 -0
  201. package/dist/transactions/landing/providers/nozomi.js +124 -0
  202. package/dist/transactions/landing/providers/nozomi.js.map +1 -0
  203. package/dist/transactions/landing/providers/soyas.d.ts +16 -0
  204. package/dist/transactions/landing/providers/soyas.d.ts.map +1 -0
  205. package/dist/transactions/landing/providers/soyas.js +192 -0
  206. package/dist/transactions/landing/providers/soyas.js.map +1 -0
  207. package/dist/transactions/landing/providers/stellium.d.ts +11 -0
  208. package/dist/transactions/landing/providers/stellium.d.ts.map +1 -0
  209. package/dist/transactions/landing/providers/stellium.js +102 -0
  210. package/dist/transactions/landing/providers/stellium.js.map +1 -0
  211. package/dist/transactions/landing/providers/zero-slot.d.ts +10 -0
  212. package/dist/transactions/landing/providers/zero-slot.d.ts.map +1 -0
  213. package/dist/transactions/landing/providers/zero-slot.js +92 -0
  214. package/dist/transactions/landing/providers/zero-slot.js.map +1 -0
  215. package/dist/transactions/landing/tip-accounts.d.ts +22 -0
  216. package/dist/transactions/landing/tip-accounts.d.ts.map +1 -0
  217. package/dist/transactions/landing/tip-accounts.js +140 -0
  218. package/dist/transactions/landing/tip-accounts.js.map +1 -0
  219. package/dist/transactions/landing/types.d.ts +98 -0
  220. package/dist/transactions/landing/types.d.ts.map +1 -0
  221. package/dist/transactions/landing/types.js +30 -0
  222. package/dist/transactions/landing/types.js.map +1 -0
  223. package/dist/transactions/nozomi/tx-submission.d.ts +14 -0
  224. package/dist/transactions/nozomi/tx-submission.d.ts.map +1 -0
  225. package/dist/transactions/nozomi/tx-submission.js +107 -0
  226. package/dist/transactions/nozomi/tx-submission.js.map +1 -0
  227. package/dist/transactions/send-rpc.d.ts +54 -0
  228. package/dist/transactions/send-rpc.d.ts.map +1 -0
  229. package/dist/transactions/send-rpc.js +126 -0
  230. package/dist/transactions/send-rpc.js.map +1 -0
  231. package/dist/transactions/simple_tx_executor.d.ts +10 -0
  232. package/dist/transactions/simple_tx_executor.d.ts.map +1 -0
  233. package/dist/transactions/simple_tx_executor.js +33 -0
  234. package/dist/transactions/simple_tx_executor.js.map +1 -0
  235. package/package.json +112 -0
package/dist/cli.js ADDED
@@ -0,0 +1,1251 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ // Suppress bigint-buffer native binding warning (pure JS fallback works fine)
38
+ // bigint-buffer@1.1.5 doesn't have Node 22+ bindings and prints to stderr on load
39
+ const _origWarn = process.stderr.write.bind(process.stderr);
40
+ process.stderr.write = (chunk, ...args) => {
41
+ if (typeof chunk === "string" && chunk.includes("bigint: Failed to load bindings"))
42
+ return true;
43
+ return _origWarn(chunk, ...args);
44
+ };
45
+ /**
46
+ * outsmart CLI — The Solana trading command-line interface.
47
+ *
48
+ * 18 DEX adapters, 12 TX landing providers, one unified interface.
49
+ *
50
+ * Usage:
51
+ * # On-chain DEX — pool address required, token auto-detected from pool
52
+ * outsmart buy --dex meteora-dlmm --pool <POOL> --amount 0.1
53
+ * outsmart sell --dex meteora-dlmm --pool <POOL> --pct 100
54
+ *
55
+ * # Stablecoin pool — auto-swaps SOL→USD1 then buys, no extra steps needed
56
+ * outsmart buy --dex raydium-launchlab --pool <POOL> --amount 0.1
57
+ *
58
+ * # Swap aggregator — requires token mint only (finds best route automatically)
59
+ * outsmart buy --dex jupiter-ultra --token <MINT> --amount 0.1
60
+ * outsmart sell --dex jupiter-ultra --token <MINT> --pct 100
61
+ *
62
+ * outsmart quote --dex meteora-dlmm --pool <POOL>
63
+ * outsmart list-dex
64
+ * outsmart list-dex --cap canSell
65
+ * outsmart config show
66
+ * outsmart init
67
+ */
68
+ require("dotenv/config");
69
+ const commander_1 = require("commander");
70
+ // ---------------------------------------------------------------------------
71
+ // Side-effect imports — trigger adapter self-registration
72
+ // ---------------------------------------------------------------------------
73
+ require("./dex/raydium-amm-v4");
74
+ require("./dex/raydium-cpmm");
75
+ require("./dex/raydium-clmm");
76
+ require("./dex/raydium-launchlab");
77
+ require("./dex/meteora-damm-v1");
78
+ require("./dex/meteora-damm-v2");
79
+ require("./dex/meteora-dlmm");
80
+ require("./dex/meteora-dbc");
81
+ require("./dex/orca");
82
+ require("./dex/byreal-clmm");
83
+ require("./dex/pancakeswap-clmm");
84
+ require("./dex/fusion-amm");
85
+ require("./dex/futarchy-amm");
86
+ require("./dex/futarchy-launchpad");
87
+ require("./dex/pumpfun");
88
+ require("./dex/pumpfun-amm");
89
+ require("./dex/jupiter-ultra");
90
+ require("./dex/dflow");
91
+ // ---------------------------------------------------------------------------
92
+ // Internal imports
93
+ // ---------------------------------------------------------------------------
94
+ const dex_1 = require("./dex");
95
+ const types_1 = require("./dex/types");
96
+ // ---------------------------------------------------------------------------
97
+ // Version from package.json
98
+ // ---------------------------------------------------------------------------
99
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
100
+ const pkg = require("../package.json");
101
+ // ---------------------------------------------------------------------------
102
+ // Helpers
103
+ // ---------------------------------------------------------------------------
104
+ function die(msg) {
105
+ console.error(`\n error: ${msg}\n`);
106
+ process.exit(1);
107
+ }
108
+ /** Base58 character set (no 0, O, I, l) */
109
+ const BASE58_CHARS = /^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$/;
110
+ /**
111
+ * Validate that a string is a valid Solana base58 address.
112
+ * Checks length (32-44 chars), character set, and PublicKey construction.
113
+ */
114
+ function validateBase58(value, flag) {
115
+ if (value.length < 32 || value.length > 44) {
116
+ die(`Invalid ${flag}: "${value}" is not a valid Solana address (must be 32-44 characters, got ${value.length})`);
117
+ }
118
+ if (!BASE58_CHARS.test(value)) {
119
+ die(`Invalid ${flag}: "${value}" is not a valid Solana address (contains invalid base58 characters)`);
120
+ }
121
+ try {
122
+ // Dynamic import would be async; use require for synchronous validation
123
+ // PublicKey is already used elsewhere in this file via dynamic import,
124
+ // but for a sync helper we use require.
125
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
126
+ const { PublicKey } = require("@solana/web3.js");
127
+ new PublicKey(value);
128
+ }
129
+ catch {
130
+ die(`Invalid ${flag}: "${value}" is not a valid Solana address`);
131
+ }
132
+ }
133
+ /**
134
+ * Validate that a DEX name is registered in the adapter registry.
135
+ * Shows available adapters if the name is not found.
136
+ */
137
+ function validateDex(dexName) {
138
+ const registry = (0, dex_1.getRegistry)();
139
+ if (!registry.has(dexName)) {
140
+ const available = registry.getNames();
141
+ die(`Unknown --dex "${dexName}". Available adapters:\n` +
142
+ available.map((n) => ` ${n}`).join("\n"));
143
+ }
144
+ }
145
+ function printResult(result) {
146
+ console.log();
147
+ console.log(` dex: ${result.dex}`);
148
+ console.log(` tx: ${result.txSignature}`);
149
+ console.log(` confirmed: ${result.confirmed}`);
150
+ console.log(` in: ${result.amountIn} ${result.amountInToken}`);
151
+ if (result.amountOut != null) {
152
+ console.log(` out: ${result.amountOut} ${result.amountOutToken ?? ""}`);
153
+ }
154
+ if (result.poolAddress) {
155
+ console.log(` pool: ${result.poolAddress}`);
156
+ }
157
+ if (result.priceImpactPct != null) {
158
+ console.log(` impact: ${result.priceImpactPct.toFixed(2)}%`);
159
+ }
160
+ console.log();
161
+ }
162
+ /** Friendly label for a stablecoin mint */
163
+ function stablecoinLabel(mint) {
164
+ if (mint === types_1.USDC_MINT)
165
+ return "USDC";
166
+ if (mint === types_1.USDT_MINT)
167
+ return "USDT";
168
+ if (mint === types_1.USD1_MINT)
169
+ return "USD1";
170
+ return mint.slice(0, 8) + "...";
171
+ }
172
+ /**
173
+ * Resolve the token mint and quote mint from pool state.
174
+ *
175
+ * Calls adapter.getPrice(pool) which decodes the pool account and returns
176
+ * baseMint + quoteMint. We determine which is the "token" (what the user
177
+ * wants to buy/sell) and which is the "quote" (SOL or stablecoin).
178
+ *
179
+ * Priority: SOL > USDC/USDT/USD1 > error (ambiguous).
180
+ */
181
+ async function resolvePool(adapter, poolAddress) {
182
+ if (!adapter.capabilities.canGetPrice || !adapter.getPrice) {
183
+ die(`${adapter.name} cannot auto-detect token from pool — please provide --token <mint>`);
184
+ }
185
+ const price = await adapter.getPrice(poolAddress);
186
+ const { baseMint, quoteMint } = price;
187
+ // SOL as quote — most common
188
+ if (quoteMint === dex_1.WSOL_MINT)
189
+ return { tokenMint: baseMint, quoteMint, price };
190
+ if (baseMint === dex_1.WSOL_MINT)
191
+ return { tokenMint: quoteMint, quoteMint: baseMint, price };
192
+ // Stablecoin as quote
193
+ if (types_1.STABLECOIN_MINTS.has(quoteMint))
194
+ return { tokenMint: baseMint, quoteMint, price };
195
+ if (types_1.STABLECOIN_MINTS.has(baseMint))
196
+ return { tokenMint: quoteMint, quoteMint: baseMint, price };
197
+ // Neither side is SOL or stablecoin — ambiguous
198
+ die(`Pool ${poolAddress} has no SOL or stablecoin side (${baseMint} / ${quoteMint}).\n`
199
+ + ` Please specify --token <mint> to indicate which token to trade.`);
200
+ }
201
+ /**
202
+ * Get the SPL token balance for a mint in the user's wallet.
203
+ */
204
+ async function getTokenBalance(mint) {
205
+ const { getConnection, getWallet } = await Promise.resolve().then(() => __importStar(require("./helpers/config")));
206
+ const { PublicKey } = await Promise.resolve().then(() => __importStar(require("@solana/web3.js")));
207
+ const { getAssociatedTokenAddress } = await Promise.resolve().then(() => __importStar(require("@solana/spl-token")));
208
+ const connection = getConnection();
209
+ const wallet = getWallet();
210
+ const mintPk = new PublicKey(mint);
211
+ try {
212
+ const ata = await getAssociatedTokenAddress(mintPk, wallet.publicKey);
213
+ const res = await connection.getTokenAccountBalance(ata);
214
+ return {
215
+ amount: Number(res.value.uiAmount ?? 0),
216
+ raw: BigInt(res.value.amount),
217
+ decimals: res.value.decimals,
218
+ };
219
+ }
220
+ catch {
221
+ return { amount: 0, raw: 0n, decimals: 0 };
222
+ }
223
+ }
224
+ /**
225
+ * Check if Jupiter Ultra API is available (JUPITER_API_KEY is set).
226
+ */
227
+ function hasJupiterApiKey() {
228
+ return !!process.env.JUPITER_API_KEY;
229
+ }
230
+ /**
231
+ * Auto-swap SOL → stablecoin. Uses jupiter-ultra if JUPITER_API_KEY is set,
232
+ * otherwise falls back to on-chain DEX adapters from SOL_STABLECOIN_POOLS.
233
+ * Returns the stablecoin amount received.
234
+ */
235
+ async function autoSwapSolToStablecoin(stablecoinMint, amountSol) {
236
+ const label = stablecoinLabel(stablecoinMint);
237
+ // Snapshot balance BEFORE swap so we return only the delta
238
+ const balanceBefore = await getTokenBalance(stablecoinMint);
239
+ if (hasJupiterApiKey()) {
240
+ // --- Jupiter Ultra path ---
241
+ console.log(`\n step 1: swapping ${amountSol} SOL → ${label} via jupiter-ultra...`);
242
+ const jupAdapter = (0, dex_1.getDexAdapter)("jupiter-ultra");
243
+ const result = await jupAdapter.buy({
244
+ tokenMint: stablecoinMint,
245
+ amountSol,
246
+ });
247
+ if (!result.txSignature) {
248
+ die(`Failed to swap SOL → ${label}: no transaction signature returned`);
249
+ }
250
+ console.log(` ✓ tx: ${result.txSignature}`);
251
+ if (result.confirmed)
252
+ console.log(` ✓ confirmed`);
253
+ }
254
+ else {
255
+ // --- On-chain fallback path ---
256
+ const pools = types_1.SOL_STABLECOIN_POOLS[stablecoinMint];
257
+ if (!pools || pools.length === 0) {
258
+ die(`No on-chain SOL/${label} pools configured and JUPITER_API_KEY is not set.`);
259
+ }
260
+ let swapped = false;
261
+ for (const entry of pools) {
262
+ try {
263
+ const adapter = (0, dex_1.getDexAdapter)(entry.dex);
264
+ console.log(`\n step 1: swapping ${amountSol} SOL → ${label} via ${entry.dex} (pool ${entry.pool.slice(0, 8)}...)...`);
265
+ const result = await adapter.buy({
266
+ tokenMint: stablecoinMint,
267
+ amountSol,
268
+ poolAddress: entry.pool,
269
+ quoteMint: dex_1.WSOL_MINT,
270
+ });
271
+ if (!result.txSignature) {
272
+ console.log(` ✗ no tx signature, trying next pool...`);
273
+ continue;
274
+ }
275
+ console.log(` ✓ tx: ${result.txSignature}`);
276
+ if (result.confirmed)
277
+ console.log(` ✓ confirmed`);
278
+ swapped = true;
279
+ break;
280
+ }
281
+ catch (err) {
282
+ console.log(` ✗ ${entry.dex} failed: ${err.message ?? err}. Trying next pool...`);
283
+ }
284
+ }
285
+ if (!swapped) {
286
+ die(`All on-chain SOL/${label} pools failed. Set JUPITER_API_KEY for jupiter-ultra fallback,\n`
287
+ + ` or check your SOL balance and RPC connection.`);
288
+ }
289
+ }
290
+ // Wait for balance to settle, then compute delta (only the swapped amount)
291
+ await new Promise((r) => setTimeout(r, 2000));
292
+ const balanceAfter = await getTokenBalance(stablecoinMint);
293
+ const received = balanceAfter.amount - balanceBefore.amount;
294
+ console.log(` ✓ received: ${received.toFixed(6)} ${label} (wallet total: ${balanceAfter.amount} ${label})`);
295
+ if (received <= 0) {
296
+ die(`SOL → ${label} swap TX landed but received 0 ${label}. TX may have failed on-chain.`);
297
+ }
298
+ return received;
299
+ }
300
+ /**
301
+ * Auto-swap stablecoin → SOL after a sell. Uses jupiter-ultra if JUPITER_API_KEY
302
+ * is set, otherwise falls back to on-chain DEX adapters.
303
+ */
304
+ async function autoSwapStablecoinToSol(stablecoinMint) {
305
+ const balance = await getTokenBalance(stablecoinMint);
306
+ if (balance.amount === 0)
307
+ return;
308
+ const label = stablecoinLabel(stablecoinMint);
309
+ if (hasJupiterApiKey()) {
310
+ // --- Jupiter Ultra path ---
311
+ console.log(`\n step 2: swapping ${balance.amount} ${label} → SOL via jupiter-ultra...`);
312
+ const jupAdapter = (0, dex_1.getDexAdapter)("jupiter-ultra");
313
+ const result = await jupAdapter.sell({
314
+ tokenMint: stablecoinMint,
315
+ percentage: 100,
316
+ });
317
+ if (result.txSignature) {
318
+ console.log(` ✓ tx: ${result.txSignature}`);
319
+ if (result.confirmed)
320
+ console.log(` ✓ confirmed`);
321
+ }
322
+ }
323
+ else {
324
+ // --- On-chain fallback path ---
325
+ const pools = types_1.SOL_STABLECOIN_POOLS[stablecoinMint];
326
+ if (!pools || pools.length === 0) {
327
+ console.log(`\n ⚠ No on-chain ${label}/SOL pools configured and JUPITER_API_KEY is not set. ${label} remains in wallet.`);
328
+ return;
329
+ }
330
+ for (const entry of pools) {
331
+ try {
332
+ const adapter = (0, dex_1.getDexAdapter)(entry.dex);
333
+ console.log(`\n step 2: swapping ${balance.amount} ${label} → SOL via ${entry.dex} (pool ${entry.pool.slice(0, 8)}...)...`);
334
+ const result = await adapter.sell({
335
+ tokenMint: stablecoinMint,
336
+ percentage: 100,
337
+ poolAddress: entry.pool,
338
+ quoteMint: dex_1.WSOL_MINT,
339
+ });
340
+ if (result.txSignature) {
341
+ console.log(` ✓ tx: ${result.txSignature}`);
342
+ if (result.confirmed)
343
+ console.log(` ✓ confirmed`);
344
+ return;
345
+ }
346
+ console.log(` ✗ no tx signature, trying next pool...`);
347
+ }
348
+ catch (err) {
349
+ console.log(` ✗ ${entry.dex} failed: ${err.message ?? err}. Trying next pool...`);
350
+ }
351
+ }
352
+ console.log(`\n ⚠ All on-chain ${label}/SOL pools failed. ${label} remains in wallet.`);
353
+ }
354
+ }
355
+ function buildSwapOpts(cmd) {
356
+ const opts = {};
357
+ if (cmd.slippage != null)
358
+ opts.slippageBps = Number(cmd.slippage);
359
+ if (cmd.priority != null)
360
+ opts.priorityFeeMicroLamports = Number(cmd.priority);
361
+ if (cmd.tip != null)
362
+ opts.tipSol = Number(cmd.tip);
363
+ if (cmd.cu != null)
364
+ opts.computeUnitLimit = Number(cmd.cu);
365
+ if (cmd.jito)
366
+ opts.useJito = true;
367
+ if (cmd.strategy != null) {
368
+ opts.landingStrategy = cmd.strategy;
369
+ }
370
+ return opts;
371
+ }
372
+ /**
373
+ * Shared option definitions for swap commands.
374
+ * @param includeTip - whether to add --tip (false for snipe, which has it as required)
375
+ */
376
+ function addSwapOptions(cmd, includeTip = true) {
377
+ cmd
378
+ .option("--slippage <bps>", `slippage tolerance in basis points (default: ${dex_1.DEFAULT_SLIPPAGE_BPS})`)
379
+ .option("--priority <microLamports>", "priority fee in microLamports per CU")
380
+ .option("--cu <units>", "compute unit limit")
381
+ .option("--jito", "use Jito bundle submission")
382
+ .option("--strategy <mode>", "TX landing strategy: concurrent|race|random|sequential")
383
+ .option("--quote <mint>", "quote token mint (default: WSOL)");
384
+ if (includeTip) {
385
+ cmd.option("--tip <sol>", "MEV tip in SOL");
386
+ }
387
+ return cmd;
388
+ }
389
+ // ---------------------------------------------------------------------------
390
+ // Program
391
+ // ---------------------------------------------------------------------------
392
+ const program = new commander_1.Command()
393
+ .name("outsmart")
394
+ .description("The Solana trading CLI — 18 DEX adapters, 12 TX landing providers.")
395
+ .version(pkg.version);
396
+ // ---------------------------------------------------------------------------
397
+ // outsmart buy
398
+ // ---------------------------------------------------------------------------
399
+ const buyCmd = new commander_1.Command("buy")
400
+ .description("Buy tokens with SOL (or quote token)")
401
+ .requiredOption("-d, --dex <name>", "DEX adapter name (e.g. raydium-cpmm, jupiter-ultra)")
402
+ .requiredOption("-a, --amount <sol>", "amount of SOL to spend")
403
+ .option("-p, --pool <address>", "pool address (required for on-chain DEXes)")
404
+ .option("-t, --token <mint>", "token mint address to buy")
405
+ .action(async (cmdOpts) => {
406
+ // --- Input sanitization ---
407
+ validateDex(cmdOpts.dex);
408
+ if (cmdOpts.pool)
409
+ validateBase58(cmdOpts.pool, "--pool");
410
+ if (cmdOpts.token)
411
+ validateBase58(cmdOpts.token, "--token");
412
+ if (cmdOpts.quote)
413
+ validateBase58(cmdOpts.quote, "--quote");
414
+ const adapter = (0, dex_1.getDexAdapter)(cmdOpts.dex);
415
+ if (!adapter.capabilities.canBuy) {
416
+ die(`${adapter.name} does not support buy`);
417
+ }
418
+ // Validate inputs based on adapter type
419
+ if (adapter.capabilities.isAggregator) {
420
+ // Aggregators (jupiter-ultra, dflow): need --token, no --pool needed
421
+ if (!cmdOpts.token) {
422
+ die(`${adapter.name} is a swap aggregator — --token <mint> is required.\n Example: outsmart buy --dex ${adapter.name} --token <MINT> --amount 0.1`);
423
+ }
424
+ }
425
+ else {
426
+ // On-chain DEXes: need --pool (--token is optional, auto-resolved from pool)
427
+ if (!cmdOpts.pool) {
428
+ die(`${adapter.name} is an on-chain DEX — --pool <address> is required.\n Example: outsmart buy --dex ${adapter.name} --pool <POOL> --amount 0.1`);
429
+ }
430
+ }
431
+ // Auto-resolve token + quote from pool state
432
+ let tokenMint = cmdOpts.token;
433
+ let quoteMint = cmdOpts.quote;
434
+ let amountToSpend = Number(cmdOpts.amount);
435
+ if (isNaN(amountToSpend) || amountToSpend <= 0) {
436
+ die(`Invalid --amount: ${cmdOpts.amount}. Must be a positive number.`);
437
+ }
438
+ if (!tokenMint && cmdOpts.pool) {
439
+ const resolved = await resolvePool(adapter, cmdOpts.pool);
440
+ tokenMint = resolved.tokenMint;
441
+ quoteMint = quoteMint ?? resolved.quoteMint;
442
+ console.log(` auto-detected token: ${tokenMint}`);
443
+ // If the quote is a stablecoin (not SOL), auto-swap SOL → stablecoin first
444
+ if (resolved.quoteMint !== dex_1.WSOL_MINT && types_1.STABLECOIN_MINTS.has(resolved.quoteMint)) {
445
+ const label = stablecoinLabel(resolved.quoteMint);
446
+ console.log(` pool quote: ${label} (not SOL)`);
447
+ quoteMint = resolved.quoteMint;
448
+ // Check existing balance
449
+ const existingBalance = await getTokenBalance(resolved.quoteMint);
450
+ if (existingBalance.amount > 0) {
451
+ console.log(` wallet has ${existingBalance.amount} ${label}`);
452
+ }
453
+ // Swap SOL → stablecoin, then use full stablecoin balance for the buy
454
+ amountToSpend = await autoSwapSolToStablecoin(resolved.quoteMint, amountToSpend);
455
+ }
456
+ }
457
+ const params = {
458
+ tokenMint,
459
+ amountSol: amountToSpend,
460
+ poolAddress: cmdOpts.pool,
461
+ quoteMint,
462
+ opts: buildSwapOpts(cmdOpts),
463
+ };
464
+ const isStablecoinQuote = quoteMint && types_1.STABLECOIN_MINTS.has(quoteMint);
465
+ const stepLabel = isStablecoinQuote ? "step 2: " : "";
466
+ console.log(`\n ${stepLabel}buying on ${adapter.name}...`);
467
+ const result = await adapter.buy(params);
468
+ printResult(result);
469
+ });
470
+ addSwapOptions(buyCmd);
471
+ program.addCommand(buyCmd);
472
+ // ---------------------------------------------------------------------------
473
+ // outsmart sell
474
+ // ---------------------------------------------------------------------------
475
+ const sellCmd = new commander_1.Command("sell")
476
+ .description("Sell tokens for SOL (or quote token)")
477
+ .requiredOption("-d, --dex <name>", "DEX adapter name")
478
+ .requiredOption("--pct <percentage>", "percentage of held balance to sell (0-100)")
479
+ .option("-p, --pool <address>", "pool address (required for on-chain DEXes)")
480
+ .option("-t, --token <mint>", "token mint address to sell")
481
+ .action(async (cmdOpts) => {
482
+ // --- Input sanitization ---
483
+ validateDex(cmdOpts.dex);
484
+ if (cmdOpts.pool)
485
+ validateBase58(cmdOpts.pool, "--pool");
486
+ if (cmdOpts.token)
487
+ validateBase58(cmdOpts.token, "--token");
488
+ if (cmdOpts.quote)
489
+ validateBase58(cmdOpts.quote, "--quote");
490
+ const adapter = (0, dex_1.getDexAdapter)(cmdOpts.dex);
491
+ if (!adapter.capabilities.canSell) {
492
+ die(`${adapter.name} does not support sell`);
493
+ }
494
+ // Validate inputs based on adapter type
495
+ if (adapter.capabilities.isAggregator) {
496
+ // Aggregators (jupiter-ultra, dflow): need --token, no --pool needed
497
+ if (!cmdOpts.token) {
498
+ die(`${adapter.name} is a swap aggregator — --token <mint> is required.\n Example: outsmart sell --dex ${adapter.name} --token <MINT> --pct 100`);
499
+ }
500
+ }
501
+ else {
502
+ // On-chain DEXes: need --pool (--token is optional, auto-resolved from pool)
503
+ if (!cmdOpts.pool) {
504
+ die(`${adapter.name} is an on-chain DEX — --pool <address> is required.\n Example: outsmart sell --dex ${adapter.name} --pool <POOL> --pct 100`);
505
+ }
506
+ }
507
+ const pct = Number(cmdOpts.pct);
508
+ if (isNaN(pct) || pct <= 0 || pct > 100) {
509
+ die(`Invalid --pct: ${cmdOpts.pct}. Must be between 1 and 100.`);
510
+ }
511
+ // Auto-resolve token + quote from pool state
512
+ let tokenMint = cmdOpts.token;
513
+ let quoteMint = cmdOpts.quote;
514
+ let isStablecoinQuote = false;
515
+ if (!tokenMint && cmdOpts.pool) {
516
+ const resolved = await resolvePool(adapter, cmdOpts.pool);
517
+ tokenMint = resolved.tokenMint;
518
+ quoteMint = quoteMint ?? resolved.quoteMint;
519
+ console.log(` auto-detected token: ${tokenMint}`);
520
+ if (resolved.quoteMint !== dex_1.WSOL_MINT && types_1.STABLECOIN_MINTS.has(resolved.quoteMint)) {
521
+ isStablecoinQuote = true;
522
+ const label = stablecoinLabel(resolved.quoteMint);
523
+ console.log(` pool quote: ${label} (will auto-convert to SOL after sell)`);
524
+ }
525
+ }
526
+ const params = {
527
+ tokenMint,
528
+ percentage: pct,
529
+ poolAddress: cmdOpts.pool,
530
+ quoteMint,
531
+ opts: buildSwapOpts(cmdOpts),
532
+ };
533
+ const stepLabel = isStablecoinQuote ? "step 1: " : "";
534
+ console.log(`\n ${stepLabel}selling ${params.percentage}% on ${adapter.name}...`);
535
+ const result = await adapter.sell(params);
536
+ printResult(result);
537
+ // Auto-swap stablecoin proceeds → SOL
538
+ if (isStablecoinQuote && quoteMint && result.txSignature) {
539
+ await autoSwapStablecoinToSol(quoteMint);
540
+ }
541
+ });
542
+ addSwapOptions(sellCmd);
543
+ program.addCommand(sellCmd);
544
+ // ---------------------------------------------------------------------------
545
+ // outsmart snipe — NOT YET IMPLEMENTED
546
+ //
547
+ // Real sniping requires a gRPC (Geyser/Yellowstone) listener that monitors
548
+ // pool creation events in real time. When a new pool is created where the
549
+ // base or quote token matches the target, it fires an instant buy through
550
+ // concurrent multi-provider TX landing.
551
+ //
552
+ // This needs the user's own Geyser gRPC key and runs as a background
553
+ // process (cronjob/tmux). Will be added when gRPC integration is built.
554
+ //
555
+ // For now, use `outsmart buy --pool <POOL> --tip <SOL>` to execute a
556
+ // competitive buy on a known pool.
557
+ // ---------------------------------------------------------------------------
558
+ // ---------------------------------------------------------------------------
559
+ // outsmart quote
560
+ // ---------------------------------------------------------------------------
561
+ program
562
+ .command("quote")
563
+ .description("Get the current on-chain price from a pool")
564
+ .requiredOption("-d, --dex <name>", "DEX adapter name")
565
+ .requiredOption("-p, --pool <address>", "pool address")
566
+ .action(async (cmdOpts) => {
567
+ const adapter = (0, dex_1.getDexAdapter)(cmdOpts.dex);
568
+ if (!adapter.capabilities.canGetPrice) {
569
+ die(`${adapter.name} does not support getPrice`);
570
+ }
571
+ if (!adapter.getPrice) {
572
+ die(`${adapter.name} declares canGetPrice but has no getPrice() implementation`);
573
+ }
574
+ const price = await adapter.getPrice(cmdOpts.pool);
575
+ console.log();
576
+ console.log(` dex: ${adapter.name}`);
577
+ console.log(` pool: ${price.poolAddress}`);
578
+ console.log(` price: ${price.price}`);
579
+ console.log(` base: ${price.baseMint}`);
580
+ console.log(` quote: ${price.quoteMint}`);
581
+ console.log(` source: ${price.source}`);
582
+ console.log(` time: ${new Date(price.timestamp).toISOString()}`);
583
+ console.log();
584
+ });
585
+ // ---------------------------------------------------------------------------
586
+ // outsmart find-pool
587
+ // ---------------------------------------------------------------------------
588
+ program
589
+ .command("find-pool")
590
+ .description("Discover a pool for a token pair on a specific DEX")
591
+ .requiredOption("-d, --dex <name>", "DEX adapter name")
592
+ .requiredOption("-t, --token <mint>", "base token mint address")
593
+ .option("--quote <mint>", "quote token mint (default: WSOL)")
594
+ .action(async (cmdOpts) => {
595
+ const adapter = (0, dex_1.getDexAdapter)(cmdOpts.dex);
596
+ if (!adapter.capabilities.canFindPool) {
597
+ die(`${adapter.name} does not support findPool`);
598
+ }
599
+ if (!adapter.findPool) {
600
+ die(`${adapter.name} declares canFindPool but has no findPool() implementation`);
601
+ }
602
+ const pool = await adapter.findPool(cmdOpts.token, cmdOpts.quote);
603
+ if (!pool) {
604
+ console.log(`\n No pool found for ${cmdOpts.token} on ${adapter.name}\n`);
605
+ process.exit(1);
606
+ }
607
+ console.log();
608
+ console.log(` dex: ${pool.dex}`);
609
+ console.log(` protocol: ${pool.protocol}`);
610
+ console.log(` pool: ${pool.address}`);
611
+ console.log(` base: ${pool.baseMint} (${pool.baseDecimals} decimals)`);
612
+ console.log(` quote: ${pool.quoteMint} (${pool.quoteDecimals} decimals)`);
613
+ if (pool.liquidity != null) {
614
+ console.log(` liquidity: $${pool.liquidity.toLocaleString()}`);
615
+ }
616
+ if (pool.price != null) {
617
+ console.log(` price: ${pool.price}`);
618
+ }
619
+ console.log();
620
+ });
621
+ // ---------------------------------------------------------------------------
622
+ // outsmart add-liq
623
+ // ---------------------------------------------------------------------------
624
+ program
625
+ .command("add-liq")
626
+ .description("Add liquidity to a pool")
627
+ .requiredOption("-d, --dex <name>", "DEX adapter name (e.g. meteora-dlmm)")
628
+ .requiredOption("-p, --pool <address>", "pool address")
629
+ .option("--amount-sol <amount>", "amount of SOL to deposit")
630
+ .option("--amount-token <amount>", "amount of non-SOL token to deposit")
631
+ .option("-t, --token <mint>", "token mint (for single-sided token deposits)")
632
+ .option("--strategy <type>", "distribution strategy: spot|curve|bid-ask (default: spot)")
633
+ .option("--bins <count>", "number of bins to spread across (default: 50, max: 70)")
634
+ .option("--amount-a <amount>", "amount of token A (legacy, use --amount-sol instead)")
635
+ .option("--amount-b <amount>", "amount of token B (legacy, use --amount-token instead)")
636
+ .option("--slippage <bps>", "slippage tolerance in basis points")
637
+ .option("--priority <microLamports>", "priority fee in microLamports per CU")
638
+ .option("--tip <sol>", "MEV tip in SOL")
639
+ .action(async (cmdOpts) => {
640
+ const adapter = (0, dex_1.getDexAdapter)(cmdOpts.dex);
641
+ if (!adapter.capabilities.canAddLiquidity) {
642
+ die(`${adapter.name} does not support addLiquidity`);
643
+ }
644
+ if (!adapter.addLiquidity) {
645
+ die(`${adapter.name} declares canAddLiquidity but has no addLiquidity() implementation`);
646
+ }
647
+ // Validate: at least one amount must be provided
648
+ const hasAmountSol = cmdOpts.amountSol != null;
649
+ const hasAmountToken = cmdOpts.amountToken != null;
650
+ const hasLegacyA = cmdOpts.amountA != null;
651
+ const hasLegacyB = cmdOpts.amountB != null;
652
+ if (!hasAmountSol && !hasAmountToken && !hasLegacyA && !hasLegacyB) {
653
+ die("At least one of --amount-sol or --amount-token must be provided.\n"
654
+ + " Examples:\n"
655
+ + ` outsmart add-liq --dex ${adapter.name} --pool <POOL> --amount-sol 0.5\n`
656
+ + ` outsmart add-liq --dex ${adapter.name} --pool <POOL> --amount-token 1000 --token <MINT>\n`
657
+ + ` outsmart add-liq --dex ${adapter.name} --pool <POOL> --amount-sol 0.5 --amount-token 1000`);
658
+ }
659
+ // Validate strategy
660
+ const validStrategies = ["spot", "curve", "bid-ask"];
661
+ if (cmdOpts.strategy && !validStrategies.includes(cmdOpts.strategy)) {
662
+ die(`Invalid strategy "${cmdOpts.strategy}". Must be one of: ${validStrategies.join(", ")}`);
663
+ }
664
+ const params = {
665
+ poolAddress: cmdOpts.pool,
666
+ amountSol: hasAmountSol ? Number(cmdOpts.amountSol) : undefined,
667
+ amountToken: hasAmountToken ? Number(cmdOpts.amountToken) : undefined,
668
+ tokenMint: cmdOpts.token,
669
+ strategy: cmdOpts.strategy,
670
+ bins: cmdOpts.bins != null ? Number(cmdOpts.bins) : undefined,
671
+ amountA: hasLegacyA ? Number(cmdOpts.amountA) : undefined,
672
+ amountB: hasLegacyB ? Number(cmdOpts.amountB) : undefined,
673
+ opts: buildSwapOpts(cmdOpts),
674
+ };
675
+ const mode = params.amountSol && params.amountToken
676
+ ? "balanced" : params.amountSol ? "one-sided SOL" : "one-sided token";
677
+ console.log(`\n adding ${mode} liquidity on ${adapter.name} (pool: ${params.poolAddress})...`);
678
+ if (cmdOpts.strategy)
679
+ console.log(` strategy: ${cmdOpts.strategy}`);
680
+ if (cmdOpts.bins)
681
+ console.log(` bins: ${cmdOpts.bins}`);
682
+ const result = await adapter.addLiquidity(params);
683
+ console.log();
684
+ console.log(` tx: ${result.txSignature}`);
685
+ console.log(` confirmed: ${result.confirmed}`);
686
+ if (result.positionAddress) {
687
+ console.log(` position: ${result.positionAddress}`);
688
+ }
689
+ if (result.error) {
690
+ console.log(` error: ${result.error}`);
691
+ }
692
+ console.log();
693
+ });
694
+ // ---------------------------------------------------------------------------
695
+ // outsmart remove-liq
696
+ // ---------------------------------------------------------------------------
697
+ program
698
+ .command("remove-liq")
699
+ .description("Remove liquidity from a pool")
700
+ .requiredOption("-d, --dex <name>", "DEX adapter name (e.g. meteora-dlmm)")
701
+ .requiredOption("-p, --pool <address>", "pool address")
702
+ .requiredOption("--pct <percentage>", "percentage of LP position to remove (0-100)")
703
+ .option("--position <address>", "specific position address to remove from (default: first found)")
704
+ .option("--slippage <bps>", "slippage tolerance in basis points")
705
+ .option("--priority <microLamports>", "priority fee in microLamports per CU")
706
+ .option("--tip <sol>", "MEV tip in SOL")
707
+ .action(async (cmdOpts) => {
708
+ const adapter = (0, dex_1.getDexAdapter)(cmdOpts.dex);
709
+ if (!adapter.capabilities.canRemoveLiquidity) {
710
+ die(`${adapter.name} does not support removeLiquidity`);
711
+ }
712
+ if (!adapter.removeLiquidity) {
713
+ die(`${adapter.name} declares canRemoveLiquidity but has no removeLiquidity() implementation`);
714
+ }
715
+ const params = {
716
+ poolAddress: cmdOpts.pool,
717
+ percentage: Number(cmdOpts.pct),
718
+ positionAddress: cmdOpts.position,
719
+ opts: buildSwapOpts(cmdOpts),
720
+ };
721
+ console.log(`\n removing ${params.percentage}% liquidity on ${adapter.name} (pool: ${params.poolAddress})...`);
722
+ if (params.positionAddress) {
723
+ console.log(` position: ${params.positionAddress}`);
724
+ }
725
+ const result = await adapter.removeLiquidity(params);
726
+ console.log();
727
+ console.log(` tx: ${result.txSignature}`);
728
+ console.log(` confirmed: ${result.confirmed}`);
729
+ if (result.positionAddress) {
730
+ console.log(` position: ${result.positionAddress}`);
731
+ }
732
+ if (result.error) {
733
+ console.log(` error: ${result.error}`);
734
+ }
735
+ console.log();
736
+ });
737
+ // ---------------------------------------------------------------------------
738
+ // outsmart claim-fees
739
+ // ---------------------------------------------------------------------------
740
+ program
741
+ .command("claim-fees")
742
+ .description("Claim accumulated swap fees from LP positions")
743
+ .requiredOption("-d, --dex <name>", "DEX adapter name (e.g. meteora-dlmm)")
744
+ .requiredOption("-p, --pool <address>", "pool address")
745
+ .option("--position <address>", "specific position address to claim from (default: first found)")
746
+ .action(async (cmdOpts) => {
747
+ const adapter = (0, dex_1.getDexAdapter)(cmdOpts.dex);
748
+ if (!adapter.capabilities.canClaimFees) {
749
+ die(`${adapter.name} does not support claimFees`);
750
+ }
751
+ if (!adapter.claimFees) {
752
+ die(`${adapter.name} declares canClaimFees but has no claimFees() implementation`);
753
+ }
754
+ console.log(`\n claiming fees on ${adapter.name} (pool: ${cmdOpts.pool})...`);
755
+ const result = await adapter.claimFees(cmdOpts.pool, cmdOpts.position);
756
+ console.log();
757
+ console.log(` tx: ${result.txSignature}`);
758
+ console.log(` confirmed: ${result.confirmed}`);
759
+ if (result.positionAddress) {
760
+ console.log(` position: ${result.positionAddress}`);
761
+ }
762
+ if (result.error) {
763
+ console.log(` error: ${result.error}`);
764
+ }
765
+ console.log();
766
+ });
767
+ // ---------------------------------------------------------------------------
768
+ // outsmart positions
769
+ // ---------------------------------------------------------------------------
770
+ program
771
+ .command("positions")
772
+ .description("List LP positions in a pool")
773
+ .requiredOption("-d, --dex <name>", "DEX adapter name (e.g. meteora-dlmm)")
774
+ .requiredOption("-p, --pool <address>", "pool address")
775
+ .option("--json", "output as JSON")
776
+ .action(async (cmdOpts) => {
777
+ const adapter = (0, dex_1.getDexAdapter)(cmdOpts.dex);
778
+ if (!adapter.capabilities.canListPositions) {
779
+ die(`${adapter.name} does not support listPositions`);
780
+ }
781
+ if (!adapter.listPositions) {
782
+ die(`${adapter.name} declares canListPositions but has no listPositions() implementation`);
783
+ }
784
+ const positions = await adapter.listPositions(cmdOpts.pool);
785
+ if (cmdOpts.json) {
786
+ console.log(JSON.stringify(positions, null, 2));
787
+ return;
788
+ }
789
+ if (positions.length === 0) {
790
+ console.log(`\n No positions found in pool ${cmdOpts.pool}\n`);
791
+ return;
792
+ }
793
+ console.log(`\n ${positions.length} position(s) in pool ${cmdOpts.pool}:\n`);
794
+ for (const pos of positions) {
795
+ console.log(` position: ${pos.positionAddress}`);
796
+ console.log(` bins: ${pos.lowerBinId} → ${pos.upperBinId}`);
797
+ console.log(` in-range: ${pos.inRange}`);
798
+ console.log(` tokenX: ${pos.amountX} (${pos.tokenXMint})`);
799
+ console.log(` tokenY: ${pos.amountY} (${pos.tokenYMint})`);
800
+ console.log(` feeX: ${pos.feeX}`);
801
+ console.log(` feeY: ${pos.feeY}`);
802
+ console.log();
803
+ }
804
+ });
805
+ // ---------------------------------------------------------------------------
806
+ // outsmart create-pump-coin (PumpFun bonding curve)
807
+ // ---------------------------------------------------------------------------
808
+ program
809
+ .command("create-pump-coin")
810
+ .description("Create a new PumpFun token with a bonding curve")
811
+ .requiredOption("--name <name>", "token name")
812
+ .requiredOption("--symbol <symbol>", "token symbol")
813
+ .requiredOption("--uri <uri>", "metadata URI (IPFS link to JSON metadata)")
814
+ .action(async (cmdOpts) => {
815
+ const adapter = (0, dex_1.getDexAdapter)("pumpfun");
816
+ console.log(`\n creating token "${cmdOpts.name}" (${cmdOpts.symbol}) on pump.fun...`);
817
+ const result = await adapter.create(cmdOpts.name, cmdOpts.symbol, cmdOpts.uri);
818
+ console.log();
819
+ console.log(` tx: ${result.txSignature}`);
820
+ console.log(` confirmed: ${result.confirmed}`);
821
+ if (result.positionAddress) {
822
+ console.log(` mint: ${result.positionAddress}`);
823
+ }
824
+ if (result.poolAddress) {
825
+ console.log(` curve: ${result.poolAddress}`);
826
+ }
827
+ if (result.error) {
828
+ console.log(` error: ${result.error}`);
829
+ }
830
+ console.log();
831
+ });
832
+ // ---------------------------------------------------------------------------
833
+ // outsmart create-pool (PumpSwap AMM)
834
+ // ---------------------------------------------------------------------------
835
+ program
836
+ .command("create-pool")
837
+ .description("Create a new PumpSwap AMM pool with initial liquidity")
838
+ .requiredOption("--base <mint>", "base token mint address")
839
+ .requiredOption("--quote <mint>", "quote token mint address (usually WSOL)")
840
+ .requiredOption("--base-amount <amount>", "initial base token deposit (human-readable)")
841
+ .requiredOption("--quote-amount <amount>", "initial quote token deposit (human-readable)")
842
+ .option("--index <number>", "pool index (default: 1; 0 is reserved for canonical pump pools)", "1")
843
+ .action(async (cmdOpts) => {
844
+ const adapter = (0, dex_1.getDexAdapter)("pumpfun-amm");
845
+ console.log(`\n creating pool on PumpSwap AMM...`);
846
+ console.log(` base: ${cmdOpts.base}`);
847
+ console.log(` quote: ${cmdOpts.quote}`);
848
+ console.log(` amounts: ${cmdOpts.baseAmount} base + ${cmdOpts.quoteAmount} quote`);
849
+ console.log(` index: ${cmdOpts.index}`);
850
+ const result = await adapter.createPool(cmdOpts.base, cmdOpts.quote, Number(cmdOpts.baseAmount), Number(cmdOpts.quoteAmount), Number(cmdOpts.index));
851
+ console.log();
852
+ console.log(` tx: ${result.txSignature}`);
853
+ console.log(` confirmed: ${result.confirmed}`);
854
+ if (result.poolAddress) {
855
+ console.log(` pool: ${result.poolAddress}`);
856
+ }
857
+ if (result.error) {
858
+ console.log(` error: ${result.error}`);
859
+ }
860
+ console.log();
861
+ });
862
+ // ---------------------------------------------------------------------------
863
+ // outsmart create-damm-pool (DAMM v2 custom pool — full fee config)
864
+ // ---------------------------------------------------------------------------
865
+ program
866
+ .command("create-damm-pool")
867
+ .description("Create a Meteora DAMM v2 custom pool with full fee configuration")
868
+ .requiredOption("--base <mint>", "base token mint address")
869
+ .requiredOption("--base-amount <amount>", "initial base token deposit (human-readable)")
870
+ .requiredOption("--quote-amount <amount>", "initial quote token deposit (human-readable)")
871
+ .option("--quote <mint>", "quote token mint (default: WSOL)")
872
+ .option("--price <number>", "initial price in quote/base units (default: quoteAmount / baseAmount)")
873
+ .option("--max-fee <bps>", "max base fee in bps, charged at activation (default: 9900)", "9900")
874
+ .option("--min-fee <bps>", "min base fee in bps, reached after decay (default: 200)", "200")
875
+ .option("--periods <n>", "number of fee decay periods (default: 1440)", "1440")
876
+ .option("--duration <secs>", "total fee decay duration in seconds (default: 86400)", "86400")
877
+ .option("--fee-mode <0|1>", "fee scheduler: 0=linear, 1=exponential (default: 0)", "0")
878
+ .option("--dynamic-fee", "enable dynamic fee on top of base fee")
879
+ .option("--collect-mode <0|1>", "fee collection: 0=both tokens, 1=quote only (default: 1)", "1")
880
+ .option("--activation <timestamp>", "activation unix timestamp (default: immediate)")
881
+ .option("--alpha-vault", "create alpha vault after pool")
882
+ .option("--priority <microLamports>", "priority fee in microLamports per CU")
883
+ .option("--cu <units>", "compute unit limit")
884
+ .action(async (cmdOpts) => {
885
+ const adapter = (0, dex_1.getDexAdapter)("meteora-damm-v2");
886
+ const params = {
887
+ baseMint: cmdOpts.base,
888
+ quoteMint: cmdOpts.quote,
889
+ baseAmount: Number(cmdOpts.baseAmount),
890
+ quoteAmount: Number(cmdOpts.quoteAmount),
891
+ initPrice: cmdOpts.price ? Number(cmdOpts.price) : undefined,
892
+ poolFees: {
893
+ maxBaseFeeBps: Number(cmdOpts.maxFee),
894
+ minBaseFeeBps: Number(cmdOpts.minFee),
895
+ numberOfPeriod: Number(cmdOpts.periods),
896
+ totalDuration: Number(cmdOpts.duration),
897
+ feeSchedulerMode: Number(cmdOpts.feeMode),
898
+ useDynamicFee: !!cmdOpts.dynamicFee,
899
+ dynamicFeeConfig: null,
900
+ },
901
+ collectFeeMode: Number(cmdOpts.collectMode),
902
+ activationType: 1, // timestamp
903
+ activationPoint: cmdOpts.activation ? Number(cmdOpts.activation) : null,
904
+ hasAlphaVault: !!cmdOpts.alphaVault,
905
+ opts: buildSwapOpts(cmdOpts),
906
+ };
907
+ console.log(`\n creating DAMM v2 custom pool...`);
908
+ console.log(` base: ${cmdOpts.base}`);
909
+ console.log(` quote: ${cmdOpts.quote ?? "WSOL"}`);
910
+ console.log(` amounts: ${cmdOpts.baseAmount} base + ${cmdOpts.quoteAmount} quote`);
911
+ console.log(` fees: ${cmdOpts.maxFee} → ${cmdOpts.minFee} bps (${cmdOpts.feeMode === "1" ? "exponential" : "linear"})`);
912
+ const result = await adapter.createCustomPool(params);
913
+ console.log();
914
+ console.log(` tx: ${result.txSignature}`);
915
+ console.log(` confirmed: ${result.confirmed}`);
916
+ if (result.poolAddress)
917
+ console.log(` pool: ${result.poolAddress}`);
918
+ if (result.positionAddress)
919
+ console.log(` position: ${result.positionAddress}`);
920
+ if (result.error)
921
+ console.log(` error: ${result.error}`);
922
+ console.log();
923
+ });
924
+ // ---------------------------------------------------------------------------
925
+ // outsmart create-damm-config-pool (DAMM v2 config-based pool)
926
+ // ---------------------------------------------------------------------------
927
+ program
928
+ .command("create-damm-config-pool")
929
+ .description("Create a Meteora DAMM v2 pool using an existing config")
930
+ .requiredOption("--base <mint>", "base token mint address")
931
+ .requiredOption("--base-amount <amount>", "initial base token deposit (human-readable)")
932
+ .requiredOption("--quote-amount <amount>", "initial quote token deposit (human-readable)")
933
+ .requiredOption("--config <address>", "on-chain config address")
934
+ .option("--quote <mint>", "quote token mint (default: WSOL)")
935
+ .option("--price <number>", "initial price in quote/base units (default: quoteAmount / baseAmount)")
936
+ .option("--activation <timestamp>", "activation unix timestamp (default: immediate)")
937
+ .option("--lock", "permanently lock the initial liquidity")
938
+ .option("--priority <microLamports>", "priority fee in microLamports per CU")
939
+ .option("--cu <units>", "compute unit limit")
940
+ .action(async (cmdOpts) => {
941
+ const adapter = (0, dex_1.getDexAdapter)("meteora-damm-v2");
942
+ const params = {
943
+ baseMint: cmdOpts.base,
944
+ quoteMint: cmdOpts.quote,
945
+ baseAmount: Number(cmdOpts.baseAmount),
946
+ quoteAmount: Number(cmdOpts.quoteAmount),
947
+ initPrice: cmdOpts.price ? Number(cmdOpts.price) : undefined,
948
+ configAddress: cmdOpts.config,
949
+ activationPoint: cmdOpts.activation ? Number(cmdOpts.activation) : null,
950
+ lockLiquidity: !!cmdOpts.lock,
951
+ opts: buildSwapOpts(cmdOpts),
952
+ };
953
+ console.log(`\n creating DAMM v2 config-based pool...`);
954
+ console.log(` base: ${cmdOpts.base}`);
955
+ console.log(` quote: ${cmdOpts.quote ?? "WSOL"}`);
956
+ console.log(` config: ${cmdOpts.config}`);
957
+ console.log(` amounts: ${cmdOpts.baseAmount} base + ${cmdOpts.quoteAmount} quote`);
958
+ const result = await adapter.createConfigPool(params);
959
+ console.log();
960
+ console.log(` tx: ${result.txSignature}`);
961
+ console.log(` confirmed: ${result.confirmed}`);
962
+ if (result.poolAddress)
963
+ console.log(` pool: ${result.poolAddress}`);
964
+ if (result.positionAddress)
965
+ console.log(` position: ${result.positionAddress}`);
966
+ if (result.error)
967
+ console.log(` error: ${result.error}`);
968
+ console.log();
969
+ });
970
+ // ---------------------------------------------------------------------------
971
+ // outsmart list-dex
972
+ // ---------------------------------------------------------------------------
973
+ program
974
+ .command("list-dex")
975
+ .description("List all registered DEX adapters")
976
+ .option("--cap <capability>", "filter by capability (e.g. canBuy, canSell, canSnipe)")
977
+ .option("--json", "output as JSON")
978
+ .action((cmdOpts) => {
979
+ let adapters = (0, dex_1.listDexAdapters)();
980
+ if (cmdOpts.cap) {
981
+ const cap = cmdOpts.cap;
982
+ adapters = adapters.filter((a) => a.capabilities[cap]);
983
+ }
984
+ if (cmdOpts.json) {
985
+ console.log(JSON.stringify(adapters, null, 2));
986
+ return;
987
+ }
988
+ console.log();
989
+ console.log(` ${adapters.length} DEX adapter(s) registered:\n`);
990
+ // Table header
991
+ const nameWidth = 22;
992
+ const protoWidth = 14;
993
+ console.log(` ${"NAME".padEnd(nameWidth)}${"PROTOCOL".padEnd(protoWidth)}CAPABILITIES`);
994
+ console.log(` ${"─".repeat(nameWidth)}${"─".repeat(protoWidth)}${"─".repeat(40)}`);
995
+ for (const a of adapters) {
996
+ const caps = Object.entries(a.capabilities)
997
+ .filter(([, v]) => v)
998
+ .map(([k]) => k.replace("can", "").toLowerCase())
999
+ .join(", ");
1000
+ console.log(` ${a.name.padEnd(nameWidth)}${a.protocol.padEnd(protoWidth)}${caps}`);
1001
+ }
1002
+ console.log();
1003
+ });
1004
+ // ---------------------------------------------------------------------------
1005
+ // outsmart config
1006
+ // ---------------------------------------------------------------------------
1007
+ const configCmd = new commander_1.Command("config")
1008
+ .description("View or update outsmart configuration");
1009
+ configCmd
1010
+ .command("show")
1011
+ .description("Show current configuration from environment")
1012
+ .action(() => {
1013
+ const envVars = [
1014
+ "SOLANA_RPC_URL",
1015
+ "RPC_URL",
1016
+ "HELIUS_API_KEY",
1017
+ "JITO_API_KEY",
1018
+ "BLOXROUTE_AUTH_HEADER",
1019
+ "NOZOMI_API_KEY",
1020
+ "BLOCKRAZOR_API_KEY",
1021
+ "NEXTBLOCK_API_KEY",
1022
+ "ZERO_SLOT_API_KEY",
1023
+ "SOYAS_API_KEY",
1024
+ "ASTRALANE_API_KEY",
1025
+ "STELLIUM_API_KEY",
1026
+ "FLASHBLOCK_API_KEY",
1027
+ "NODE1_API_KEY",
1028
+ "TX_LANDING_MODE",
1029
+ "TX_LANDING_PROVIDERS",
1030
+ "DEFAULT_TIP_SOL",
1031
+ "DEFAULT_SLIPPAGE_BPS",
1032
+ "DEFAULT_PRIORITY_FEE",
1033
+ ];
1034
+ console.log();
1035
+ console.log(" outsmart configuration (from environment):\n");
1036
+ for (const key of envVars) {
1037
+ const val = process.env[key];
1038
+ if (val) {
1039
+ // Mask sensitive values
1040
+ const isSensitive = key.includes("KEY") || key.includes("AUTH") || key.includes("PRIVATE");
1041
+ const display = isSensitive ? val.slice(0, 6) + "..." + val.slice(-4) : val;
1042
+ console.log(` ${key.padEnd(28)} ${display}`);
1043
+ }
1044
+ else {
1045
+ console.log(` ${key.padEnd(28)} (not set)`);
1046
+ }
1047
+ }
1048
+ console.log();
1049
+ });
1050
+ configCmd
1051
+ .command("env")
1052
+ .description("Print a .env template with all supported variables")
1053
+ .action(() => {
1054
+ console.log(`# outsmart .env configuration
1055
+ # Copy this to .env in your project root
1056
+
1057
+ # ─── Solana RPC ───
1058
+ SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
1059
+ # RPC_URL= # fallback alias
1060
+
1061
+ # ─── Wallet ───
1062
+ # WALLET_PRIVATE_KEY= # base58 private key (or set WALLET_PATH)
1063
+ # WALLET_PATH= # path to keypair JSON file
1064
+
1065
+ # ─── TX Landing Providers (API keys) ───
1066
+ # HELIUS_API_KEY=
1067
+ # JITO_API_KEY=
1068
+ # BLOXROUTE_AUTH_HEADER=
1069
+ # NOZOMI_API_KEY=
1070
+ # BLOCKRAZOR_API_KEY=
1071
+ # NEXTBLOCK_API_KEY=
1072
+ # ZERO_SLOT_API_KEY=
1073
+ # SOYAS_API_KEY=
1074
+ # ASTRALANE_API_KEY=
1075
+ # STELLIUM_API_KEY=
1076
+ # FLASHBLOCK_API_KEY=
1077
+ # NODE1_API_KEY=
1078
+
1079
+ # ─── TX Landing Strategy ───
1080
+ # TX_LANDING_MODE=race # concurrent|race|random|sequential
1081
+ # TX_LANDING_PROVIDERS= # comma-separated provider names (empty = all enabled)
1082
+ # DEFAULT_TIP_SOL=0.001
1083
+
1084
+ # ─── Defaults ───
1085
+ # DEFAULT_SLIPPAGE_BPS=300 # 3%
1086
+ # DEFAULT_PRIORITY_FEE=4000 # microLamports per CU
1087
+ `);
1088
+ });
1089
+ program.addCommand(configCmd);
1090
+ // ---------------------------------------------------------------------------
1091
+ // outsmart init
1092
+ // ---------------------------------------------------------------------------
1093
+ program
1094
+ .command("init")
1095
+ .description("Set up outsmart — prompts for wallet key and RPC endpoint")
1096
+ .action(async () => {
1097
+ const readline = await Promise.resolve().then(() => __importStar(require("readline")));
1098
+ const fs = await Promise.resolve().then(() => __importStar(require("fs")));
1099
+ const path = await Promise.resolve().then(() => __importStar(require("path")));
1100
+ const rl = readline.createInterface({
1101
+ input: process.stdin,
1102
+ output: process.stdout,
1103
+ });
1104
+ const ask = (question) => new Promise((resolve) => rl.question(question, (answer) => resolve(answer.trim())));
1105
+ console.log();
1106
+ console.log(" outsmart init");
1107
+ console.log(" ─────────────");
1108
+ console.log();
1109
+ // Determine config location
1110
+ const homeDir = process.env.HOME || process.env.USERPROFILE || "~";
1111
+ const globalDir = path.join(homeDir, ".outsmart");
1112
+ const globalConfig = path.join(globalDir, "config.env");
1113
+ const localConfig = path.join(process.cwd(), ".env");
1114
+ // Check if local .env already exists
1115
+ const hasLocalEnv = fs.existsSync(localConfig);
1116
+ const hasGlobalConfig = fs.existsSync(globalConfig);
1117
+ if (hasLocalEnv) {
1118
+ console.log(` Found existing .env at ${localConfig}`);
1119
+ }
1120
+ if (hasGlobalConfig) {
1121
+ console.log(` Found existing config at ${globalConfig}`);
1122
+ }
1123
+ // Ask for required values
1124
+ const privateKey = await ask(" Wallet private key (base58): ");
1125
+ if (!privateKey) {
1126
+ console.log("\n Aborted — no private key provided.\n");
1127
+ rl.close();
1128
+ return;
1129
+ }
1130
+ // Validate the key
1131
+ try {
1132
+ const bs58Module = await Promise.resolve().then(() => __importStar(require("bs58")));
1133
+ const { Keypair } = await Promise.resolve().then(() => __importStar(require("@solana/web3.js")));
1134
+ const decoded = bs58Module.default.decode(privateKey);
1135
+ const kp = Keypair.fromSecretKey(decoded);
1136
+ console.log(` Wallet: ${kp.publicKey.toBase58()}`);
1137
+ }
1138
+ catch {
1139
+ console.log("\n Error: invalid private key. Must be base58-encoded.\n");
1140
+ rl.close();
1141
+ return;
1142
+ }
1143
+ const rpcUrl = await ask(" RPC endpoint (e.g. https://mainnet.helius-rpc.com/?api-key=...): ");
1144
+ if (!rpcUrl) {
1145
+ console.log("\n Aborted — no RPC endpoint provided.\n");
1146
+ rl.close();
1147
+ return;
1148
+ }
1149
+ // Build config content
1150
+ const configContent = [
1151
+ "# outsmart configuration",
1152
+ `# Generated by outsmart init on ${new Date().toISOString()}`,
1153
+ "",
1154
+ "# ─── Required ───",
1155
+ `PRIVATE_KEY=${privateKey}`,
1156
+ `MAINNET_ENDPOINT=${rpcUrl}`,
1157
+ "",
1158
+ "# ─── TX Landing Providers (optional) ───",
1159
+ "# HELIUS_API_KEY=",
1160
+ "# JITO_API_KEY=",
1161
+ "# BLOXROUTE_AUTH_HEADER=",
1162
+ "# NOZOMI_API_KEY=",
1163
+ "# BLOCKRAZOR_API_KEY=",
1164
+ "# NEXTBLOCK_API_KEY=",
1165
+ "# ZERO_SLOT_API_KEY=",
1166
+ "# SOYAS_API_KEY=",
1167
+ "# ASTRALANE_API_KEY=",
1168
+ "# STELLIUM_API_KEY=",
1169
+ "# FLASHBLOCK_API_KEY=",
1170
+ "# NODE1_API_KEY=",
1171
+ "",
1172
+ "# ─── Trading Defaults (optional) ───",
1173
+ "# TX_LANDING_MODE=concurrent",
1174
+ "# DEFAULT_TIP_SOL=0.001",
1175
+ "# DEFAULT_SLIPPAGE_BPS=300",
1176
+ "# DEFAULT_PRIORITY_FEE=4000",
1177
+ "",
1178
+ ].join("\n");
1179
+ // Write to global config (~/.outsmart/config.env)
1180
+ if (!fs.existsSync(globalDir)) {
1181
+ fs.mkdirSync(globalDir, { recursive: true });
1182
+ }
1183
+ fs.writeFileSync(globalConfig, configContent, { mode: 0o600 });
1184
+ console.log(`\n Config written to ${globalConfig}`);
1185
+ console.log(" (file permissions set to owner-only read/write)");
1186
+ // Also write local .env if we're in a project directory
1187
+ const hasPkgJson = fs.existsSync(path.join(process.cwd(), "package.json"));
1188
+ if (hasPkgJson && !hasLocalEnv) {
1189
+ fs.writeFileSync(localConfig, configContent, { mode: 0o600 });
1190
+ console.log(` Also written to ${localConfig}`);
1191
+ }
1192
+ console.log();
1193
+ console.log(" You're ready to trade:");
1194
+ console.log(" outsmart buy --dex raydium-cpmm --pool <POOL> --amount 0.1");
1195
+ console.log(" outsmart buy --dex jupiter-ultra --token <MINT> --amount 0.1");
1196
+ console.log(" outsmart list-dex");
1197
+ console.log();
1198
+ rl.close();
1199
+ });
1200
+ // ---------------------------------------------------------------------------
1201
+ // outsmart info
1202
+ // ---------------------------------------------------------------------------
1203
+ program
1204
+ .command("info")
1205
+ .description("Show token info from DexScreener")
1206
+ .requiredOption("-t, --token <mint>", "token mint address")
1207
+ .action(async (cmdOpts) => {
1208
+ const { getInfoFromDexscreener } = await Promise.resolve().then(() => __importStar(require("./dexscreener/info")));
1209
+ const info = await getInfoFromDexscreener(cmdOpts.token);
1210
+ console.log();
1211
+ console.log(` name: ${info.name}`);
1212
+ console.log(` address: ${info.address}`);
1213
+ console.log(` price: $${info.priceInUSD}`);
1214
+ console.log(` mcap: $${Number(info.marketCap).toLocaleString()}`);
1215
+ console.log(` age: ${info.pairAge}`);
1216
+ console.log(` liq (SOL): ${info.liquidityInSOL}`);
1217
+ console.log(` pool: ${info.poolId}`);
1218
+ console.log();
1219
+ console.log(` vol 5m/1h/6h/24h: ${info.volume5m} / ${info.volume1h} / ${info.volume6h} / ${info.volume24h}`);
1220
+ console.log(` buyers 5m/1h/6h/24h: ${info.buyers5m} / ${info.buyers1h} / ${info.buyers6h} / ${info.buyers24h}`);
1221
+ console.log();
1222
+ if (info.dexscreenerURL)
1223
+ console.log(` dexscreener: ${info.dexscreenerURL}`);
1224
+ if (info.twitterURL)
1225
+ console.log(` twitter: ${info.twitterURL}`);
1226
+ if (info.telegramURL)
1227
+ console.log(` telegram: ${info.telegramURL}`);
1228
+ if (info.websiteURL)
1229
+ console.log(` website: ${info.websiteURL}`);
1230
+ console.log();
1231
+ });
1232
+ // ---------------------------------------------------------------------------
1233
+ // Graceful shutdown — prevents hanging on Ctrl+C during long RPC calls
1234
+ // ---------------------------------------------------------------------------
1235
+ for (const signal of ["SIGINT", "SIGTERM"]) {
1236
+ process.on(signal, () => {
1237
+ console.log("\nShutting down...");
1238
+ process.exit(0);
1239
+ });
1240
+ }
1241
+ // ---------------------------------------------------------------------------
1242
+ // Parse & run
1243
+ // ---------------------------------------------------------------------------
1244
+ program.parseAsync(process.argv).catch((err) => {
1245
+ console.error(`\n fatal: ${err.message}\n`);
1246
+ if (process.env.DEBUG) {
1247
+ console.error(err.stack);
1248
+ }
1249
+ process.exit(1);
1250
+ });
1251
+ //# sourceMappingURL=cli.js.map