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.
- package/LICENSE +21 -0
- package/README.md +568 -0
- package/dist/cli.d.ts +44 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1251 -0
- package/dist/cli.js.map +1 -0
- package/dist/dex/byreal-clmm.d.ts +16 -0
- package/dist/dex/byreal-clmm.d.ts.map +1 -0
- package/dist/dex/byreal-clmm.js +39 -0
- package/dist/dex/byreal-clmm.js.map +1 -0
- package/dist/dex/dflow.d.ts +27 -0
- package/dist/dex/dflow.d.ts.map +1 -0
- package/dist/dex/dflow.js +200 -0
- package/dist/dex/dflow.js.map +1 -0
- package/dist/dex/fusion-amm.d.ts +44 -0
- package/dist/dex/fusion-amm.d.ts.map +1 -0
- package/dist/dex/fusion-amm.js +546 -0
- package/dist/dex/fusion-amm.js.map +1 -0
- package/dist/dex/futarchy-amm.d.ts +32 -0
- package/dist/dex/futarchy-amm.d.ts.map +1 -0
- package/dist/dex/futarchy-amm.js +443 -0
- package/dist/dex/futarchy-amm.js.map +1 -0
- package/dist/dex/futarchy-idl.d.ts +2568 -0
- package/dist/dex/futarchy-idl.d.ts.map +1 -0
- package/dist/dex/futarchy-idl.js +2570 -0
- package/dist/dex/futarchy-idl.js.map +1 -0
- package/dist/dex/futarchy-launchpad.d.ts +68 -0
- package/dist/dex/futarchy-launchpad.d.ts.map +1 -0
- package/dist/dex/futarchy-launchpad.js +377 -0
- package/dist/dex/futarchy-launchpad.js.map +1 -0
- package/dist/dex/index.d.ts +88 -0
- package/dist/dex/index.d.ts.map +1 -0
- package/dist/dex/index.js +159 -0
- package/dist/dex/index.js.map +1 -0
- package/dist/dex/jupiter-ultra.d.ts +27 -0
- package/dist/dex/jupiter-ultra.d.ts.map +1 -0
- package/dist/dex/jupiter-ultra.js +369 -0
- package/dist/dex/jupiter-ultra.js.map +1 -0
- package/dist/dex/meteora-damm-v1.d.ts +36 -0
- package/dist/dex/meteora-damm-v1.d.ts.map +1 -0
- package/dist/dex/meteora-damm-v1.js +314 -0
- package/dist/dex/meteora-damm-v1.js.map +1 -0
- package/dist/dex/meteora-damm-v2.d.ts +103 -0
- package/dist/dex/meteora-damm-v2.d.ts.map +1 -0
- package/dist/dex/meteora-damm-v2.js +1146 -0
- package/dist/dex/meteora-damm-v2.js.map +1 -0
- package/dist/dex/meteora-dbc.d.ts +38 -0
- package/dist/dex/meteora-dbc.d.ts.map +1 -0
- package/dist/dex/meteora-dbc.js +374 -0
- package/dist/dex/meteora-dbc.js.map +1 -0
- package/dist/dex/meteora-dlmm.d.ts +79 -0
- package/dist/dex/meteora-dlmm.d.ts.map +1 -0
- package/dist/dex/meteora-dlmm.js +735 -0
- package/dist/dex/meteora-dlmm.js.map +1 -0
- package/dist/dex/orca.d.ts +31 -0
- package/dist/dex/orca.d.ts.map +1 -0
- package/dist/dex/orca.js +536 -0
- package/dist/dex/orca.js.map +1 -0
- package/dist/dex/pancakeswap-clmm.d.ts +16 -0
- package/dist/dex/pancakeswap-clmm.d.ts.map +1 -0
- package/dist/dex/pancakeswap-clmm.js +39 -0
- package/dist/dex/pancakeswap-clmm.js.map +1 -0
- package/dist/dex/pumpfun-amm.d.ts +46 -0
- package/dist/dex/pumpfun-amm.d.ts.map +1 -0
- package/dist/dex/pumpfun-amm.js +692 -0
- package/dist/dex/pumpfun-amm.js.map +1 -0
- package/dist/dex/pumpfun.d.ts +41 -0
- package/dist/dex/pumpfun.d.ts.map +1 -0
- package/dist/dex/pumpfun.js +555 -0
- package/dist/dex/pumpfun.js.map +1 -0
- package/dist/dex/raydium-amm-v4.d.ts +11 -0
- package/dist/dex/raydium-amm-v4.d.ts.map +1 -0
- package/dist/dex/raydium-amm-v4.js +649 -0
- package/dist/dex/raydium-amm-v4.js.map +1 -0
- package/dist/dex/raydium-clmm.d.ts +12 -0
- package/dist/dex/raydium-clmm.d.ts.map +1 -0
- package/dist/dex/raydium-clmm.js +675 -0
- package/dist/dex/raydium-clmm.js.map +1 -0
- package/dist/dex/raydium-cpmm.d.ts +10 -0
- package/dist/dex/raydium-cpmm.d.ts.map +1 -0
- package/dist/dex/raydium-cpmm.js +613 -0
- package/dist/dex/raydium-cpmm.js.map +1 -0
- package/dist/dex/raydium-launchlab.d.ts +12 -0
- package/dist/dex/raydium-launchlab.d.ts.map +1 -0
- package/dist/dex/raydium-launchlab.js +530 -0
- package/dist/dex/raydium-launchlab.js.map +1 -0
- package/dist/dex/shared/clmm-base.d.ts +58 -0
- package/dist/dex/shared/clmm-base.d.ts.map +1 -0
- package/dist/dex/shared/clmm-base.js +891 -0
- package/dist/dex/shared/clmm-base.js.map +1 -0
- package/dist/dex/types.d.ts +601 -0
- package/dist/dex/types.d.ts.map +1 -0
- package/dist/dex/types.js +137 -0
- package/dist/dex/types.js.map +1 -0
- package/dist/dexscreener/index.d.ts +2 -0
- package/dist/dexscreener/index.d.ts.map +1 -0
- package/dist/dexscreener/index.js +18 -0
- package/dist/dexscreener/index.js.map +1 -0
- package/dist/dexscreener/info.d.ts +22 -0
- package/dist/dexscreener/info.d.ts.map +1 -0
- package/dist/dexscreener/info.js +104 -0
- package/dist/dexscreener/info.js.map +1 -0
- package/dist/helpers/check_balance.d.ts +10 -0
- package/dist/helpers/check_balance.d.ts.map +1 -0
- package/dist/helpers/check_balance.js +34 -0
- package/dist/helpers/check_balance.js.map +1 -0
- package/dist/helpers/config.d.ts +51 -0
- package/dist/helpers/config.d.ts.map +1 -0
- package/dist/helpers/config.js +118 -0
- package/dist/helpers/config.js.map +1 -0
- package/dist/helpers/index.d.ts +8 -0
- package/dist/helpers/index.d.ts.map +1 -0
- package/dist/helpers/index.js +29 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/helpers/logger.d.ts +27 -0
- package/dist/helpers/logger.d.ts.map +1 -0
- package/dist/helpers/logger.js +39 -0
- package/dist/helpers/logger.js.map +1 -0
- package/dist/helpers/token-2022.d.ts +32 -0
- package/dist/helpers/token-2022.d.ts.map +1 -0
- package/dist/helpers/token-2022.js +48 -0
- package/dist/helpers/token-2022.js.map +1 -0
- package/dist/helpers/unwrap_sol.d.ts +2 -0
- package/dist/helpers/unwrap_sol.d.ts.map +1 -0
- package/dist/helpers/unwrap_sol.js +67 -0
- package/dist/helpers/unwrap_sol.js.map +1 -0
- package/dist/helpers/util.d.ts +698 -0
- package/dist/helpers/util.d.ts.map +1 -0
- package/dist/helpers/util.js +181 -0
- package/dist/helpers/util.js.map +1 -0
- package/dist/helpers/utils.d.ts +10 -0
- package/dist/helpers/utils.d.ts.map +1 -0
- package/dist/helpers/utils.js +97 -0
- package/dist/helpers/utils.js.map +1 -0
- package/dist/helpers/wrap_sol.d.ts +3 -0
- package/dist/helpers/wrap_sol.d.ts.map +1 -0
- package/dist/helpers/wrap_sol.js +88 -0
- package/dist/helpers/wrap_sol.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/transactions/bloXroute_tips_tx_executor.d.ts +4 -0
- package/dist/transactions/bloXroute_tips_tx_executor.d.ts.map +1 -0
- package/dist/transactions/bloXroute_tips_tx_executor.js +70 -0
- package/dist/transactions/bloXroute_tips_tx_executor.js.map +1 -0
- package/dist/transactions/index.d.ts +6 -0
- package/dist/transactions/index.d.ts.map +1 -0
- package/dist/transactions/index.js +30 -0
- package/dist/transactions/index.js.map +1 -0
- package/dist/transactions/jito_tips_tx_executor.d.ts +15 -0
- package/dist/transactions/jito_tips_tx_executor.d.ts.map +1 -0
- package/dist/transactions/jito_tips_tx_executor.js +99 -0
- package/dist/transactions/jito_tips_tx_executor.js.map +1 -0
- package/dist/transactions/landing/index.d.ts +30 -0
- package/dist/transactions/landing/index.d.ts.map +1 -0
- package/dist/transactions/landing/index.js +60 -0
- package/dist/transactions/landing/index.js.map +1 -0
- package/dist/transactions/landing/nonce-manager.d.ts +116 -0
- package/dist/transactions/landing/nonce-manager.d.ts.map +1 -0
- package/dist/transactions/landing/nonce-manager.js +393 -0
- package/dist/transactions/landing/nonce-manager.js.map +1 -0
- package/dist/transactions/landing/orchestrator.d.ts +104 -0
- package/dist/transactions/landing/orchestrator.d.ts.map +1 -0
- package/dist/transactions/landing/orchestrator.js +329 -0
- package/dist/transactions/landing/orchestrator.js.map +1 -0
- package/dist/transactions/landing/providers/astralane.d.ts +12 -0
- package/dist/transactions/landing/providers/astralane.d.ts.map +1 -0
- package/dist/transactions/landing/providers/astralane.js +132 -0
- package/dist/transactions/landing/providers/astralane.js.map +1 -0
- package/dist/transactions/landing/providers/blockrazor.d.ts +11 -0
- package/dist/transactions/landing/providers/blockrazor.d.ts.map +1 -0
- package/dist/transactions/landing/providers/blockrazor.js +134 -0
- package/dist/transactions/landing/providers/blockrazor.js.map +1 -0
- package/dist/transactions/landing/providers/bloxroute.d.ts +12 -0
- package/dist/transactions/landing/providers/bloxroute.d.ts.map +1 -0
- package/dist/transactions/landing/providers/bloxroute.js +102 -0
- package/dist/transactions/landing/providers/bloxroute.js.map +1 -0
- package/dist/transactions/landing/providers/flashblock.d.ts +10 -0
- package/dist/transactions/landing/providers/flashblock.d.ts.map +1 -0
- package/dist/transactions/landing/providers/flashblock.js +102 -0
- package/dist/transactions/landing/providers/flashblock.js.map +1 -0
- package/dist/transactions/landing/providers/helius-sender.d.ts +11 -0
- package/dist/transactions/landing/providers/helius-sender.d.ts.map +1 -0
- package/dist/transactions/landing/providers/helius-sender.js +101 -0
- package/dist/transactions/landing/providers/helius-sender.js.map +1 -0
- package/dist/transactions/landing/providers/jito.d.ts +16 -0
- package/dist/transactions/landing/providers/jito.d.ts.map +1 -0
- package/dist/transactions/landing/providers/jito.js +110 -0
- package/dist/transactions/landing/providers/jito.js.map +1 -0
- package/dist/transactions/landing/providers/nextblock.d.ts +11 -0
- package/dist/transactions/landing/providers/nextblock.d.ts.map +1 -0
- package/dist/transactions/landing/providers/nextblock.js +109 -0
- package/dist/transactions/landing/providers/nextblock.js.map +1 -0
- package/dist/transactions/landing/providers/node1.d.ts +11 -0
- package/dist/transactions/landing/providers/node1.d.ts.map +1 -0
- package/dist/transactions/landing/providers/node1.js +101 -0
- package/dist/transactions/landing/providers/node1.js.map +1 -0
- package/dist/transactions/landing/providers/nozomi.d.ts +11 -0
- package/dist/transactions/landing/providers/nozomi.d.ts.map +1 -0
- package/dist/transactions/landing/providers/nozomi.js +124 -0
- package/dist/transactions/landing/providers/nozomi.js.map +1 -0
- package/dist/transactions/landing/providers/soyas.d.ts +16 -0
- package/dist/transactions/landing/providers/soyas.d.ts.map +1 -0
- package/dist/transactions/landing/providers/soyas.js +192 -0
- package/dist/transactions/landing/providers/soyas.js.map +1 -0
- package/dist/transactions/landing/providers/stellium.d.ts +11 -0
- package/dist/transactions/landing/providers/stellium.d.ts.map +1 -0
- package/dist/transactions/landing/providers/stellium.js +102 -0
- package/dist/transactions/landing/providers/stellium.js.map +1 -0
- package/dist/transactions/landing/providers/zero-slot.d.ts +10 -0
- package/dist/transactions/landing/providers/zero-slot.d.ts.map +1 -0
- package/dist/transactions/landing/providers/zero-slot.js +92 -0
- package/dist/transactions/landing/providers/zero-slot.js.map +1 -0
- package/dist/transactions/landing/tip-accounts.d.ts +22 -0
- package/dist/transactions/landing/tip-accounts.d.ts.map +1 -0
- package/dist/transactions/landing/tip-accounts.js +140 -0
- package/dist/transactions/landing/tip-accounts.js.map +1 -0
- package/dist/transactions/landing/types.d.ts +98 -0
- package/dist/transactions/landing/types.d.ts.map +1 -0
- package/dist/transactions/landing/types.js +30 -0
- package/dist/transactions/landing/types.js.map +1 -0
- package/dist/transactions/nozomi/tx-submission.d.ts +14 -0
- package/dist/transactions/nozomi/tx-submission.d.ts.map +1 -0
- package/dist/transactions/nozomi/tx-submission.js +107 -0
- package/dist/transactions/nozomi/tx-submission.js.map +1 -0
- package/dist/transactions/send-rpc.d.ts +54 -0
- package/dist/transactions/send-rpc.d.ts.map +1 -0
- package/dist/transactions/send-rpc.js +126 -0
- package/dist/transactions/send-rpc.js.map +1 -0
- package/dist/transactions/simple_tx_executor.d.ts +10 -0
- package/dist/transactions/simple_tx_executor.d.ts.map +1 -0
- package/dist/transactions/simple_tx_executor.js +33 -0
- package/dist/transactions/simple_tx_executor.js.map +1 -0
- 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
|