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
@@ -0,0 +1,891 @@
1
+ "use strict";
2
+ /**
3
+ * Shared CLMM (Concentrated Liquidity Market Maker) Base
4
+ *
5
+ * This module provides the common CLMM swap logic shared by:
6
+ * - byreal-clmm (program: REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2)
7
+ * - pancakeswap-clmm (program: HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq)
8
+ *
9
+ * Both are forks of the Raydium CLMM program with identical:
10
+ * - Account layout (pool state, tick arrays, bitmap extension)
11
+ * - Instruction format (swap_v2 discriminator + args)
12
+ * - PDA derivation seeds
13
+ *
14
+ * The ONLY difference is the program ID. This base module is parameterized
15
+ * by programId so each adapter just passes its own.
16
+ *
17
+ * Source: 100x-algo-bots/trading-modules/byreal-clmm/ and pancakeswap-clmm/
18
+ */
19
+ var __importDefault = (this && this.__importDefault) || function (mod) {
20
+ return (mod && mod.__esModule) ? mod : { "default": mod };
21
+ };
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.ClmmBaseAdapter = void 0;
24
+ const web3_js_1 = require("@solana/web3.js");
25
+ const spl_token_1 = require("@solana/spl-token");
26
+ const bn_js_1 = __importDefault(require("bn.js"));
27
+ const decimal_js_1 = __importDefault(require("decimal.js"));
28
+ const raydium_sdk_v2_1 = require("@raydium-io/raydium-sdk-v2");
29
+ const config_1 = require("../../helpers/config");
30
+ const landing_1 = require("../../transactions/landing");
31
+ const send_rpc_1 = require("../../transactions/send-rpc");
32
+ const types_1 = require("../types");
33
+ // ---------------------------------------------------------------------------
34
+ // Constants
35
+ // ---------------------------------------------------------------------------
36
+ const USD1_MINT_STR = "USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB";
37
+ const MEMO_PROGRAM_ID = new web3_js_1.PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr");
38
+ const SIX_DECIMAL_MINTS = new Set([types_1.USDC_MINT, types_1.USDT_MINT, USD1_MINT_STR]);
39
+ /** swap_v2 discriminator — identical across Raydium/Byreal/PancakeSwap CLMM forks */
40
+ const SWAP_V2_DISCRIMINATOR = Buffer.from([43, 4, 237, 11, 26, 201, 30, 98]);
41
+ /** Tick array constants */
42
+ const TICK_ARRAY_SIZE = 60;
43
+ const TICK_ARRAY_BITMAP_SIZE = 512;
44
+ const MAX_TICK = 443636;
45
+ const MIN_TICK = -443636;
46
+ const MIN_SQRT_PRICE_X64 = new bn_js_1.default("4295048016");
47
+ const MAX_SQRT_PRICE_X64 = new bn_js_1.default("79226673515401279992447579055");
48
+ // Extension bitmap constants
49
+ const EXTENSION_TICKARRAY_BITMAP_SIZE = 14;
50
+ // ---------------------------------------------------------------------------
51
+ // PDA derivation — CLMM pool accounts
52
+ // ---------------------------------------------------------------------------
53
+ /**
54
+ * Convert i32 to 4-byte big-endian buffer.
55
+ * Note: Byreal & PancakeSwap CLMM forks use big-endian for tick array
56
+ * start index in PDA derivation (differs from Raydium CLMM which uses LE).
57
+ */
58
+ function i32ToBytes(num) {
59
+ const buf = Buffer.alloc(4);
60
+ buf.writeInt32BE(num, 0);
61
+ return buf;
62
+ }
63
+ function deriveTickArray(programId, poolId, startIndex) {
64
+ const [addr] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("tick_array"), poolId.toBuffer(), i32ToBytes(startIndex)], programId);
65
+ return addr;
66
+ }
67
+ function deriveTickArrayBitmapExtension(programId, poolId) {
68
+ const [addr] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("pool_tick_array_bitmap_extension"), poolId.toBuffer()], programId);
69
+ return addr;
70
+ }
71
+ function deriveObservationState(programId, poolId) {
72
+ const [addr] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("observation"), poolId.toBuffer()], programId);
73
+ return addr;
74
+ }
75
+ function decodePoolState(data) {
76
+ let offset = 8; // skip discriminator
77
+ // Bump [1 byte]
78
+ offset += 1;
79
+ // ammConfig [32 bytes]
80
+ const ammConfig = new web3_js_1.PublicKey(data.subarray(offset, offset + 32));
81
+ offset += 32;
82
+ // owner [32 bytes]
83
+ offset += 32;
84
+ // tokenMint0 [32 bytes]
85
+ const tokenMint0 = new web3_js_1.PublicKey(data.subarray(offset, offset + 32));
86
+ offset += 32;
87
+ // tokenMint1 [32 bytes]
88
+ const tokenMint1 = new web3_js_1.PublicKey(data.subarray(offset, offset + 32));
89
+ offset += 32;
90
+ // tokenVault0 [32 bytes]
91
+ const tokenVault0 = new web3_js_1.PublicKey(data.subarray(offset, offset + 32));
92
+ offset += 32;
93
+ // tokenVault1 [32 bytes]
94
+ const tokenVault1 = new web3_js_1.PublicKey(data.subarray(offset, offset + 32));
95
+ offset += 32;
96
+ // observationKey [32 bytes]
97
+ const observationKey = new web3_js_1.PublicKey(data.subarray(offset, offset + 32));
98
+ offset += 32;
99
+ // mintDecimals0 [1 byte]
100
+ const mintDecimals0 = data.readUInt8(offset);
101
+ offset += 1;
102
+ // mintDecimals1 [1 byte]
103
+ const mintDecimals1 = data.readUInt8(offset);
104
+ offset += 1;
105
+ // tickSpacing [2 bytes]
106
+ const tickSpacing = data.readUInt16LE(offset);
107
+ offset += 2;
108
+ // liquidity [16 bytes]
109
+ const liquidity = new bn_js_1.default(data.subarray(offset, offset + 16), "le");
110
+ offset += 16;
111
+ // sqrtPriceX64 [16 bytes]
112
+ const sqrtPriceX64 = new bn_js_1.default(data.subarray(offset, offset + 16), "le");
113
+ offset += 16;
114
+ // tickCurrent [4 bytes]
115
+ const tickCurrent = data.readInt32LE(offset);
116
+ offset += 4;
117
+ // padding [4 bytes]
118
+ offset += 4;
119
+ // feeGrowthGlobal0X64 [16]
120
+ offset += 16;
121
+ // feeGrowthGlobal1X64 [16]
122
+ offset += 16;
123
+ // protocolFeesToken0 [8]
124
+ offset += 8;
125
+ // protocolFeesToken1 [8]
126
+ offset += 8;
127
+ // swapInAmountToken0 [16]
128
+ offset += 16;
129
+ // swapOutAmountToken1 [16]
130
+ offset += 16;
131
+ // swapInAmountToken1 [16]
132
+ offset += 16;
133
+ // swapOutAmountToken0 [16]
134
+ offset += 16;
135
+ // status [1 byte]
136
+ offset += 1;
137
+ // padding [7 bytes]
138
+ offset += 7;
139
+ // rewardInfos [3 * 169 bytes = 507 bytes]
140
+ offset += 507;
141
+ // tickArrayBitmap [16 * u64 = 16 * 8 bytes = 128 bytes]
142
+ // Note: This is [u64; 16], NOT [u128; 16]. Each element is 8 bytes.
143
+ const tickArrayBitmap = [];
144
+ for (let i = 0; i < 16; i++) {
145
+ tickArrayBitmap.push(new bn_js_1.default(data.subarray(offset, offset + 8), "le"));
146
+ offset += 8;
147
+ }
148
+ return {
149
+ ammConfig,
150
+ tokenMint0,
151
+ tokenMint1,
152
+ tokenVault0,
153
+ tokenVault1,
154
+ observationKey,
155
+ mintDecimals0,
156
+ mintDecimals1,
157
+ tickSpacing,
158
+ sqrtPriceX64,
159
+ tickCurrent,
160
+ liquidity,
161
+ tickArrayBitmap,
162
+ };
163
+ }
164
+ async function fetchClmmPoolState(connection, poolId, programId) {
165
+ const accountInfo = await connection.getAccountInfo(poolId);
166
+ if (!accountInfo) {
167
+ throw new Error(`CLMM pool not found: ${poolId.toBase58()}`);
168
+ }
169
+ if (accountInfo.owner.toBase58() !== programId.toBase58()) {
170
+ throw new Error(`Pool ${poolId.toBase58()} owner ${accountInfo.owner.toBase58()} does not match expected program ${programId.toBase58()}`);
171
+ }
172
+ return decodePoolState(accountInfo.data);
173
+ }
174
+ async function getTickArrayBitmapExtension(programId, poolId, connection) {
175
+ const exBitmapAddress = deriveTickArrayBitmapExtension(programId, poolId);
176
+ const accountInfo = await connection.getAccountInfo(exBitmapAddress);
177
+ if (!accountInfo) {
178
+ // Graceful fallback — return empty bitmaps (pancakeswap pattern)
179
+ const emptyBitmaps = [];
180
+ for (let i = 0; i < EXTENSION_TICKARRAY_BITMAP_SIZE; i++) {
181
+ emptyBitmaps.push(Array(8).fill(new bn_js_1.default(0)));
182
+ }
183
+ return {
184
+ poolId,
185
+ exBitmapAddress,
186
+ exists: false,
187
+ positiveTickArrayBitmap: emptyBitmaps,
188
+ negativeTickArrayBitmap: emptyBitmaps,
189
+ };
190
+ }
191
+ const data = accountInfo.data;
192
+ let offset = 8 + 32; // discriminator + poolId
193
+ // positiveTickArrayBitmap [14 * 8 * 8 = 896 bytes]
194
+ const positiveTickArrayBitmap = [];
195
+ for (let i = 0; i < EXTENSION_TICKARRAY_BITMAP_SIZE; i++) {
196
+ const row = [];
197
+ for (let j = 0; j < 8; j++) {
198
+ row.push(new bn_js_1.default(data.subarray(offset, offset + 8), "le"));
199
+ offset += 8;
200
+ }
201
+ positiveTickArrayBitmap.push(row);
202
+ }
203
+ // negativeTickArrayBitmap [14 * 8 * 8 = 896 bytes]
204
+ const negativeTickArrayBitmap = [];
205
+ for (let i = 0; i < EXTENSION_TICKARRAY_BITMAP_SIZE; i++) {
206
+ const row = [];
207
+ for (let j = 0; j < 8; j++) {
208
+ row.push(new bn_js_1.default(data.subarray(offset, offset + 8), "le"));
209
+ offset += 8;
210
+ }
211
+ negativeTickArrayBitmap.push(row);
212
+ }
213
+ return {
214
+ poolId,
215
+ exBitmapAddress,
216
+ exists: true,
217
+ positiveTickArrayBitmap,
218
+ negativeTickArrayBitmap,
219
+ };
220
+ }
221
+ // ---------------------------------------------------------------------------
222
+ // Tick array bitmap search
223
+ // ---------------------------------------------------------------------------
224
+ function getNextTickArrayStartIndex(currentStartIndex, tickSpacing, zeroForOne) {
225
+ const ticksPerArray = tickSpacing * TICK_ARRAY_SIZE;
226
+ return zeroForOne
227
+ ? currentStartIndex - ticksPerArray
228
+ : currentStartIndex + ticksPerArray;
229
+ }
230
+ function getTickArrayStartIndexForTick(tick, tickSpacing) {
231
+ const ticksPerArray = tickSpacing * TICK_ARRAY_SIZE;
232
+ let startIndex = Math.floor(tick / ticksPerArray) * ticksPerArray;
233
+ if (tick < 0 && tick % ticksPerArray !== 0) {
234
+ startIndex -= ticksPerArray;
235
+ }
236
+ return startIndex;
237
+ }
238
+ /**
239
+ * Merge an array of u64 BNs into a single large BN.
240
+ * Matches the reference SDK: b = sum(bns[i] << (64 * i))
241
+ */
242
+ function mergeTickArrayBitmapToSingleBN(bns) {
243
+ let b = new bn_js_1.default(0);
244
+ for (let i = 0; i < bns.length; i++) {
245
+ b = b.add(bns[i].shln(64 * i));
246
+ }
247
+ return b;
248
+ }
249
+ /**
250
+ * Check if a tick array is initialized using the pool's default bitmap.
251
+ * The default bitmap covers tick arrays within ±(tickSpacing * 60 * 512) of 0.
252
+ *
253
+ * Matches reference: compressed = floor(tick / multiplier) + 512
254
+ * bitPos = abs(compressed)
255
+ * isInit = bitmap.testn(bitPos)
256
+ */
257
+ function checkTickArrayIsInitialized(bitmap, tick, tickSpacing) {
258
+ const multiplier = tickSpacing * TICK_ARRAY_SIZE;
259
+ const compressed = Math.floor(tick / multiplier) + 512;
260
+ const bitPos = Math.abs(compressed);
261
+ return {
262
+ isInitialized: bitmap.testn(bitPos),
263
+ startIndex: (bitPos - 512) * multiplier,
264
+ };
265
+ }
266
+ /**
267
+ * Check if tick indices overflow the default bitmap range.
268
+ * If so, the extension bitmap must be used.
269
+ */
270
+ function isOverflowDefaultTickarrayBitmap(tickSpacing, tickIndices) {
271
+ const ticksInOneBitmap = tickSpacing * TICK_ARRAY_SIZE * TICK_ARRAY_BITMAP_SIZE;
272
+ const maxTickBoundary = ticksInOneBitmap;
273
+ const minTickBoundary = -maxTickBoundary;
274
+ for (const tickIndex of tickIndices) {
275
+ const tickArrayStartIndex = getTickArrayStartIndexForTick(tickIndex, tickSpacing);
276
+ if (tickArrayStartIndex >= maxTickBoundary || tickArrayStartIndex < minTickBoundary) {
277
+ return true;
278
+ }
279
+ }
280
+ return false;
281
+ }
282
+ /**
283
+ * Get the offset index into the extension bitmap array for a given tick index.
284
+ */
285
+ function getBitmapOffset(tickIndex, tickSpacing) {
286
+ const ticksInOneBitmap = tickSpacing * TICK_ARRAY_SIZE * TICK_ARRAY_BITMAP_SIZE;
287
+ let offset = Math.floor(Math.abs(tickIndex) / ticksInOneBitmap) - 1;
288
+ if (tickIndex < 0 && Math.abs(tickIndex) % ticksInOneBitmap === 0)
289
+ offset--;
290
+ return offset;
291
+ }
292
+ /**
293
+ * Check if a tick array is initialized using the extension bitmap.
294
+ * Used for tick arrays outside the default bitmap range.
295
+ */
296
+ function checkTickArrayIsInitInExtension(tickArrayStartIndex, tickSpacing, exBitmapInfo) {
297
+ const offset = getBitmapOffset(tickArrayStartIndex, tickSpacing);
298
+ const tickarrayBitmap = tickArrayStartIndex < 0
299
+ ? exBitmapInfo.negativeTickArrayBitmap[offset]
300
+ : exBitmapInfo.positiveTickArrayBitmap[offset];
301
+ if (!tickarrayBitmap) {
302
+ return { isInitialized: false, startIndex: tickArrayStartIndex };
303
+ }
304
+ const ticksInOneBitmap = tickSpacing * TICK_ARRAY_SIZE * TICK_ARRAY_BITMAP_SIZE;
305
+ const tickArrayOffsetInBitmap = Math.floor((Math.abs(tickArrayStartIndex) % ticksInOneBitmap) / (tickSpacing * TICK_ARRAY_SIZE));
306
+ const merged = mergeTickArrayBitmapToSingleBN(tickarrayBitmap);
307
+ return {
308
+ isInitialized: merged.testn(tickArrayOffsetInBitmap),
309
+ startIndex: tickArrayStartIndex,
310
+ };
311
+ }
312
+ /**
313
+ * Check if a specific tick array start index is initialized, using
314
+ * either the default bitmap or extension bitmap depending on range.
315
+ */
316
+ function isTickArrayInitialized(tickArrayStartIndex, tickSpacing, poolBitmap, exBitmapInfo) {
317
+ const isOverflow = isOverflowDefaultTickarrayBitmap(tickSpacing, [tickArrayStartIndex]);
318
+ if (isOverflow) {
319
+ const result = checkTickArrayIsInitInExtension(tickArrayStartIndex, tickSpacing, exBitmapInfo);
320
+ return result.isInitialized;
321
+ }
322
+ else {
323
+ const merged = mergeTickArrayBitmapToSingleBN(poolBitmap);
324
+ const result = checkTickArrayIsInitialized(merged, tickArrayStartIndex, tickSpacing);
325
+ return result.isInitialized;
326
+ }
327
+ }
328
+ /**
329
+ * Find the first initialized tick array, starting from the current tick's array.
330
+ *
331
+ * Algorithm (matches reference tick-array-sdk.ts):
332
+ * 1. Check if the current tick's tick array is initialized in the bitmap.
333
+ * 2. If yes, verify it exists on-chain and return it.
334
+ * 3. If no, walk in the swap direction to find the next initialized one.
335
+ */
336
+ async function findFirstInitializedTickArrayFromBitmap(connection, programId, poolId, poolState, zeroForOne) {
337
+ const currentStartIndex = getTickArrayStartIndexForTick(poolState.tickCurrent, poolState.tickSpacing);
338
+ // Step 1: Check if the current tick's array is initialized
339
+ const isOverflow = isOverflowDefaultTickarrayBitmap(poolState.tickSpacing, [poolState.tickCurrent]);
340
+ let isInit = false;
341
+ let startIndex = currentStartIndex;
342
+ if (isOverflow) {
343
+ const result = checkTickArrayIsInitInExtension(currentStartIndex, poolState.tickSpacing, poolState.exBitmapInfo);
344
+ isInit = result.isInitialized;
345
+ startIndex = result.startIndex;
346
+ }
347
+ else {
348
+ const merged = mergeTickArrayBitmapToSingleBN(poolState.tickArrayBitmap);
349
+ const result = checkTickArrayIsInitialized(merged, poolState.tickCurrent, poolState.tickSpacing);
350
+ isInit = result.isInitialized;
351
+ startIndex = result.startIndex;
352
+ }
353
+ if (isInit) {
354
+ // Verify on-chain before returning
355
+ const tickArrayAddr = deriveTickArray(programId, poolId, startIndex);
356
+ const info = await connection.getAccountInfo(tickArrayAddr);
357
+ if (info && info.owner.equals(programId)) {
358
+ return {
359
+ isExist: true,
360
+ startIndex,
361
+ nextAccountMeta: tickArrayAddr,
362
+ };
363
+ }
364
+ }
365
+ // Step 2: Walk in the swap direction to find the next initialized tick array
366
+ const ticksPerArray = poolState.tickSpacing * TICK_ARRAY_SIZE;
367
+ const maxSearchDistance = 20;
368
+ let searchIndex = currentStartIndex;
369
+ for (let i = 0; i < maxSearchDistance; i++) {
370
+ searchIndex = zeroForOne
371
+ ? searchIndex - ticksPerArray
372
+ : searchIndex + ticksPerArray;
373
+ if (searchIndex < MIN_TICK || searchIndex > MAX_TICK)
374
+ break;
375
+ const initCheck = isTickArrayInitialized(searchIndex, poolState.tickSpacing, poolState.tickArrayBitmap, poolState.exBitmapInfo);
376
+ if (initCheck) {
377
+ // Verify on-chain
378
+ const tickArrayAddr = deriveTickArray(programId, poolId, searchIndex);
379
+ const info = await connection.getAccountInfo(tickArrayAddr);
380
+ if (info && info.owner.equals(programId)) {
381
+ return {
382
+ isExist: true,
383
+ startIndex: searchIndex,
384
+ nextAccountMeta: tickArrayAddr,
385
+ };
386
+ }
387
+ }
388
+ }
389
+ return { isExist: false, startIndex: currentStartIndex };
390
+ }
391
+ // Brute-force fallback: check account existence directly
392
+ async function findFirstInitializedTickArrayBruteForce(connection, programId, poolId, tickCurrent, tickSpacing, zeroForOne) {
393
+ const currentStartIndex = getTickArrayStartIndexForTick(tickCurrent, tickSpacing);
394
+ const maxSearch = 10;
395
+ // Search in primary direction
396
+ let searchIndex = currentStartIndex;
397
+ for (let i = 0; i < maxSearch; i++) {
398
+ const addr = deriveTickArray(programId, poolId, searchIndex);
399
+ const info = await connection.getAccountInfo(addr);
400
+ if (info && info.owner.equals(programId)) {
401
+ return { isExist: true, startIndex: searchIndex, nextAccountMeta: addr };
402
+ }
403
+ searchIndex = getNextTickArrayStartIndex(searchIndex, tickSpacing, zeroForOne);
404
+ }
405
+ // Search in opposite direction
406
+ searchIndex = getNextTickArrayStartIndex(currentStartIndex, tickSpacing, !zeroForOne);
407
+ for (let i = 0; i < maxSearch; i++) {
408
+ const addr = deriveTickArray(programId, poolId, searchIndex);
409
+ const info = await connection.getAccountInfo(addr);
410
+ if (info && info.owner.equals(programId)) {
411
+ return { isExist: true, startIndex: searchIndex, nextAccountMeta: addr };
412
+ }
413
+ searchIndex = getNextTickArrayStartIndex(searchIndex, tickSpacing, !zeroForOne);
414
+ }
415
+ return { isExist: false, startIndex: currentStartIndex };
416
+ }
417
+ async function deriveClmmPoolAccounts(connection, poolId, programId) {
418
+ const poolState = await fetchClmmPoolState(connection, poolId, programId);
419
+ return {
420
+ poolId,
421
+ ammConfig: poolState.ammConfig,
422
+ poolState: poolId,
423
+ observationState: poolState.observationKey,
424
+ tokenVault0: poolState.tokenVault0,
425
+ tokenVault1: poolState.tokenVault1,
426
+ tokenMint0: poolState.tokenMint0,
427
+ tokenMint1: poolState.tokenMint1,
428
+ };
429
+ }
430
+ // ---------------------------------------------------------------------------
431
+ // Swap IX builder
432
+ // ---------------------------------------------------------------------------
433
+ /**
434
+ * Create a CLMM swap_v2 instruction.
435
+ *
436
+ * @param zeroForOne - Swap direction: true = token0→token1, false = token1→token0.
437
+ * Determines which vault is input/output.
438
+ * @param isExactInput - true = `amount` is the exact input (typical case).
439
+ * false = `amount` is the exact output desired.
440
+ * For isExactInput=true, otherAmountThreshold = min output (0 = unlimited).
441
+ * For isExactInput=false, otherAmountThreshold = max input (u64::MAX = unlimited).
442
+ */
443
+ function createClmmSwapIx(programId, poolAccounts, payer, inputTokenAccount, outputTokenAccount, amount, otherAmountThreshold, sqrtPriceLimitX64, zeroForOne, isExactInput, tickArrays, inputVaultMint, outputVaultMint, tickArrayBitmapExtension) {
444
+ const inputVault = zeroForOne ? poolAccounts.tokenVault0 : poolAccounts.tokenVault1;
445
+ const outputVault = zeroForOne ? poolAccounts.tokenVault1 : poolAccounts.tokenVault0;
446
+ const accounts = [
447
+ { pubkey: payer, isSigner: true, isWritable: true },
448
+ { pubkey: poolAccounts.ammConfig, isSigner: false, isWritable: false },
449
+ { pubkey: poolAccounts.poolState, isSigner: false, isWritable: true },
450
+ { pubkey: inputTokenAccount, isSigner: false, isWritable: true },
451
+ { pubkey: outputTokenAccount, isSigner: false, isWritable: true },
452
+ { pubkey: inputVault, isSigner: false, isWritable: true },
453
+ { pubkey: outputVault, isSigner: false, isWritable: true },
454
+ { pubkey: poolAccounts.observationState, isSigner: false, isWritable: true },
455
+ { pubkey: spl_token_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
456
+ { pubkey: spl_token_1.TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
457
+ { pubkey: MEMO_PROGRAM_ID, isSigner: false, isWritable: false },
458
+ { pubkey: inputVaultMint, isSigner: false, isWritable: false },
459
+ { pubkey: outputVaultMint, isSigner: false, isWritable: false },
460
+ ];
461
+ // Remaining accounts: bitmap extension (only if exists on-chain) + tick arrays
462
+ const remainingAccounts = [];
463
+ if (tickArrayBitmapExtension) {
464
+ remainingAccounts.push({
465
+ pubkey: tickArrayBitmapExtension,
466
+ isSigner: false,
467
+ isWritable: true,
468
+ });
469
+ }
470
+ for (const ta of tickArrays) {
471
+ remainingAccounts.push({
472
+ pubkey: ta,
473
+ isSigner: false,
474
+ isWritable: true,
475
+ });
476
+ }
477
+ // Build instruction data (41 bytes)
478
+ const data = Buffer.alloc(41);
479
+ let off = 0;
480
+ SWAP_V2_DISCRIMINATOR.copy(data, off);
481
+ off += 8;
482
+ data.writeBigUInt64LE(amount, off);
483
+ off += 8;
484
+ data.writeBigUInt64LE(otherAmountThreshold, off);
485
+ off += 8;
486
+ const sqrtPriceBytes = sqrtPriceLimitX64.toArrayLike(Buffer, "le", 16);
487
+ sqrtPriceBytes.copy(data, off);
488
+ off += 16;
489
+ data.writeUInt8(isExactInput ? 1 : 0, off);
490
+ return new web3_js_1.TransactionInstruction({
491
+ keys: [...accounts, ...remainingAccounts],
492
+ programId,
493
+ data,
494
+ });
495
+ }
496
+ // ---------------------------------------------------------------------------
497
+ // Token program detection
498
+ // ---------------------------------------------------------------------------
499
+ async function getTokenProgramForMint(connection, mint) {
500
+ const info = await connection.getAccountInfo(mint);
501
+ if (!info)
502
+ throw new Error(`Mint account not found: ${mint.toBase58()}`);
503
+ return info.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)
504
+ ? spl_token_1.TOKEN_2022_PROGRAM_ID
505
+ : spl_token_1.TOKEN_PROGRAM_ID;
506
+ }
507
+ // ---------------------------------------------------------------------------
508
+ // Price calculation
509
+ // ---------------------------------------------------------------------------
510
+ function sqrtPriceX64ToPrice(sqrtPriceX64, decimalsA, decimalsB) {
511
+ return raydium_sdk_v2_1.MathUtil.x64ToDecimal(sqrtPriceX64)
512
+ .pow(2)
513
+ .mul(decimal_js_1.default.pow(10, decimalsA - decimalsB));
514
+ }
515
+ // ---------------------------------------------------------------------------
516
+ // Helpers
517
+ // ---------------------------------------------------------------------------
518
+ /**
519
+ * Compute the sqrt price limit for a swap.
520
+ * zeroForOne=true (price decreasing): limit = MIN_SQRT_PRICE_X64 + 1
521
+ * zeroForOne=false (price increasing): limit = MAX_SQRT_PRICE_X64 - 1
522
+ */
523
+ function computeSqrtPriceLimit(currentSqrtPrice, zeroForOne) {
524
+ if (zeroForOne) {
525
+ const minPlusOne = MIN_SQRT_PRICE_X64.add(new bn_js_1.default(1));
526
+ if (minPlusOne.lt(currentSqrtPrice)) {
527
+ return minPlusOne;
528
+ }
529
+ const fallback = currentSqrtPrice.sub(new bn_js_1.default(1));
530
+ if (fallback.lte(MIN_SQRT_PRICE_X64)) {
531
+ return MIN_SQRT_PRICE_X64.add(new bn_js_1.default(1));
532
+ }
533
+ return fallback;
534
+ }
535
+ else {
536
+ const maxMinusOne = MAX_SQRT_PRICE_X64.sub(new bn_js_1.default(1));
537
+ if (maxMinusOne.gt(currentSqrtPrice)) {
538
+ return maxMinusOne;
539
+ }
540
+ const fallback = currentSqrtPrice.add(new bn_js_1.default(1));
541
+ if (fallback.gte(MAX_SQRT_PRICE_X64)) {
542
+ return MAX_SQRT_PRICE_X64.sub(new bn_js_1.default(1));
543
+ }
544
+ return fallback;
545
+ }
546
+ }
547
+ function quoteDecimals(quoteMintStr) {
548
+ return SIX_DECIMAL_MINTS.has(quoteMintStr) ? 6 : 9;
549
+ }
550
+ function amountToSmallestUnit(amount, quoteMintStr) {
551
+ const decimals = quoteDecimals(quoteMintStr);
552
+ return BigInt(Math.floor(amount * Math.pow(10, decimals)));
553
+ }
554
+ class ClmmBaseAdapter {
555
+ name;
556
+ protocol;
557
+ capabilities = (0, types_1.defaultCapabilities)({
558
+ canBuy: true,
559
+ canSell: true,
560
+ canSnipe: true,
561
+ canFindPool: false, // Requires off-chain indexing or Raydium API
562
+ canGetPrice: true,
563
+ });
564
+ programId;
565
+ constructor(config) {
566
+ this.name = config.name;
567
+ this.protocol = config.protocol;
568
+ this.programId = config.programId;
569
+ }
570
+ // ---- Core: buy ----
571
+ async buy(params) {
572
+ const tokenMint = (0, types_1.requireTokenMint)(params, this.name);
573
+ const { amountSol, quoteMint: quoteMintParam, poolAddress, opts } = params;
574
+ const connection = (0, config_1.getConnection)();
575
+ const wallet = (0, config_1.getWallet)();
576
+ const quoteMintStr = quoteMintParam ?? types_1.WSOL_MINT;
577
+ if (!poolAddress) {
578
+ throw new Error(`${this.name}: poolAddress is required (auto-discovery not yet supported)`);
579
+ }
580
+ const allIxs = await this.buildFullSwapTx(tokenMint, amountSol, quoteMintStr, poolAddress, wallet, opts);
581
+ // Submit via RPC send+confirm
582
+ const result = await (0, send_rpc_1.sendAndConfirmVtx)(connection, allIxs, wallet);
583
+ return {
584
+ txSignature: result.txSignature,
585
+ confirmed: result.confirmed,
586
+ amountIn: amountSol,
587
+ amountInToken: quoteMintStr,
588
+ dex: this.name,
589
+ poolAddress,
590
+ };
591
+ }
592
+ // ---- Core: sell ----
593
+ async sell(params) {
594
+ const tokenMint = (0, types_1.requireTokenMint)(params, this.name);
595
+ const { percentage, quoteMint: quoteMintParam, poolAddress, opts } = params;
596
+ const connection = (0, config_1.getConnection)();
597
+ const wallet = (0, config_1.getWallet)();
598
+ const quoteMintStr = quoteMintParam ?? types_1.WSOL_MINT;
599
+ if (!poolAddress) {
600
+ throw new Error(`${this.name}: poolAddress is required (auto-discovery not yet supported)`);
601
+ }
602
+ // 1. Get balance of the token being sold
603
+ const baseMintPk = new web3_js_1.PublicKey(tokenMint);
604
+ const baseMintTokenProgram = await getTokenProgramForMint(connection, baseMintPk);
605
+ const baseAta = await (0, spl_token_1.getAssociatedTokenAddress)(baseMintPk, wallet.publicKey, false, baseMintTokenProgram);
606
+ let balance;
607
+ try {
608
+ const res = await connection.getTokenAccountBalance(baseAta);
609
+ balance = { amount: BigInt(res.value.amount), decimals: res.value.decimals };
610
+ }
611
+ catch {
612
+ balance = { amount: 0n, decimals: 0 };
613
+ }
614
+ // 2. Calculate sell amount from percentage
615
+ const sellAmount = (balance.amount * BigInt(Math.floor(percentage))) / 100n;
616
+ if (sellAmount === 0n) {
617
+ return {
618
+ txSignature: "",
619
+ confirmed: false,
620
+ amountIn: 0,
621
+ amountInToken: tokenMint,
622
+ dex: this.name,
623
+ poolAddress,
624
+ };
625
+ }
626
+ // 3. Build swap IXs with reversed direction: tokenMint is now the input
627
+ const allIxs = await this.buildFullSellTx(tokenMint, sellAmount, quoteMintStr, poolAddress, wallet, opts);
628
+ // 4. Submit via RPC send+confirm
629
+ const result = await (0, send_rpc_1.sendAndConfirmVtx)(connection, allIxs, wallet);
630
+ const humanSellAmount = Number(sellAmount) / 10 ** balance.decimals;
631
+ return {
632
+ txSignature: result.txSignature,
633
+ confirmed: result.confirmed,
634
+ amountIn: humanSellAmount,
635
+ amountInToken: tokenMint,
636
+ dex: this.name,
637
+ poolAddress,
638
+ };
639
+ }
640
+ // ---- Snipe ----
641
+ async snipe(params) {
642
+ const { tokenMint, amountSol, poolAddress, quoteMint: quoteMintParam, tipSol, opts } = params;
643
+ const connection = (0, config_1.getConnection)();
644
+ const wallet = (0, config_1.getWallet)();
645
+ const quoteMintStr = quoteMintParam ?? types_1.WSOL_MINT;
646
+ const allIxs = await this.buildFullSwapTx(tokenMint, amountSol, quoteMintStr, poolAddress, wallet, opts);
647
+ const blockhash = await connection.getLatestBlockhash();
648
+ const results = await (0, landing_1.landTransaction)(allIxs, wallet, blockhash, {
649
+ dex: this.name,
650
+ operation: "snipe",
651
+ tipSol,
652
+ });
653
+ const accepted = results.find((r) => r.accepted);
654
+ return {
655
+ txSignature: accepted?.signature ?? "",
656
+ confirmed: !!accepted,
657
+ amountIn: amountSol,
658
+ amountInToken: quoteMintStr,
659
+ dex: this.name,
660
+ poolAddress,
661
+ };
662
+ }
663
+ // ---- buildSwapIxs ----
664
+ async buildSwapIxs(params) {
665
+ const tokenMint = (0, types_1.requireTokenMint)(params, this.name);
666
+ if ("percentage" in params) {
667
+ // Sell path
668
+ const { percentage, quoteMint: quoteMintParam, poolAddress } = params;
669
+ const quoteMintStr = quoteMintParam ?? types_1.WSOL_MINT;
670
+ if (!poolAddress) {
671
+ throw new Error(`${this.name}: poolAddress is required for buildSwapIxs`);
672
+ }
673
+ const connection = (0, config_1.getConnection)();
674
+ const wallet = (0, config_1.getWallet)();
675
+ const baseMintPk = new web3_js_1.PublicKey(tokenMint);
676
+ const baseMintTokenProgram = await getTokenProgramForMint(connection, baseMintPk);
677
+ const baseAta = await (0, spl_token_1.getAssociatedTokenAddress)(baseMintPk, wallet.publicKey, false, baseMintTokenProgram);
678
+ let balance;
679
+ try {
680
+ const res = await connection.getTokenAccountBalance(baseAta);
681
+ balance = { amount: BigInt(res.value.amount), decimals: res.value.decimals };
682
+ }
683
+ catch {
684
+ balance = { amount: 0n, decimals: 0 };
685
+ }
686
+ const sellAmount = (balance.amount * BigInt(Math.floor(percentage))) / 100n;
687
+ const sellAmountHuman = Number(sellAmount) / 10 ** balance.decimals;
688
+ // For sell: tokenMint is input, quoteMint is output
689
+ // Pass tokenMint as "quoteMint" (input) and quoteMintStr as "tokenMint" (output)
690
+ return this.doBuildSwapIxs(quoteMintStr, sellAmountHuman, tokenMint, poolAddress);
691
+ }
692
+ const { amountSol, quoteMint: quoteMintParam, poolAddress, opts } = params;
693
+ const quoteMintStr = quoteMintParam ?? types_1.WSOL_MINT;
694
+ if (!poolAddress) {
695
+ throw new Error(`${this.name}: poolAddress is required for buildSwapIxs`);
696
+ }
697
+ return this.doBuildSwapIxs(tokenMint, amountSol, quoteMintStr, poolAddress);
698
+ }
699
+ // ---- findPool (stub) ----
700
+ async findPool(baseMint, _quoteMint) {
701
+ // CLMM pool discovery requires off-chain indexing or Raydium API.
702
+ return null;
703
+ }
704
+ // ---- getPrice ----
705
+ async getPrice(poolAddress) {
706
+ const connection = (0, config_1.getConnection)();
707
+ const poolId = new web3_js_1.PublicKey(poolAddress);
708
+ const poolState = await fetchClmmPoolState(connection, poolId, this.programId);
709
+ const price = sqrtPriceX64ToPrice(poolState.sqrtPriceX64, poolState.mintDecimals0, poolState.mintDecimals1);
710
+ let realPrice = Number(price.toString());
711
+ if (realPrice > 1)
712
+ realPrice = 1 / realPrice;
713
+ return {
714
+ price: realPrice,
715
+ baseMint: poolState.tokenMint0.toBase58(),
716
+ quoteMint: poolState.tokenMint1.toBase58(),
717
+ source: "on-chain",
718
+ poolAddress,
719
+ timestamp: Date.now(),
720
+ };
721
+ }
722
+ // ---- Internal: resolve tick arrays for swap ----
723
+ async resolveTickArrays(connection, poolId, poolState, exBitmapInfo, zeroForOne) {
724
+ let firstResult = await findFirstInitializedTickArrayFromBitmap(connection, this.programId, poolId, {
725
+ tickCurrent: poolState.tickCurrent,
726
+ tickSpacing: poolState.tickSpacing,
727
+ tickArrayBitmap: poolState.tickArrayBitmap,
728
+ exBitmapInfo: {
729
+ positiveTickArrayBitmap: exBitmapInfo.positiveTickArrayBitmap,
730
+ negativeTickArrayBitmap: exBitmapInfo.negativeTickArrayBitmap,
731
+ },
732
+ }, zeroForOne);
733
+ // Brute-force fallback if bitmap search fails
734
+ if (!firstResult.isExist) {
735
+ firstResult = await findFirstInitializedTickArrayBruteForce(connection, this.programId, poolId, poolState.tickCurrent, poolState.tickSpacing, zeroForOne);
736
+ }
737
+ if (!firstResult.isExist || !firstResult.nextAccountMeta) {
738
+ throw new Error(`No initialized tick array found for pool ${poolId.toBase58()}. ` +
739
+ `Current tick: ${poolState.tickCurrent}, Tick spacing: ${poolState.tickSpacing}`);
740
+ }
741
+ // Build tick array list (up to 4)
742
+ const tickArrays = [firstResult.nextAccountMeta];
743
+ let currentStartIndex = firstResult.startIndex;
744
+ for (let i = 1; i < 4; i++) {
745
+ currentStartIndex = getNextTickArrayStartIndex(currentStartIndex, poolState.tickSpacing, zeroForOne);
746
+ const addr = deriveTickArray(this.programId, poolId, currentStartIndex);
747
+ const info = await connection.getAccountInfo(addr);
748
+ if (info && info.owner.equals(this.programId)) {
749
+ tickArrays.push(addr);
750
+ }
751
+ else {
752
+ break;
753
+ }
754
+ }
755
+ return { tickArrays, firstResult };
756
+ }
757
+ // ---- Internal: build raw swap instructions ----
758
+ async doBuildSwapIxs(tokenMint, amountSol, quoteMintStr, poolAddress) {
759
+ const connection = (0, config_1.getConnection)();
760
+ const wallet = (0, config_1.getWallet)();
761
+ const poolId = new web3_js_1.PublicKey(poolAddress);
762
+ const baseMint = new web3_js_1.PublicKey(tokenMint);
763
+ const quoteMint = new web3_js_1.PublicKey(quoteMintStr);
764
+ // 1. Derive pool accounts
765
+ const poolAccounts = await deriveClmmPoolAccounts(connection, poolId, this.programId);
766
+ // 2. Fetch pool state
767
+ const poolState = await fetchClmmPoolState(connection, poolId, this.programId);
768
+ // 3. Determine swap direction
769
+ // inputMint is what we're spending (quoteMint), outputMint is what we're receiving (baseMint)
770
+ const inputMint = quoteMint;
771
+ const outputMint = baseMint;
772
+ // zeroForOne = true means token0→token1 (price decreasing)
773
+ // zeroForOne = false means token1→token0 (price increasing)
774
+ const zeroForOne = poolState.tokenMint0.equals(inputMint);
775
+ // 4. Calculate amount
776
+ const amountIn = amountToSmallestUnit(amountSol, quoteMintStr);
777
+ // 5. Sqrt price limit
778
+ const sqrtPriceLimitX64 = computeSqrtPriceLimit(poolState.sqrtPriceX64, zeroForOne);
779
+ // 6. Get bitmap extension
780
+ const exBitmapInfo = await getTickArrayBitmapExtension(this.programId, poolId, connection);
781
+ // 7. Find initialized tick arrays
782
+ const { tickArrays } = await this.resolveTickArrays(connection, poolId, poolState, exBitmapInfo, zeroForOne);
783
+ // 8. Get user ATAs
784
+ const inputMintTokenProgram = await getTokenProgramForMint(connection, inputMint);
785
+ const outputMintTokenProgram = await getTokenProgramForMint(connection, outputMint);
786
+ const inputAta = await (0, spl_token_1.getAssociatedTokenAddress)(inputMint, wallet.publicKey, false, inputMintTokenProgram);
787
+ const outputAta = await (0, spl_token_1.getAssociatedTokenAddress)(outputMint, wallet.publicKey, false, outputMintTokenProgram);
788
+ // 9. Build swap IX (always exact-input mode: is_base_input=true, threshold=0)
789
+ const inputVaultMint = zeroForOne ? poolState.tokenMint0 : poolState.tokenMint1;
790
+ const outputVaultMint = zeroForOne ? poolState.tokenMint1 : poolState.tokenMint0;
791
+ const swapIx = createClmmSwapIx(this.programId, poolAccounts, wallet.publicKey, inputAta, outputAta, amountIn, BigInt(0), // min output = 0 (unlimited slippage)
792
+ sqrtPriceLimitX64, zeroForOne, true, // isExactInput = true (amount is the input amount)
793
+ tickArrays, inputVaultMint, outputVaultMint, exBitmapInfo.exists ? exBitmapInfo.exBitmapAddress : null);
794
+ // 10. Create ATA instructions
795
+ const createInputAtaIx = (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(wallet.publicKey, inputAta, wallet.publicKey, inputMint, inputMintTokenProgram);
796
+ const createOutputAtaIx = (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(wallet.publicKey, outputAta, wallet.publicKey, outputMint, outputMintTokenProgram);
797
+ return {
798
+ instructions: [createInputAtaIx, createOutputAtaIx, swapIx],
799
+ signers: [],
800
+ };
801
+ }
802
+ // ---- Internal: full transaction with WSOL wrapping + compute budget ----
803
+ async buildFullSwapTx(tokenMint, amountSol, quoteMintStr, poolAddress, wallet, opts) {
804
+ const { instructions: swapIxs } = await this.doBuildSwapIxs(tokenMint, amountSol, quoteMintStr, poolAddress);
805
+ const cuPrice = opts?.priorityFeeMicroLamports ?? types_1.DEFAULT_PRIORITY_FEE_MICRO_LAMPORTS;
806
+ const cuLimit = opts?.computeUnitLimit ?? types_1.DEFAULT_COMPUTE_UNIT_LIMIT;
807
+ const isWSol = quoteMintStr === types_1.WSOL_MINT;
808
+ const preIxs = [
809
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: cuLimit }),
810
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: cuPrice }),
811
+ ];
812
+ if (isWSol) {
813
+ const quoteMintPk = new web3_js_1.PublicKey(quoteMintStr);
814
+ const inputAta = await (0, spl_token_1.getAssociatedTokenAddress)(quoteMintPk, wallet.publicKey, false, spl_token_1.TOKEN_PROGRAM_ID);
815
+ const amountLamports = Math.floor(amountSol * web3_js_1.LAMPORTS_PER_SOL);
816
+ // swapIxs = [createInputATA, createOutputATA, swapIx]
817
+ // Insert ATA creates first, then transfer+sync, then swap
818
+ const createAtaIxs = swapIxs.slice(0, -1); // ATA creation instructions
819
+ const swapOnlyIx = swapIxs[swapIxs.length - 1]; // the actual swap
820
+ const closeIx = (0, spl_token_1.createCloseAccountInstruction)(inputAta, wallet.publicKey, wallet.publicKey, [], spl_token_1.TOKEN_PROGRAM_ID);
821
+ return [
822
+ ...preIxs,
823
+ ...createAtaIxs,
824
+ web3_js_1.SystemProgram.transfer({
825
+ fromPubkey: wallet.publicKey,
826
+ toPubkey: inputAta,
827
+ lamports: amountLamports,
828
+ }),
829
+ (0, spl_token_1.createSyncNativeInstruction)(inputAta, spl_token_1.TOKEN_PROGRAM_ID),
830
+ swapOnlyIx,
831
+ closeIx,
832
+ ];
833
+ }
834
+ return [...preIxs, ...swapIxs];
835
+ }
836
+ // ---- Internal: full sell transaction with WSOL output handling + compute budget ----
837
+ async buildFullSellTx(tokenMint, sellAmount, quoteMintStr, poolAddress, wallet, opts) {
838
+ // For sell, the input is tokenMint and the output is quoteMint.
839
+ // We build swap IXs directly using the raw sell amount (bigint) to avoid
840
+ // decimal rounding issues with quoteDecimals().
841
+ const connection = (0, config_1.getConnection)();
842
+ const poolId = new web3_js_1.PublicKey(poolAddress);
843
+ const baseMint = new web3_js_1.PublicKey(tokenMint);
844
+ const quoteMint = new web3_js_1.PublicKey(quoteMintStr);
845
+ // 1. Derive pool accounts
846
+ const poolAccounts = await deriveClmmPoolAccounts(connection, poolId, this.programId);
847
+ // 2. Fetch pool state
848
+ const poolState = await fetchClmmPoolState(connection, poolId, this.programId);
849
+ // 3. For sell: input is baseMint (token being sold), output is quoteMint
850
+ const inputMint = baseMint;
851
+ const outputMint = quoteMint;
852
+ // zeroForOne = true means token0→token1. For sell, input is the token.
853
+ const zeroForOne = poolState.tokenMint0.equals(inputMint);
854
+ // 4. Sqrt price limit
855
+ const sqrtPriceLimitX64 = computeSqrtPriceLimit(poolState.sqrtPriceX64, zeroForOne);
856
+ // 5. Get bitmap extension
857
+ const exBitmapInfo = await getTickArrayBitmapExtension(this.programId, poolId, connection);
858
+ // 6. Find initialized tick arrays
859
+ const { tickArrays } = await this.resolveTickArrays(connection, poolId, poolState, exBitmapInfo, zeroForOne);
860
+ // 7. Get user ATAs
861
+ const inputMintTokenProgram = await getTokenProgramForMint(connection, inputMint);
862
+ const outputMintTokenProgram = await getTokenProgramForMint(connection, outputMint);
863
+ const inputAta = await (0, spl_token_1.getAssociatedTokenAddress)(inputMint, wallet.publicKey, false, inputMintTokenProgram);
864
+ const outputAta = await (0, spl_token_1.getAssociatedTokenAddress)(outputMint, wallet.publicKey, false, outputMintTokenProgram);
865
+ // 8. Build swap IX (always exact-input mode: is_base_input=true, threshold=0)
866
+ const inputVaultMint = zeroForOne ? poolState.tokenMint0 : poolState.tokenMint1;
867
+ const outputVaultMint = zeroForOne ? poolState.tokenMint1 : poolState.tokenMint0;
868
+ const swapIx = createClmmSwapIx(this.programId, poolAccounts, wallet.publicKey, inputAta, outputAta, BigInt(sellAmount.toString()), BigInt(0), // min output = 0 (unlimited slippage)
869
+ sqrtPriceLimitX64, zeroForOne, true, // isExactInput = true (amount is the input amount)
870
+ tickArrays, inputVaultMint, outputVaultMint, exBitmapInfo.exists ? exBitmapInfo.exBitmapAddress : null);
871
+ // 9. Compose full transaction
872
+ const cuPrice = opts?.priorityFeeMicroLamports ?? types_1.DEFAULT_PRIORITY_FEE_MICRO_LAMPORTS;
873
+ const cuLimit = opts?.computeUnitLimit ?? types_1.DEFAULT_COMPUTE_UNIT_LIMIT;
874
+ const isWsolOutput = quoteMintStr === types_1.WSOL_MINT;
875
+ const preIxs = [
876
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: cuLimit }),
877
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: cuPrice }),
878
+ ];
879
+ // Create ATAs idempotently
880
+ const createInputAtaIx = (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(wallet.publicKey, inputAta, wallet.publicKey, inputMint, inputMintTokenProgram);
881
+ const createOutputAtaIx = (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(wallet.publicKey, outputAta, wallet.publicKey, outputMint, outputMintTokenProgram);
882
+ if (isWsolOutput) {
883
+ // For sell to WSOL: create output WSOL ATA, swap, then close to unwrap SOL
884
+ const closeIx = (0, spl_token_1.createCloseAccountInstruction)(outputAta, wallet.publicKey, wallet.publicKey, [], spl_token_1.TOKEN_PROGRAM_ID);
885
+ return [...preIxs, createInputAtaIx, createOutputAtaIx, swapIx, closeIx];
886
+ }
887
+ return [...preIxs, createInputAtaIx, createOutputAtaIx, swapIx];
888
+ }
889
+ }
890
+ exports.ClmmBaseAdapter = ClmmBaseAdapter;
891
+ //# sourceMappingURL=clmm-base.js.map