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,1146 @@
1
+ "use strict";
2
+ /**
3
+ * Meteora DAMM v2 (CP AMM) — IDexAdapter Implementation
4
+ *
5
+ * Wraps the @meteora-ag/cp-amm-sdk (CpAmm) for buy/sell/snipe/findPool/getPrice
6
+ * plus LP operations: addLiquidity (create position), removeLiquidity (close
7
+ * position), and claimFees.
8
+ *
9
+ * Source: 100x-algo-bots/trading-modules/meteora-damm-v2/
10
+ *
11
+ * SDK pattern:
12
+ * const cpAmm = new CpAmm(connection);
13
+ * const poolState = await cpAmm.fetchPoolState(poolAddress);
14
+ * const swapTx = await cpAmm.swap({ payer, pool, inputTokenMint, ... });
15
+ * const addTx = cpAmm.createPositionAndAddLiquidity({ ... });
16
+ * const removeTx = cpAmm.removeAllLiquidityAndClosePosition({ ... });
17
+ * const claimTx = cpAmm.claimPositionFee2({ ... });
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.MeteoraDammV2Adapter = void 0;
24
+ const bn_js_1 = __importDefault(require("bn.js"));
25
+ const decimal_js_1 = __importDefault(require("decimal.js"));
26
+ const web3_js_1 = require("@solana/web3.js");
27
+ const cp_amm_sdk_1 = require("@meteora-ag/cp-amm-sdk");
28
+ const spl_token_1 = require("@solana/spl-token");
29
+ const raydium_sdk_v2_1 = require("@raydium-io/raydium-sdk-v2");
30
+ const config_1 = require("../helpers/config");
31
+ const landing_1 = require("../transactions/landing");
32
+ const send_rpc_1 = require("../transactions/send-rpc");
33
+ const types_1 = require("./types");
34
+ const index_1 = require("./index");
35
+ // ---------------------------------------------------------------------------
36
+ // Constants
37
+ // ---------------------------------------------------------------------------
38
+ const DAMM_PROGRAM_ID = new web3_js_1.PublicKey("cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG");
39
+ /** Known stablecoin mints with 6 decimals */
40
+ const SIX_DECIMAL_MINTS = new Set([
41
+ types_1.USDC_MINT,
42
+ types_1.USDT_MINT,
43
+ "USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB", // USD1
44
+ ]);
45
+ // ---------------------------------------------------------------------------
46
+ // Helpers
47
+ // ---------------------------------------------------------------------------
48
+ function quoteDecimals(quoteMintStr) {
49
+ return SIX_DECIMAL_MINTS.has(quoteMintStr) ? 6 : 9;
50
+ }
51
+ function amountToLamports(amount, quoteMintStr) {
52
+ const decimals = quoteDecimals(quoteMintStr);
53
+ return new bn_js_1.default(Math.floor(amount * Math.pow(10, decimals)));
54
+ }
55
+ /** Map token flag to token program ID (0=SPL, 1=Token-2022) */
56
+ function getTokenProgram(flag) {
57
+ return flag === 0 ? spl_token_1.TOKEN_PROGRAM_ID : spl_token_1.TOKEN_2022_PROGRAM_ID;
58
+ }
59
+ /** Convert sqrtPriceX64 to human price */
60
+ function sqrtPriceX64ToPrice(sqrtPriceX64, decimalsA, decimalsB) {
61
+ return raydium_sdk_v2_1.MathUtil.x64ToDecimal(sqrtPriceX64)
62
+ .pow(2)
63
+ .mul(decimal_js_1.default.pow(10, decimalsA - decimalsB));
64
+ }
65
+ /**
66
+ * Calculate initial sqrt price from reserves and sqrt bounds.
67
+ * Ported from source: meteora-damm-v2/buy.ts calculateInitSqrtPrice()
68
+ */
69
+ function calculateInitSqrtPrice(tokenAAmount, tokenBAmount, minSqrtPrice, maxSqrtPrice) {
70
+ if (tokenAAmount.isZero() || tokenBAmount.isZero()) {
71
+ throw new Error("Amount cannot be zero");
72
+ }
73
+ const amountADecimal = new decimal_js_1.default(tokenAAmount.toString());
74
+ const amountBDecimal = new decimal_js_1.default(tokenBAmount.toString());
75
+ const minSqrtPriceDecimal = new decimal_js_1.default(minSqrtPrice.toString()).div(decimal_js_1.default.pow(2, 64));
76
+ const maxSqrtPriceDecimal = new decimal_js_1.default(maxSqrtPrice.toString()).div(decimal_js_1.default.pow(2, 64));
77
+ const x = new decimal_js_1.default(1).div(maxSqrtPriceDecimal);
78
+ const y = amountBDecimal.div(amountADecimal);
79
+ const xy = x.mul(y);
80
+ const paMinusXY = minSqrtPriceDecimal.sub(xy);
81
+ const xyMinusPa = xy.sub(minSqrtPriceDecimal);
82
+ const fourY = new decimal_js_1.default(4).mul(y);
83
+ const discriminant = xyMinusPa.mul(xyMinusPa).add(fourY);
84
+ const sqrtDiscriminant = discriminant.sqrt();
85
+ const result = paMinusXY
86
+ .add(sqrtDiscriminant)
87
+ .div(new decimal_js_1.default(2))
88
+ .mul(decimal_js_1.default.pow(2, 64));
89
+ return new bn_js_1.default(result.floor().toFixed());
90
+ }
91
+ // PDA derivation helpers (from source utils/pda.ts)
92
+ function getFirstKey(key1, key2) {
93
+ const buf1 = key1.toBuffer();
94
+ const buf2 = key2.toBuffer();
95
+ return Buffer.compare(buf1, buf2) === 1 ? buf1 : buf2;
96
+ }
97
+ function getSecondKey(key1, key2) {
98
+ const buf1 = key1.toBuffer();
99
+ const buf2 = key2.toBuffer();
100
+ return Buffer.compare(buf1, buf2) === 1 ? buf2 : buf1;
101
+ }
102
+ function deriveCustomizablePoolAddress(tokenAMint, tokenBMint) {
103
+ return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("cpool"), getFirstKey(tokenAMint, tokenBMint), getSecondKey(tokenAMint, tokenBMint)], DAMM_PROGRAM_ID)[0];
104
+ }
105
+ function derivePoolAddress(config, tokenAMint, tokenBMint) {
106
+ return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("pool"), config.toBuffer(), getFirstKey(tokenAMint, tokenBMint), getSecondKey(tokenAMint, tokenBMint)], DAMM_PROGRAM_ID)[0];
107
+ }
108
+ function derivePositionAddress(positionNft) {
109
+ return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("position"), positionNft.toBuffer()], DAMM_PROGRAM_ID)[0];
110
+ }
111
+ // ---------------------------------------------------------------------------
112
+ // Adapter
113
+ // ---------------------------------------------------------------------------
114
+ class MeteoraDammV2Adapter {
115
+ name = "meteora-damm-v2";
116
+ protocol = "damm-v2";
117
+ capabilities = (0, types_1.defaultCapabilities)({
118
+ canBuy: true,
119
+ canSell: true,
120
+ canSnipe: true,
121
+ canFindPool: true,
122
+ canGetPrice: true,
123
+ canAddLiquidity: true,
124
+ canRemoveLiquidity: true,
125
+ canClaimFees: true,
126
+ canListPositions: true,
127
+ canCreatePool: true,
128
+ });
129
+ // ----- Core: buy -----
130
+ async buy(params) {
131
+ const tokenMint = (0, types_1.requireTokenMint)(params, this.name);
132
+ const { amountSol, quoteMint: quoteMintParam, poolAddress, opts } = params;
133
+ const connection = (0, config_1.getConnection)();
134
+ const wallet = (0, config_1.getWallet)();
135
+ const quoteMintStr = quoteMintParam ?? types_1.WSOL_MINT;
136
+ const poolPk = poolAddress
137
+ ? new web3_js_1.PublicKey(poolAddress)
138
+ : await this.resolvePool(tokenMint, quoteMintStr);
139
+ const cpAmm = new cp_amm_sdk_1.CpAmm(connection);
140
+ const poolState = await cpAmm.fetchPoolState(poolPk);
141
+ const baseMintPk = new web3_js_1.PublicKey(tokenMint);
142
+ const quoteMintPk = new web3_js_1.PublicKey(quoteMintStr);
143
+ const inputAmount = amountToLamports(amountSol, quoteMintStr);
144
+ const swapParams = {
145
+ payer: wallet.publicKey,
146
+ pool: poolPk,
147
+ inputTokenMint: quoteMintPk,
148
+ outputTokenMint: baseMintPk,
149
+ amountIn: inputAmount,
150
+ minimumAmountOut: new bn_js_1.default(0),
151
+ tokenAMint: poolState.tokenAMint,
152
+ tokenBMint: poolState.tokenBMint,
153
+ tokenAVault: poolState.tokenAVault,
154
+ tokenBVault: poolState.tokenBVault,
155
+ tokenAProgram: getTokenProgram(poolState.tokenAFlag),
156
+ tokenBProgram: getTokenProgram(poolState.tokenBFlag),
157
+ referralTokenAccount: null,
158
+ };
159
+ const swapTx = await cpAmm.swap(swapParams);
160
+ // Ensure output token ATA exists before swap
161
+ const baseMintAccInfo = await connection.getAccountInfo(baseMintPk);
162
+ const baseTokenProgram = baseMintAccInfo?.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)
163
+ ? spl_token_1.TOKEN_2022_PROGRAM_ID
164
+ : spl_token_1.TOKEN_PROGRAM_ID;
165
+ const outputAta = await (0, spl_token_1.getAssociatedTokenAddress)(baseMintPk, wallet.publicKey, false, baseTokenProgram);
166
+ const createAtaIx = (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(wallet.publicKey, outputAta, wallet.publicKey, baseMintPk, baseTokenProgram);
167
+ const computeLimit = opts?.computeUnitLimit ?? types_1.DEFAULT_COMPUTE_UNIT_LIMIT;
168
+ const priorityFee = opts?.priorityFeeMicroLamports ?? types_1.DEFAULT_PRIORITY_FEE_MICRO_LAMPORTS;
169
+ const ixs = [
170
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: computeLimit }),
171
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }),
172
+ createAtaIx,
173
+ ...swapTx.instructions,
174
+ ];
175
+ const result = await (0, send_rpc_1.sendAndConfirmVtx)(connection, ixs, wallet, {
176
+ addressLookupTables: opts?.addressLookupTables,
177
+ });
178
+ return {
179
+ txSignature: result.txSignature,
180
+ confirmed: result.confirmed,
181
+ amountIn: amountSol,
182
+ amountInToken: quoteMintStr,
183
+ dex: this.name,
184
+ poolAddress: poolPk.toBase58(),
185
+ };
186
+ }
187
+ // ----- Core: sell -----
188
+ async sell(params) {
189
+ const tokenMint = (0, types_1.requireTokenMint)(params, this.name);
190
+ const { percentage, quoteMint: quoteMintParam, poolAddress, opts } = params;
191
+ const connection = (0, config_1.getConnection)();
192
+ const wallet = (0, config_1.getWallet)();
193
+ const quoteMintStr = quoteMintParam ?? types_1.WSOL_MINT;
194
+ const poolPk = poolAddress
195
+ ? new web3_js_1.PublicKey(poolAddress)
196
+ : await this.resolvePool(tokenMint, quoteMintStr);
197
+ const baseMintPk = new web3_js_1.PublicKey(tokenMint);
198
+ const quoteMintPk = new web3_js_1.PublicKey(quoteMintStr);
199
+ // Determine token program for baseMint
200
+ const baseMintAccInfo = await connection.getAccountInfo(baseMintPk);
201
+ if (!baseMintAccInfo) {
202
+ throw new Error(`Token mint not found: ${tokenMint}`);
203
+ }
204
+ const baseTokenProgram = baseMintAccInfo.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)
205
+ ? spl_token_1.TOKEN_2022_PROGRAM_ID
206
+ : spl_token_1.TOKEN_PROGRAM_ID;
207
+ // Get token balance
208
+ const ata = await (0, spl_token_1.getAssociatedTokenAddress)(baseMintPk, wallet.publicKey, false, baseTokenProgram);
209
+ const tokenAccount = await (0, spl_token_1.getAccount)(connection, ata, "confirmed", baseTokenProgram);
210
+ const balance = tokenAccount.amount;
211
+ // Calculate sell amount based on percentage
212
+ const sellAmount = new bn_js_1.default(Math.floor((Number(balance) * percentage) / 100).toString());
213
+ if (sellAmount.isZero()) {
214
+ throw new Error(`No balance to sell for ${tokenMint}`);
215
+ }
216
+ const cpAmm = new cp_amm_sdk_1.CpAmm(connection);
217
+ const poolState = await cpAmm.fetchPoolState(poolPk);
218
+ const swapParams = {
219
+ payer: wallet.publicKey,
220
+ pool: poolPk,
221
+ inputTokenMint: baseMintPk,
222
+ outputTokenMint: quoteMintPk,
223
+ amountIn: sellAmount,
224
+ minimumAmountOut: new bn_js_1.default(0),
225
+ tokenAMint: poolState.tokenAMint,
226
+ tokenBMint: poolState.tokenBMint,
227
+ tokenAVault: poolState.tokenAVault,
228
+ tokenBVault: poolState.tokenBVault,
229
+ tokenAProgram: getTokenProgram(poolState.tokenAFlag),
230
+ tokenBProgram: getTokenProgram(poolState.tokenBFlag),
231
+ referralTokenAccount: null,
232
+ };
233
+ const swapTx = await cpAmm.swap(swapParams);
234
+ const computeLimit = opts?.computeUnitLimit ?? types_1.DEFAULT_COMPUTE_UNIT_LIMIT;
235
+ const priorityFee = opts?.priorityFeeMicroLamports ?? types_1.DEFAULT_PRIORITY_FEE_MICRO_LAMPORTS;
236
+ const ixs = [
237
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: computeLimit }),
238
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }),
239
+ ...swapTx.instructions,
240
+ ];
241
+ const rpcResult = await (0, send_rpc_1.sendAndConfirmVtx)(connection, ixs, wallet, {
242
+ addressLookupTables: opts?.addressLookupTables,
243
+ });
244
+ // Get actual token decimals for human-readable amount
245
+ let tokenDecimals = 9; // default
246
+ try {
247
+ const mintData = await connection.getTokenSupply(baseMintPk);
248
+ tokenDecimals = mintData.value.decimals;
249
+ }
250
+ catch { /* fallback to 9 */ }
251
+ const humanAmount = Number(sellAmount.toString()) / Math.pow(10, tokenDecimals);
252
+ return {
253
+ txSignature: rpcResult.txSignature,
254
+ confirmed: rpcResult.confirmed,
255
+ amountIn: humanAmount,
256
+ amountInToken: tokenMint,
257
+ dex: this.name,
258
+ poolAddress: poolPk.toBase58(),
259
+ };
260
+ }
261
+ // ----- Snipe -----
262
+ async snipe(params) {
263
+ const { tokenMint, amountSol, poolAddress, quoteMint: quoteMintParam, tipSol, opts } = params;
264
+ const connection = (0, config_1.getConnection)();
265
+ const wallet = (0, config_1.getWallet)();
266
+ const quoteMintStr = quoteMintParam ?? types_1.WSOL_MINT;
267
+ const poolPk = new web3_js_1.PublicKey(poolAddress);
268
+ const baseMintPk = new web3_js_1.PublicKey(tokenMint);
269
+ const quoteMintPk = new web3_js_1.PublicKey(quoteMintStr);
270
+ const inputAmount = amountToLamports(amountSol, quoteMintStr);
271
+ const cpAmm = new cp_amm_sdk_1.CpAmm(connection);
272
+ const poolState = await cpAmm.fetchPoolState(poolPk);
273
+ const swapParams = {
274
+ payer: wallet.publicKey,
275
+ pool: poolPk,
276
+ inputTokenMint: quoteMintPk,
277
+ outputTokenMint: baseMintPk,
278
+ amountIn: inputAmount,
279
+ minimumAmountOut: new bn_js_1.default(0), // unlimited slippage for snipe
280
+ tokenAMint: poolState.tokenAMint,
281
+ tokenBMint: poolState.tokenBMint,
282
+ tokenAVault: poolState.tokenAVault,
283
+ tokenBVault: poolState.tokenBVault,
284
+ tokenAProgram: getTokenProgram(poolState.tokenAFlag),
285
+ tokenBProgram: getTokenProgram(poolState.tokenBFlag),
286
+ referralTokenAccount: null,
287
+ };
288
+ const swapTx = await cpAmm.swap(swapParams);
289
+ const computeLimit = opts?.computeUnitLimit ?? types_1.DEFAULT_COMPUTE_UNIT_LIMIT;
290
+ const priorityFee = opts?.priorityFeeMicroLamports ?? 40_000_000; // high priority for snipe
291
+ const ixs = [
292
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: computeLimit }),
293
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }),
294
+ ...swapTx.instructions,
295
+ ];
296
+ const blockhash = await connection.getLatestBlockhash();
297
+ const results = await (0, landing_1.landTransaction)(ixs, wallet, blockhash, {
298
+ dex: this.name,
299
+ operation: "snipe",
300
+ tipSol,
301
+ addressLookupTables: opts?.addressLookupTables,
302
+ });
303
+ const accepted = results.find((r) => r.accepted);
304
+ return {
305
+ txSignature: accepted?.signature ?? "",
306
+ confirmed: !!accepted?.accepted,
307
+ amountIn: amountSol,
308
+ amountInToken: quoteMintStr,
309
+ dex: this.name,
310
+ poolAddress,
311
+ };
312
+ }
313
+ // ----- buildSwapIxs -----
314
+ async buildSwapIxs(params) {
315
+ const connection = (0, config_1.getConnection)();
316
+ const wallet = (0, config_1.getWallet)();
317
+ const cpAmm = new cp_amm_sdk_1.CpAmm(connection);
318
+ if ("percentage" in params) {
319
+ // Sell path
320
+ const sellParams = params;
321
+ const tokenMint = (0, types_1.requireTokenMint)(sellParams, this.name);
322
+ const quoteMintStr = sellParams.quoteMint ?? types_1.WSOL_MINT;
323
+ const poolPk = sellParams.poolAddress
324
+ ? new web3_js_1.PublicKey(sellParams.poolAddress)
325
+ : await this.resolvePool(tokenMint, quoteMintStr);
326
+ const baseMintPk = new web3_js_1.PublicKey(tokenMint);
327
+ const quoteMintPk = new web3_js_1.PublicKey(quoteMintStr);
328
+ // Get balance
329
+ const baseMintAccInfo = await connection.getAccountInfo(baseMintPk);
330
+ const baseTokenProgram = baseMintAccInfo?.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID) ? spl_token_1.TOKEN_2022_PROGRAM_ID : spl_token_1.TOKEN_PROGRAM_ID;
331
+ const ata = await (0, spl_token_1.getAssociatedTokenAddress)(baseMintPk, wallet.publicKey, false, baseTokenProgram);
332
+ const tokenAccount = await (0, spl_token_1.getAccount)(connection, ata, "confirmed", baseTokenProgram);
333
+ const sellAmount = new bn_js_1.default(Math.floor((Number(tokenAccount.amount) * sellParams.percentage) / 100).toString());
334
+ const poolState = await cpAmm.fetchPoolState(poolPk);
335
+ const swapTx = await cpAmm.swap({
336
+ payer: wallet.publicKey,
337
+ pool: poolPk,
338
+ inputTokenMint: baseMintPk,
339
+ outputTokenMint: quoteMintPk,
340
+ amountIn: sellAmount,
341
+ minimumAmountOut: new bn_js_1.default(0),
342
+ tokenAMint: poolState.tokenAMint,
343
+ tokenBMint: poolState.tokenBMint,
344
+ tokenAVault: poolState.tokenAVault,
345
+ tokenBVault: poolState.tokenBVault,
346
+ tokenAProgram: getTokenProgram(poolState.tokenAFlag),
347
+ tokenBProgram: getTokenProgram(poolState.tokenBFlag),
348
+ referralTokenAccount: null,
349
+ });
350
+ return { instructions: swapTx.instructions, signers: [] };
351
+ }
352
+ // Buy path
353
+ const buyParams = params;
354
+ const tokenMint = (0, types_1.requireTokenMint)(buyParams, this.name);
355
+ const quoteMintStr = buyParams.quoteMint ?? types_1.WSOL_MINT;
356
+ const poolPk = buyParams.poolAddress
357
+ ? new web3_js_1.PublicKey(buyParams.poolAddress)
358
+ : await this.resolvePool(tokenMint, quoteMintStr);
359
+ const baseMintPk = new web3_js_1.PublicKey(tokenMint);
360
+ const quoteMintPk = new web3_js_1.PublicKey(quoteMintStr);
361
+ const inputAmount = amountToLamports(buyParams.amountSol, quoteMintStr);
362
+ const poolState = await cpAmm.fetchPoolState(poolPk);
363
+ const swapTx = await cpAmm.swap({
364
+ payer: wallet.publicKey,
365
+ pool: poolPk,
366
+ inputTokenMint: quoteMintPk,
367
+ outputTokenMint: baseMintPk,
368
+ amountIn: inputAmount,
369
+ minimumAmountOut: new bn_js_1.default(0),
370
+ tokenAMint: poolState.tokenAMint,
371
+ tokenBMint: poolState.tokenBMint,
372
+ tokenAVault: poolState.tokenAVault,
373
+ tokenBVault: poolState.tokenBVault,
374
+ tokenAProgram: getTokenProgram(poolState.tokenAFlag),
375
+ tokenBProgram: getTokenProgram(poolState.tokenBFlag),
376
+ referralTokenAccount: null,
377
+ });
378
+ return { instructions: swapTx.instructions, signers: [] };
379
+ }
380
+ // ----- findPool -----
381
+ async findPool(baseMint, quoteMint) {
382
+ const connection = (0, config_1.getConnection)();
383
+ const baseMintPk = new web3_js_1.PublicKey(baseMint);
384
+ const quoteMintPk = new web3_js_1.PublicKey(quoteMint ?? types_1.WSOL_MINT);
385
+ const cpAmm = new cp_amm_sdk_1.CpAmm(connection);
386
+ // Try customizable pool PDA first (most common)
387
+ const customPoolAddr = deriveCustomizablePoolAddress(baseMintPk, quoteMintPk);
388
+ try {
389
+ const poolState = await cpAmm.fetchPoolState(customPoolAddr);
390
+ if (poolState) {
391
+ return {
392
+ address: customPoolAddr.toBase58(),
393
+ dex: this.name,
394
+ protocol: this.protocol,
395
+ baseMint,
396
+ quoteMint: quoteMint ?? types_1.WSOL_MINT,
397
+ baseDecimals: 0, // CpAmm poolState doesn't directly expose decimals
398
+ quoteDecimals: 0,
399
+ };
400
+ }
401
+ }
402
+ catch {
403
+ // Not found at customizable PDA — fall through
404
+ }
405
+ // Could iterate config-based pools but that's expensive (requires getAllConfigs).
406
+ // For now, return null — callers should provide poolAddress when possible.
407
+ return null;
408
+ }
409
+ // ----- getPrice -----
410
+ async getPrice(poolAddress) {
411
+ const connection = (0, config_1.getConnection)();
412
+ const poolPk = new web3_js_1.PublicKey(poolAddress);
413
+ const cpAmm = new cp_amm_sdk_1.CpAmm(connection);
414
+ const poolState = await cpAmm.fetchPoolState(poolPk);
415
+ const sqrtMaxPrice = poolState.sqrtMaxPrice;
416
+ const sqrtMinPrice = poolState.sqrtMinPrice;
417
+ // Fetch vault balances for price calculation
418
+ const [balA, balB] = await Promise.all([
419
+ connection.getTokenAccountBalance(poolState.tokenAVault),
420
+ connection.getTokenAccountBalance(poolState.tokenBVault),
421
+ ]);
422
+ const tokenAAmount = new bn_js_1.default(balA.value.amount);
423
+ const tokenBAmount = new bn_js_1.default(balB.value.amount);
424
+ const decimalsA = balA.value.decimals;
425
+ const decimalsB = balB.value.decimals;
426
+ let price;
427
+ if (tokenAAmount.isZero() || tokenBAmount.isZero()) {
428
+ price = 0;
429
+ }
430
+ else {
431
+ const sqrtPriceX64 = calculateInitSqrtPrice(tokenAAmount, tokenBAmount, sqrtMinPrice, sqrtMaxPrice);
432
+ const priceDecimal = sqrtPriceX64ToPrice(sqrtPriceX64, decimalsA, decimalsB);
433
+ price = Number(priceDecimal);
434
+ }
435
+ return {
436
+ price,
437
+ baseMint: poolState.tokenAMint.toBase58(),
438
+ quoteMint: poolState.tokenBMint.toBase58(),
439
+ source: "on-chain",
440
+ poolAddress,
441
+ timestamp: Date.now(),
442
+ };
443
+ }
444
+ // ----- LP: addLiquidity -----
445
+ /**
446
+ * Add liquidity to an existing DAMM v2 pool by creating a new position.
447
+ *
448
+ * DAMM v2 uses full-range positions (MIN_SQRT_PRICE → MAX_SQRT_PRICE).
449
+ *
450
+ * The correct pattern (from zodiac/operator meteora-executor.ts):
451
+ * 1. Read the pool's actual sqrtPrice from on-chain state
452
+ * 2. Compute both token amounts proportionally from pool vault reserves
453
+ * 3. Pass both amounts + pool's sqrtPrice to getLiquidityDelta()
454
+ *
455
+ * The SDK's getLiquidityDelta is the black box — give it both max amounts
456
+ * + the current sqrtPrice, it figures out the correct liquidity delta.
457
+ */
458
+ async addLiquidity(params) {
459
+ const { poolAddress, opts } = params;
460
+ const inputAmountSol = params.amountSol ?? params.amountA;
461
+ const inputAmountToken = params.amountToken ?? params.amountB;
462
+ if ((inputAmountSol === undefined || inputAmountSol === 0) &&
463
+ (inputAmountToken === undefined || inputAmountToken === 0)) {
464
+ throw new Error("At least one of --amount-sol or --amount-token is required for DAMM v2 addLiquidity");
465
+ }
466
+ const connection = (0, config_1.getConnection)();
467
+ const wallet = (0, config_1.getWallet)();
468
+ const poolPk = new web3_js_1.PublicKey(poolAddress);
469
+ const cpAmm = new cp_amm_sdk_1.CpAmm(connection);
470
+ const poolState = await cpAmm.fetchPoolState(poolPk);
471
+ // Pool's current sqrt price from on-chain state — the ground truth
472
+ const currentSqrtPrice = poolState.sqrtPrice;
473
+ // Fetch decimals for both tokens
474
+ const tokenAMint = poolState.tokenAMint;
475
+ const tokenBMint = poolState.tokenBMint;
476
+ const tokenAProgram = (0, cp_amm_sdk_1.getTokenProgram)(poolState.tokenAFlag);
477
+ const tokenBProgram = (0, cp_amm_sdk_1.getTokenProgram)(poolState.tokenBFlag);
478
+ const [tokenAMintInfo, tokenBMintInfo] = await Promise.all([
479
+ connection.getAccountInfo(tokenAMint),
480
+ connection.getAccountInfo(tokenBMint),
481
+ ]);
482
+ if (!tokenAMintInfo || !tokenBMintInfo) {
483
+ throw new Error("Failed to fetch mint account info");
484
+ }
485
+ const mintA = (0, spl_token_1.unpackMint)(tokenAMint, tokenAMintInfo, tokenAMintInfo.owner);
486
+ const mintB = (0, spl_token_1.unpackMint)(tokenBMint, tokenBMintInfo, tokenBMintInfo.owner);
487
+ const decimalsA = mintA.decimals;
488
+ const decimalsB = mintB.decimals;
489
+ // Detect Token-2022 transfer fees
490
+ let tokenAInfo;
491
+ let tokenBInfo;
492
+ if (tokenAMintInfo.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)) {
493
+ const epochInfo = await connection.getEpochInfo();
494
+ tokenAInfo = { mint: mintA, currentEpoch: epochInfo.epoch };
495
+ }
496
+ if (tokenBMintInfo.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)) {
497
+ const epochInfo = await connection.getEpochInfo();
498
+ tokenBInfo = { mint: mintB, currentEpoch: epochInfo.epoch };
499
+ }
500
+ // Read pool vault balances for proportional calculation
501
+ const [balA, balB] = await Promise.all([
502
+ connection.getTokenAccountBalance(poolState.tokenAVault),
503
+ connection.getTokenAccountBalance(poolState.tokenBVault),
504
+ ]);
505
+ const reserveA = new bn_js_1.default(balA.value.amount);
506
+ const reserveB = new bn_js_1.default(balB.value.amount);
507
+ // Compute both token amounts. When only one side is given,
508
+ // derive the other proportionally from pool vault reserves.
509
+ let tokenAAmount;
510
+ let tokenBAmount;
511
+ if (inputAmountSol !== undefined && inputAmountSol > 0 &&
512
+ inputAmountToken !== undefined && inputAmountToken > 0) {
513
+ // Both sides provided explicitly
514
+ tokenAAmount = new bn_js_1.default(new decimal_js_1.default(inputAmountSol).mul(decimal_js_1.default.pow(10, decimalsA)).floor().toFixed());
515
+ tokenBAmount = new bn_js_1.default(new decimal_js_1.default(inputAmountToken).mul(decimal_js_1.default.pow(10, decimalsB)).floor().toFixed());
516
+ }
517
+ else if (inputAmountSol !== undefined && inputAmountSol > 0) {
518
+ // Only SOL provided — figure out which side SOL is, derive the other
519
+ const solIsTokenA = tokenAMint.toBase58() === types_1.WSOL_MINT;
520
+ if (solIsTokenA) {
521
+ tokenAAmount = new bn_js_1.default(new decimal_js_1.default(inputAmountSol).mul(decimal_js_1.default.pow(10, decimalsA)).floor().toFixed());
522
+ // Derive B proportionally: tokenBAmount = tokenAAmount * reserveB / reserveA
523
+ if (reserveA.isZero())
524
+ throw new Error("Pool has zero token A reserves");
525
+ tokenBAmount = tokenAAmount.mul(reserveB).div(reserveA);
526
+ }
527
+ else {
528
+ // SOL is token B
529
+ tokenBAmount = new bn_js_1.default(new decimal_js_1.default(inputAmountSol).mul(decimal_js_1.default.pow(10, decimalsB)).floor().toFixed());
530
+ // Derive A proportionally: tokenAAmount = tokenBAmount * reserveA / reserveB
531
+ if (reserveB.isZero())
532
+ throw new Error("Pool has zero token B reserves");
533
+ tokenAAmount = tokenBAmount.mul(reserveA).div(reserveB);
534
+ }
535
+ }
536
+ else {
537
+ // Only token amount provided — figure out which side is non-SOL
538
+ const solIsTokenA = tokenAMint.toBase58() === types_1.WSOL_MINT;
539
+ if (solIsTokenA) {
540
+ // Non-SOL token is B side
541
+ tokenBAmount = new bn_js_1.default(new decimal_js_1.default(inputAmountToken).mul(decimal_js_1.default.pow(10, decimalsB)).floor().toFixed());
542
+ if (reserveB.isZero())
543
+ throw new Error("Pool has zero token B reserves");
544
+ tokenAAmount = tokenBAmount.mul(reserveA).div(reserveB);
545
+ }
546
+ else {
547
+ // Non-SOL token is A side (or neither is SOL — treat token as A)
548
+ tokenAAmount = new bn_js_1.default(new decimal_js_1.default(inputAmountToken).mul(decimal_js_1.default.pow(10, decimalsA)).floor().toFixed());
549
+ if (reserveA.isZero())
550
+ throw new Error("Pool has zero token A reserves");
551
+ tokenBAmount = tokenAAmount.mul(reserveB).div(reserveA);
552
+ }
553
+ }
554
+ // Compute liquidityDelta using the pool's actual on-chain sqrtPrice
555
+ // This is the same pattern as zodiac/operator meteora-executor.ts
556
+ const liquidityDelta = cpAmm.getLiquidityDelta({
557
+ maxAmountTokenA: tokenAAmount,
558
+ maxAmountTokenB: tokenBAmount,
559
+ sqrtPrice: currentSqrtPrice,
560
+ sqrtMinPrice: cp_amm_sdk_1.MIN_SQRT_PRICE,
561
+ sqrtMaxPrice: cp_amm_sdk_1.MAX_SQRT_PRICE,
562
+ tokenAInfo,
563
+ tokenBInfo,
564
+ });
565
+ if (liquidityDelta.isZero()) {
566
+ throw new Error("Computed liquidityDelta is zero — amount too small for this pool");
567
+ }
568
+ // Generate a new position NFT keypair
569
+ const positionNft = web3_js_1.Keypair.generate();
570
+ // Slippage thresholds
571
+ const slippageBps = opts?.slippageBps ?? types_1.DEFAULT_SLIPPAGE_BPS;
572
+ const tokenAThreshold = tokenAAmount.muln(10000 - slippageBps).divn(10000);
573
+ const tokenBThreshold = tokenBAmount.muln(10000 - slippageBps).divn(10000);
574
+ console.log(` tokenA: ${tokenAAmount.toString()} (${tokenAMint.toBase58().slice(0, 8)}...)`);
575
+ console.log(` tokenB: ${tokenBAmount.toString()} (${tokenBMint.toBase58().slice(0, 8)}...)`);
576
+ console.log(` liqDelta: ${liquidityDelta.toString()}`);
577
+ const addLiqTx = await cpAmm.createPositionAndAddLiquidity({
578
+ owner: wallet.publicKey,
579
+ pool: poolPk,
580
+ positionNft: positionNft.publicKey,
581
+ liquidityDelta,
582
+ maxAmountTokenA: tokenAAmount,
583
+ maxAmountTokenB: tokenBAmount,
584
+ tokenAAmountThreshold: tokenAThreshold,
585
+ tokenBAmountThreshold: tokenBThreshold,
586
+ tokenAMint,
587
+ tokenBMint,
588
+ tokenAProgram,
589
+ tokenBProgram,
590
+ });
591
+ const computeLimit = opts?.computeUnitLimit ?? 400_000;
592
+ const priorityFee = opts?.priorityFeeMicroLamports ?? types_1.DEFAULT_PRIORITY_FEE_MICRO_LAMPORTS;
593
+ const ixs = [
594
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: computeLimit }),
595
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }),
596
+ ...addLiqTx.instructions,
597
+ ];
598
+ // Submit via RPC send+confirm
599
+ const result = await (0, send_rpc_1.sendAndConfirmVtx)(connection, ixs, wallet, {
600
+ addressLookupTables: opts?.addressLookupTables,
601
+ extraSigners: [positionNft],
602
+ });
603
+ return {
604
+ txSignature: result.txSignature,
605
+ confirmed: result.confirmed,
606
+ };
607
+ }
608
+ // ----- LP: removeLiquidity -----
609
+ /**
610
+ * Remove liquidity from all DAMM v2 positions on a pool.
611
+ *
612
+ * Uses getPositionsByUser() to discover positions on-chain (no database
613
+ * dependency), then calls removeAllLiquidityAndClosePosition() for each.
614
+ *
615
+ * Params:
616
+ * - poolAddress: the pool to remove liquidity from
617
+ * - percentage: 100 = remove all, <100 = partial (only full removal
618
+ * supported by the SDK's close-position method — partial uses removeLiquidity)
619
+ *
620
+ * Ported from: 100x-algo-bots/trading-modules/meteora-damm-v2/pool.ts
621
+ * removeAllLiquidityAndCloseAllPositions()
622
+ */
623
+ async removeLiquidity(params) {
624
+ const { poolAddress, percentage, opts } = params;
625
+ const connection = (0, config_1.getConnection)();
626
+ const wallet = (0, config_1.getWallet)();
627
+ const poolPk = new web3_js_1.PublicKey(poolAddress);
628
+ const cpAmm = new cp_amm_sdk_1.CpAmm(connection);
629
+ // Discover user positions on-chain — no database needed
630
+ const userPositions = await cpAmm.getPositionsByUser(wallet.publicKey);
631
+ // Filter to positions belonging to this pool
632
+ const poolPositions = userPositions.filter((p) => p.positionState.pool.toBase58() === poolAddress);
633
+ if (poolPositions.length === 0) {
634
+ throw new Error(`No positions found for pool ${poolAddress}`);
635
+ }
636
+ const poolState = await cpAmm.fetchPoolState(poolPk);
637
+ const currentSlot = await connection.getSlot();
638
+ const currentPoint = new bn_js_1.default(currentSlot);
639
+ let lastSignature = "";
640
+ let anyConfirmed = false;
641
+ for (const pos of poolPositions) {
642
+ const positionState = await cpAmm.fetchPositionState(pos.position);
643
+ // Check total liquidity
644
+ const totalLiquidity = positionState.unlockedLiquidity
645
+ .add(positionState.vestedLiquidity)
646
+ .add(positionState.permanentLockedLiquidity);
647
+ if (totalLiquidity.isZero()) {
648
+ continue; // skip empty positions
649
+ }
650
+ // Skip permanently locked positions — cannot close
651
+ if (!positionState.permanentLockedLiquidity.isZero()) {
652
+ console.log(`Skipping position ${pos.position.toBase58()} — has permanent locked liquidity`);
653
+ continue;
654
+ }
655
+ const computeLimit = opts?.computeUnitLimit ?? 400_000;
656
+ const priorityFee = opts?.priorityFeeMicroLamports ?? types_1.DEFAULT_PRIORITY_FEE_MICRO_LAMPORTS;
657
+ if (percentage >= 100) {
658
+ // Full removal + close position
659
+ const closeTx = await cpAmm.removeAllLiquidityAndClosePosition({
660
+ owner: wallet.publicKey,
661
+ position: pos.position,
662
+ positionNftAccount: pos.positionNftAccount,
663
+ positionState,
664
+ poolState,
665
+ tokenAAmountThreshold: new bn_js_1.default(0),
666
+ tokenBAmountThreshold: new bn_js_1.default(0),
667
+ currentPoint,
668
+ vestings: [],
669
+ });
670
+ const ixs = [
671
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: computeLimit }),
672
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }),
673
+ ...closeTx.instructions,
674
+ ];
675
+ // Submit via RPC send+confirm
676
+ const result = await (0, send_rpc_1.sendAndConfirmVtx)(connection, ixs, wallet, {
677
+ addressLookupTables: opts?.addressLookupTables,
678
+ });
679
+ if (result.txSignature)
680
+ lastSignature = result.txSignature;
681
+ if (result.confirmed)
682
+ anyConfirmed = true;
683
+ }
684
+ else {
685
+ // Partial removal — calculate proportional liquidityDelta
686
+ const removableLiquidity = positionState.unlockedLiquidity.add(positionState.vestedLiquidity);
687
+ const liquidityDelta = removableLiquidity.muln(percentage).divn(100);
688
+ if (liquidityDelta.isZero()) {
689
+ continue;
690
+ }
691
+ const removeTx = await cpAmm.removeLiquidity({
692
+ owner: wallet.publicKey,
693
+ position: pos.position,
694
+ pool: poolPk,
695
+ positionNftAccount: pos.positionNftAccount,
696
+ liquidityDelta,
697
+ tokenAAmountThreshold: new bn_js_1.default(0),
698
+ tokenBAmountThreshold: new bn_js_1.default(0),
699
+ tokenAMint: poolState.tokenAMint,
700
+ tokenBMint: poolState.tokenBMint,
701
+ tokenAVault: poolState.tokenAVault,
702
+ tokenBVault: poolState.tokenBVault,
703
+ tokenAProgram: (0, cp_amm_sdk_1.getTokenProgram)(poolState.tokenAFlag),
704
+ tokenBProgram: (0, cp_amm_sdk_1.getTokenProgram)(poolState.tokenBFlag),
705
+ vestings: [],
706
+ currentPoint,
707
+ });
708
+ const ixs = [
709
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: computeLimit }),
710
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }),
711
+ ...removeTx.instructions,
712
+ ];
713
+ // Submit via RPC send+confirm
714
+ const result = await (0, send_rpc_1.sendAndConfirmVtx)(connection, ixs, wallet, {
715
+ addressLookupTables: opts?.addressLookupTables,
716
+ });
717
+ if (result.txSignature)
718
+ lastSignature = result.txSignature;
719
+ if (result.confirmed)
720
+ anyConfirmed = true;
721
+ }
722
+ }
723
+ return {
724
+ txSignature: lastSignature,
725
+ confirmed: anyConfirmed,
726
+ };
727
+ }
728
+ // ----- LP: claimFees -----
729
+ /**
730
+ * Claim unclaimed fees from all DAMM v2 positions on a pool.
731
+ *
732
+ * Uses getPositionsByUser() + on-chain fee math (no database dependency).
733
+ * Iterates all positions for the given pool, skips those with zero fees.
734
+ *
735
+ * Ported from: 100x-algo-bots/trading-modules/meteora-damm-v2/pool.ts
736
+ * claimAllPositionFees() + getUnClaimLpFee()
737
+ */
738
+ async claimFees(poolAddress, _positionAddress) {
739
+ const connection = (0, config_1.getConnection)();
740
+ const wallet = (0, config_1.getWallet)();
741
+ const poolPk = new web3_js_1.PublicKey(poolAddress);
742
+ const cpAmm = new cp_amm_sdk_1.CpAmm(connection);
743
+ const userPositions = await cpAmm.getPositionsByUser(wallet.publicKey);
744
+ // Filter to positions belonging to this pool
745
+ const poolPositions = userPositions.filter((p) => p.positionState.pool.toBase58() === poolAddress);
746
+ if (poolPositions.length === 0) {
747
+ throw new Error(`No positions found for pool ${poolAddress}`);
748
+ }
749
+ const poolState = await cpAmm.fetchPoolState(poolPk);
750
+ let lastSignature = "";
751
+ let anyConfirmed = false;
752
+ for (const pos of poolPositions) {
753
+ const positionState = await cpAmm.fetchPositionState(pos.position);
754
+ // Calculate unclaimed fees using on-chain math
755
+ // Ported from source getUnClaimLpFee()
756
+ const totalPositionLiquidity = positionState.unlockedLiquidity
757
+ .add(positionState.vestedLiquidity)
758
+ .add(positionState.permanentLockedLiquidity);
759
+ const feeAPerTokenStored = new bn_js_1.default(Buffer.from(poolState.feeAPerLiquidity).reverse()).sub(new bn_js_1.default(Buffer.from(positionState.feeAPerTokenCheckpoint).reverse()));
760
+ const feeBPerTokenStored = new bn_js_1.default(Buffer.from(poolState.feeBPerLiquidity).reverse()).sub(new bn_js_1.default(Buffer.from(positionState.feeBPerTokenCheckpoint).reverse()));
761
+ const feeA = positionState.feeAPending.add(totalPositionLiquidity.mul(feeAPerTokenStored).shrn(cp_amm_sdk_1.LIQUIDITY_SCALE));
762
+ const feeB = positionState.feeBPending.add(totalPositionLiquidity.mul(feeBPerTokenStored).shrn(cp_amm_sdk_1.LIQUIDITY_SCALE));
763
+ // Skip if no fees to claim
764
+ if (feeA.isZero() && feeB.isZero()) {
765
+ continue;
766
+ }
767
+ const claimTx = await cpAmm.claimPositionFee2({
768
+ owner: wallet.publicKey,
769
+ pool: poolPk,
770
+ position: pos.position,
771
+ receiver: wallet.publicKey,
772
+ positionNftAccount: pos.positionNftAccount,
773
+ tokenAVault: poolState.tokenAVault,
774
+ tokenBVault: poolState.tokenBVault,
775
+ tokenAMint: poolState.tokenAMint,
776
+ tokenBMint: poolState.tokenBMint,
777
+ tokenAProgram: (0, cp_amm_sdk_1.getTokenProgram)(poolState.tokenAFlag),
778
+ tokenBProgram: (0, cp_amm_sdk_1.getTokenProgram)(poolState.tokenBFlag),
779
+ });
780
+ const ixs = [
781
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }),
782
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: types_1.DEFAULT_PRIORITY_FEE_MICRO_LAMPORTS }),
783
+ ...claimTx.instructions,
784
+ ];
785
+ // Submit via RPC send+confirm
786
+ const result = await (0, send_rpc_1.sendAndConfirmVtx)(connection, ixs, wallet);
787
+ if (result.txSignature)
788
+ lastSignature = result.txSignature;
789
+ if (result.confirmed)
790
+ anyConfirmed = true;
791
+ }
792
+ return {
793
+ txSignature: lastSignature,
794
+ confirmed: anyConfirmed,
795
+ };
796
+ }
797
+ // ----- Pool creation: createCustomPool -----
798
+ /**
799
+ * Create a DAMM v2 pool with full fee configuration (custom pool).
800
+ *
801
+ * Uses `cpAmm.createCustomPool()` — allows setting fee schedule, price range,
802
+ * activation params, dynamic fee, and collect-fee mode.
803
+ *
804
+ * This is the primary method for token launches. Creates a customizable pool
805
+ * with MIN_SQRT_PRICE → MAX_SQRT_PRICE full-range liquidity.
806
+ *
807
+ * Ported from: 100x-algo-bots/trading-modules/meteora-damm-v2/create.ts
808
+ * createDammV2BalancedPool()
809
+ */
810
+ async createCustomPool(params) {
811
+ const { baseMint, quoteMint: quoteMintParam, baseAmount, quoteAmount, poolFees, collectFeeMode = 1, activationType = 1, activationPoint = null, hasAlphaVault = false, opts, } = params;
812
+ const connection = (0, config_1.getConnection)();
813
+ const wallet = (0, config_1.getWallet)();
814
+ const quoteMintStr = quoteMintParam ?? types_1.WSOL_MINT;
815
+ const baseMintPk = new web3_js_1.PublicKey(baseMint);
816
+ const quoteMintPk = new web3_js_1.PublicKey(quoteMintStr);
817
+ // Fetch mint info for both tokens
818
+ const [baseMintAccInfo, quoteMintAccInfo] = await Promise.all([
819
+ connection.getAccountInfo(baseMintPk),
820
+ connection.getAccountInfo(quoteMintPk),
821
+ ]);
822
+ if (!baseMintAccInfo)
823
+ throw new Error(`Base mint not found: ${baseMint}`);
824
+ if (!quoteMintAccInfo)
825
+ throw new Error(`Quote mint not found: ${quoteMintStr}`);
826
+ const baseMintData = (0, spl_token_1.unpackMint)(baseMintPk, baseMintAccInfo, baseMintAccInfo.owner);
827
+ const quoteMintData = (0, spl_token_1.unpackMint)(quoteMintPk, quoteMintAccInfo, quoteMintAccInfo.owner);
828
+ const baseDecimals = baseMintData.decimals;
829
+ const quoteDecimalCount = quoteDecimals(quoteMintStr);
830
+ const baseTokenProgram = baseMintAccInfo.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)
831
+ ? spl_token_1.TOKEN_2022_PROGRAM_ID
832
+ : spl_token_1.TOKEN_PROGRAM_ID;
833
+ const quoteTokenProgram = quoteMintAccInfo.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)
834
+ ? spl_token_1.TOKEN_2022_PROGRAM_ID
835
+ : spl_token_1.TOKEN_PROGRAM_ID;
836
+ // Detect Token-2022 transfer fees
837
+ let baseTokenInfo;
838
+ let quoteTokenInfo;
839
+ if (baseMintAccInfo.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)) {
840
+ const epochInfo = await connection.getEpochInfo();
841
+ baseTokenInfo = { mint: baseMintData, currentEpoch: epochInfo.epoch };
842
+ }
843
+ if (quoteMintAccInfo.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)) {
844
+ const epochInfo = await connection.getEpochInfo();
845
+ quoteTokenInfo = { mint: quoteMintData, currentEpoch: epochInfo.epoch };
846
+ }
847
+ // Convert human amounts to lamports
848
+ let tokenAAmount = new bn_js_1.default(new decimal_js_1.default(baseAmount).mul(decimal_js_1.default.pow(10, baseDecimals)).floor().toFixed());
849
+ let tokenBAmount = new bn_js_1.default(new decimal_js_1.default(quoteAmount).mul(decimal_js_1.default.pow(10, quoteDecimalCount)).floor().toFixed());
850
+ // Subtract transfer fees for Token-2022 tokens
851
+ if (baseTokenInfo) {
852
+ tokenAAmount = tokenAAmount.sub((0, cp_amm_sdk_1.calculateTransferFeeIncludedAmount)(tokenAAmount, baseTokenInfo.mint, baseTokenInfo.currentEpoch).transferFee);
853
+ }
854
+ if (quoteTokenInfo) {
855
+ tokenBAmount = tokenBAmount.sub((0, cp_amm_sdk_1.calculateTransferFeeIncludedAmount)(tokenBAmount, quoteTokenInfo.mint, quoteTokenInfo.currentEpoch).transferFee);
856
+ }
857
+ const cpAmm = new cp_amm_sdk_1.CpAmm(connection);
858
+ // Calculate initial sqrt price
859
+ const initPrice = params.initPrice ?? quoteAmount / baseAmount;
860
+ const initSqrtPrice = (0, cp_amm_sdk_1.getSqrtPriceFromPrice)(initPrice.toString(), baseDecimals, quoteDecimalCount);
861
+ // Full-range liquidity
862
+ const minSqrtPrice = cp_amm_sdk_1.MIN_SQRT_PRICE;
863
+ const maxSqrtPrice = cp_amm_sdk_1.MAX_SQRT_PRICE;
864
+ const liquidityDelta = cpAmm.getLiquidityDelta({
865
+ maxAmountTokenA: tokenAAmount,
866
+ maxAmountTokenB: tokenBAmount,
867
+ sqrtPrice: initSqrtPrice,
868
+ sqrtMinPrice: minSqrtPrice,
869
+ sqrtMaxPrice: maxSqrtPrice,
870
+ tokenAInfo: baseTokenInfo,
871
+ });
872
+ // Build fee params
873
+ const { maxBaseFeeBps, minBaseFeeBps, numberOfPeriod, totalDuration, feeSchedulerMode, useDynamicFee, dynamicFeeConfig, } = poolFees;
874
+ let dynamicFee = null;
875
+ if (useDynamicFee) {
876
+ if (dynamicFeeConfig) {
877
+ dynamicFee = {
878
+ binStep: cp_amm_sdk_1.BIN_STEP_BPS_DEFAULT,
879
+ binStepU128: cp_amm_sdk_1.BIN_STEP_BPS_U128_DEFAULT,
880
+ filterPeriod: dynamicFeeConfig.filterPeriod,
881
+ decayPeriod: dynamicFeeConfig.decayPeriod,
882
+ reductionFactor: dynamicFeeConfig.reductionFactor,
883
+ variableFeeControl: dynamicFeeConfig.variableFeeControl,
884
+ maxVolatilityAccumulator: dynamicFeeConfig.maxVolatilityAccumulator,
885
+ };
886
+ }
887
+ else {
888
+ dynamicFee = (0, cp_amm_sdk_1.getDynamicFeeParams)(minBaseFeeBps);
889
+ }
890
+ }
891
+ const baseFee = (0, cp_amm_sdk_1.getBaseFeeParams)({
892
+ baseFeeMode: feeSchedulerMode === 0
893
+ ? cp_amm_sdk_1.BaseFeeMode.FeeTimeSchedulerLinear
894
+ : cp_amm_sdk_1.BaseFeeMode.FeeTimeSchedulerExponential,
895
+ feeTimeSchedulerParam: {
896
+ startingFeeBps: maxBaseFeeBps,
897
+ endingFeeBps: minBaseFeeBps,
898
+ numberOfPeriod,
899
+ totalDuration,
900
+ },
901
+ }, quoteDecimalCount, cp_amm_sdk_1.ActivationType.Timestamp);
902
+ const poolFeesParams = {
903
+ baseFee,
904
+ padding: [],
905
+ dynamicFee,
906
+ };
907
+ const positionNft = web3_js_1.Keypair.generate();
908
+ const { tx: initCustomPoolTx, pool, position, } = await cpAmm.createCustomPool({
909
+ payer: wallet.publicKey,
910
+ creator: wallet.publicKey,
911
+ positionNft: positionNft.publicKey,
912
+ tokenAMint: baseMintPk,
913
+ tokenBMint: quoteMintPk,
914
+ tokenAAmount,
915
+ tokenBAmount,
916
+ sqrtMinPrice: minSqrtPrice,
917
+ sqrtMaxPrice: maxSqrtPrice,
918
+ liquidityDelta,
919
+ initSqrtPrice,
920
+ poolFees: poolFeesParams,
921
+ hasAlphaVault,
922
+ activationType,
923
+ collectFeeMode,
924
+ activationPoint: activationPoint != null ? new bn_js_1.default(activationPoint) : null,
925
+ tokenAProgram: baseTokenProgram,
926
+ tokenBProgram: quoteTokenProgram,
927
+ });
928
+ const computeLimit = opts?.computeUnitLimit ?? 400_000;
929
+ const priorityFee = opts?.priorityFeeMicroLamports ?? types_1.DEFAULT_PRIORITY_FEE_MICRO_LAMPORTS;
930
+ const ixs = [
931
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: computeLimit }),
932
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }),
933
+ ...initCustomPoolTx.instructions,
934
+ ];
935
+ console.log(` pool: ${pool.toBase58()}`);
936
+ console.log(` position: ${position.toBase58()}`);
937
+ console.log(` price: ${(0, cp_amm_sdk_1.getPriceFromSqrtPrice)(initSqrtPrice, baseDecimals, quoteDecimalCount)}`);
938
+ const result = await (0, send_rpc_1.sendAndConfirmVtx)(connection, ixs, wallet, {
939
+ addressLookupTables: opts?.addressLookupTables,
940
+ extraSigners: [positionNft],
941
+ });
942
+ return {
943
+ txSignature: result.txSignature,
944
+ confirmed: result.confirmed,
945
+ poolAddress: pool.toBase58(),
946
+ positionAddress: position.toBase58(),
947
+ dex: this.name,
948
+ };
949
+ }
950
+ // ----- Pool creation: createConfigPool -----
951
+ /**
952
+ * Create a DAMM v2 pool using a pre-existing on-chain config.
953
+ *
954
+ * Uses `cpAmm.createPool()` — simpler, less customizable. The config
955
+ * determines the fee schedule and price range boundaries.
956
+ *
957
+ * Ported from: 100x-algo-bots/trading-modules/meteora-damm-v2/create.ts
958
+ * createDammV2Pool()
959
+ */
960
+ async createConfigPool(params) {
961
+ const { baseMint, quoteMint: quoteMintParam, baseAmount, quoteAmount, configAddress, activationPoint = null, lockLiquidity = false, opts, } = params;
962
+ const connection = (0, config_1.getConnection)();
963
+ const wallet = (0, config_1.getWallet)();
964
+ const quoteMintStr = quoteMintParam ?? types_1.WSOL_MINT;
965
+ const baseMintPk = new web3_js_1.PublicKey(baseMint);
966
+ const quoteMintPk = new web3_js_1.PublicKey(quoteMintStr);
967
+ const configPk = new web3_js_1.PublicKey(configAddress);
968
+ // Fetch mint info
969
+ const [baseMintAccInfo, quoteMintAccInfo] = await Promise.all([
970
+ connection.getAccountInfo(baseMintPk),
971
+ connection.getAccountInfo(quoteMintPk),
972
+ ]);
973
+ if (!baseMintAccInfo)
974
+ throw new Error(`Base mint not found: ${baseMint}`);
975
+ if (!quoteMintAccInfo)
976
+ throw new Error(`Quote mint not found: ${quoteMintStr}`);
977
+ const baseMintData = (0, spl_token_1.unpackMint)(baseMintPk, baseMintAccInfo, baseMintAccInfo.owner);
978
+ const quoteMintData = (0, spl_token_1.unpackMint)(quoteMintPk, quoteMintAccInfo, quoteMintAccInfo.owner);
979
+ const baseDecimals = baseMintData.decimals;
980
+ const quoteDecimalCount = quoteDecimals(quoteMintStr);
981
+ const baseTokenProgram = baseMintAccInfo.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)
982
+ ? spl_token_1.TOKEN_2022_PROGRAM_ID
983
+ : spl_token_1.TOKEN_PROGRAM_ID;
984
+ const quoteTokenProgram = quoteMintAccInfo.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)
985
+ ? spl_token_1.TOKEN_2022_PROGRAM_ID
986
+ : spl_token_1.TOKEN_PROGRAM_ID;
987
+ // Token-2022 transfer fee detection
988
+ let baseTokenInfo;
989
+ let quoteTokenInfo;
990
+ if (baseMintAccInfo.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)) {
991
+ const epochInfo = await connection.getEpochInfo();
992
+ baseTokenInfo = { mint: baseMintData, currentEpoch: epochInfo.epoch };
993
+ }
994
+ if (quoteMintAccInfo.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)) {
995
+ const epochInfo = await connection.getEpochInfo();
996
+ quoteTokenInfo = { mint: quoteMintData, currentEpoch: epochInfo.epoch };
997
+ }
998
+ let tokenAAmount = new bn_js_1.default(new decimal_js_1.default(baseAmount).mul(decimal_js_1.default.pow(10, baseDecimals)).floor().toFixed());
999
+ let tokenBAmount = new bn_js_1.default(new decimal_js_1.default(quoteAmount).mul(decimal_js_1.default.pow(10, quoteDecimalCount)).floor().toFixed());
1000
+ if (baseTokenInfo) {
1001
+ tokenAAmount = tokenAAmount.sub((0, cp_amm_sdk_1.calculateTransferFeeIncludedAmount)(tokenAAmount, baseTokenInfo.mint, baseTokenInfo.currentEpoch).transferFee);
1002
+ }
1003
+ if (quoteTokenInfo) {
1004
+ tokenBAmount = tokenBAmount.sub((0, cp_amm_sdk_1.calculateTransferFeeIncludedAmount)(tokenBAmount, quoteTokenInfo.mint, quoteTokenInfo.currentEpoch).transferFee);
1005
+ }
1006
+ const cpAmm = new cp_amm_sdk_1.CpAmm(connection);
1007
+ // Fetch config state to get its price range
1008
+ const configState = await cpAmm.fetchConfigState(configPk);
1009
+ // Calculate initial sqrt price
1010
+ const initPrice = params.initPrice ?? quoteAmount / baseAmount;
1011
+ const initSqrtPrice = (0, cp_amm_sdk_1.getSqrtPriceFromPrice)(initPrice.toString(), baseDecimals, quoteDecimalCount);
1012
+ // Use config's price range for liquidity delta
1013
+ const liquidityDelta = cpAmm.getLiquidityDelta({
1014
+ maxAmountTokenA: tokenAAmount,
1015
+ maxAmountTokenB: tokenBAmount,
1016
+ sqrtPrice: initSqrtPrice,
1017
+ sqrtMinPrice: configState.sqrtMinPrice,
1018
+ sqrtMaxPrice: configState.sqrtMaxPrice,
1019
+ tokenAInfo: baseTokenInfo,
1020
+ });
1021
+ const positionNft = web3_js_1.Keypair.generate();
1022
+ const initPoolTx = await cpAmm.createPool({
1023
+ payer: wallet.publicKey,
1024
+ creator: wallet.publicKey,
1025
+ config: configPk,
1026
+ positionNft: positionNft.publicKey,
1027
+ tokenAMint: baseMintPk,
1028
+ tokenBMint: quoteMintPk,
1029
+ tokenAAmount,
1030
+ tokenBAmount,
1031
+ liquidityDelta,
1032
+ initSqrtPrice,
1033
+ activationPoint: activationPoint != null ? new bn_js_1.default(activationPoint) : null,
1034
+ tokenAProgram: baseTokenProgram,
1035
+ tokenBProgram: quoteTokenProgram,
1036
+ isLockLiquidity: lockLiquidity,
1037
+ });
1038
+ // Derive addresses for logging
1039
+ const pool = derivePoolAddress(configPk, baseMintPk, quoteMintPk);
1040
+ const position = derivePositionAddress(positionNft.publicKey);
1041
+ const computeLimit = opts?.computeUnitLimit ?? 400_000;
1042
+ const priorityFee = opts?.priorityFeeMicroLamports ?? types_1.DEFAULT_PRIORITY_FEE_MICRO_LAMPORTS;
1043
+ const ixs = [
1044
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: computeLimit }),
1045
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }),
1046
+ ...initPoolTx.instructions,
1047
+ ];
1048
+ console.log(` pool: ${pool.toBase58()}`);
1049
+ console.log(` position: ${position.toBase58()}`);
1050
+ console.log(` config: ${configAddress}`);
1051
+ console.log(` price: ${(0, cp_amm_sdk_1.getPriceFromSqrtPrice)(initSqrtPrice, baseDecimals, quoteDecimalCount)}`);
1052
+ const result = await (0, send_rpc_1.sendAndConfirmVtx)(connection, ixs, wallet, {
1053
+ addressLookupTables: opts?.addressLookupTables,
1054
+ extraSigners: [positionNft],
1055
+ });
1056
+ return {
1057
+ txSignature: result.txSignature,
1058
+ confirmed: result.confirmed,
1059
+ poolAddress: pool.toBase58(),
1060
+ positionAddress: position.toBase58(),
1061
+ dex: this.name,
1062
+ };
1063
+ }
1064
+ // ----- LP: listPositions -----
1065
+ /**
1066
+ * List all DAMM v2 positions owned by the user on a specific pool.
1067
+ *
1068
+ * Uses `getPositionsByUser()` to discover positions on-chain, then
1069
+ * calculates unclaimed fees for each position.
1070
+ *
1071
+ * Ported from: 100x-algo-bots/trading-modules/meteora-damm-v2/pool.ts
1072
+ * fetchPositionsByUser()
1073
+ */
1074
+ async listPositions(poolAddress) {
1075
+ const connection = (0, config_1.getConnection)();
1076
+ const wallet = (0, config_1.getWallet)();
1077
+ const cpAmm = new cp_amm_sdk_1.CpAmm(connection);
1078
+ const userPositions = await cpAmm.getPositionsByUser(wallet.publicKey);
1079
+ // Filter to positions belonging to this pool
1080
+ const poolPositions = userPositions.filter((p) => p.positionState.pool.toBase58() === poolAddress);
1081
+ if (poolPositions.length === 0) {
1082
+ return [];
1083
+ }
1084
+ const poolPk = new web3_js_1.PublicKey(poolAddress);
1085
+ const poolState = await cpAmm.fetchPoolState(poolPk);
1086
+ // Fetch decimals for both tokens
1087
+ const tokenAMint = poolState.tokenAMint;
1088
+ const tokenBMint = poolState.tokenBMint;
1089
+ const tokenAProgram = (0, cp_amm_sdk_1.getTokenProgram)(poolState.tokenAFlag);
1090
+ const tokenBProgram = (0, cp_amm_sdk_1.getTokenProgram)(poolState.tokenBFlag);
1091
+ const [tokenAMintInfo, tokenBMintInfo] = await Promise.all([
1092
+ connection.getAccountInfo(tokenAMint),
1093
+ connection.getAccountInfo(tokenBMint),
1094
+ ]);
1095
+ const mintA = (0, spl_token_1.unpackMint)(tokenAMint, tokenAMintInfo, tokenAMintInfo.owner);
1096
+ const mintB = (0, spl_token_1.unpackMint)(tokenBMint, tokenBMintInfo, tokenBMintInfo.owner);
1097
+ const decimalsA = mintA.decimals;
1098
+ const decimalsB = mintB.decimals;
1099
+ const results = [];
1100
+ for (const pos of poolPositions) {
1101
+ const positionState = await cpAmm.fetchPositionState(pos.position);
1102
+ // Calculate unclaimed fees (same math as claimFees)
1103
+ const totalPositionLiquidity = positionState.unlockedLiquidity
1104
+ .add(positionState.vestedLiquidity)
1105
+ .add(positionState.permanentLockedLiquidity);
1106
+ const feeAPerTokenStored = new bn_js_1.default(Buffer.from(poolState.feeAPerLiquidity).reverse()).sub(new bn_js_1.default(Buffer.from(positionState.feeAPerTokenCheckpoint).reverse()));
1107
+ const feeBPerTokenStored = new bn_js_1.default(Buffer.from(poolState.feeBPerLiquidity).reverse()).sub(new bn_js_1.default(Buffer.from(positionState.feeBPerTokenCheckpoint).reverse()));
1108
+ const feeA = positionState.feeAPending.add(totalPositionLiquidity.mul(feeAPerTokenStored).shrn(cp_amm_sdk_1.LIQUIDITY_SCALE));
1109
+ const feeB = positionState.feeBPending.add(totalPositionLiquidity.mul(feeBPerTokenStored).shrn(cp_amm_sdk_1.LIQUIDITY_SCALE));
1110
+ const feeAHuman = Number(feeA.toString()) / Math.pow(10, decimalsA);
1111
+ const feeBHuman = Number(feeB.toString()) / Math.pow(10, decimalsB);
1112
+ // DAMM v2 uses full-range positions — lowerBinId/upperBinId not applicable
1113
+ // but we report liquidity amounts via the vault balances proportionally
1114
+ const hasLiquidity = !totalPositionLiquidity.isZero();
1115
+ results.push({
1116
+ positionAddress: pos.position.toBase58(),
1117
+ poolAddress,
1118
+ dex: this.name,
1119
+ lowerBinId: 0, // full-range — not applicable
1120
+ upperBinId: 0, // full-range — not applicable
1121
+ amountX: 0, // per-position token amounts not directly available from state
1122
+ amountY: 0,
1123
+ tokenXMint: tokenAMint.toBase58(),
1124
+ tokenYMint: tokenBMint.toBase58(),
1125
+ feeX: feeAHuman,
1126
+ feeY: feeBHuman,
1127
+ inRange: hasLiquidity,
1128
+ });
1129
+ }
1130
+ return results;
1131
+ }
1132
+ // ----- Internal helpers -----
1133
+ async resolvePool(baseMint, quoteMint) {
1134
+ const pool = await this.findPool(baseMint, quoteMint);
1135
+ if (!pool) {
1136
+ throw new types_1.PoolNotFoundError(this.name, baseMint, quoteMint);
1137
+ }
1138
+ return new web3_js_1.PublicKey(pool.address);
1139
+ }
1140
+ }
1141
+ exports.MeteoraDammV2Adapter = MeteoraDammV2Adapter;
1142
+ // ---------------------------------------------------------------------------
1143
+ // Auto-register
1144
+ // ---------------------------------------------------------------------------
1145
+ (0, index_1.registerAdapter)(new MeteoraDammV2Adapter());
1146
+ //# sourceMappingURL=meteora-damm-v2.js.map