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,735 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Meteora DLMM (Dynamic Liquidity Market Maker) — IDexAdapter Implementation
|
|
4
|
+
*
|
|
5
|
+
* Wraps the @meteora-ag/dlmm SDK for buy/sell/snipe/findPool/getPrice
|
|
6
|
+
* plus LP operations: addLiquidity, removeLiquidity, claimFees, listPositions.
|
|
7
|
+
*
|
|
8
|
+
* Source: 100x-algo-bots/trading-modules/meteora-dlmm/
|
|
9
|
+
*
|
|
10
|
+
* IMPORTANT BUG FIX: Source code hardcodes `swapYtoX = true` which only works
|
|
11
|
+
* when the user is buying (spending Y/quote to get X/base). This adapter
|
|
12
|
+
* dynamically computes the swap direction based on which token is the quote.
|
|
13
|
+
*
|
|
14
|
+
* SDK patterns:
|
|
15
|
+
* // Swaps
|
|
16
|
+
* const dlmmPool = await DLMM.create(connection, poolAddress);
|
|
17
|
+
* const binArrays = await dlmmPool.getBinArrayForSwap(swapYtoX);
|
|
18
|
+
* const quote = await dlmmPool.swapQuote(amount, swapYtoX, slippage, binArrays);
|
|
19
|
+
* const swapTx = await dlmmPool.swap({ inToken, outToken, ... });
|
|
20
|
+
*
|
|
21
|
+
* // LP operations
|
|
22
|
+
* const createTx = await dlmmPool.initializePositionAndAddLiquidityByStrategy({...});
|
|
23
|
+
* const removeTxs = await dlmmPool.removeLiquidity({...});
|
|
24
|
+
* const claimTxs = await dlmmPool.claimSwapFee({...});
|
|
25
|
+
*/
|
|
26
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
27
|
+
if (k2 === undefined) k2 = k;
|
|
28
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
29
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
30
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
31
|
+
}
|
|
32
|
+
Object.defineProperty(o, k2, desc);
|
|
33
|
+
}) : (function(o, m, k, k2) {
|
|
34
|
+
if (k2 === undefined) k2 = k;
|
|
35
|
+
o[k2] = m[k];
|
|
36
|
+
}));
|
|
37
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
38
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
39
|
+
}) : function(o, v) {
|
|
40
|
+
o["default"] = v;
|
|
41
|
+
});
|
|
42
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
43
|
+
var ownKeys = function(o) {
|
|
44
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
45
|
+
var ar = [];
|
|
46
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
47
|
+
return ar;
|
|
48
|
+
};
|
|
49
|
+
return ownKeys(o);
|
|
50
|
+
};
|
|
51
|
+
return function (mod) {
|
|
52
|
+
if (mod && mod.__esModule) return mod;
|
|
53
|
+
var result = {};
|
|
54
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
55
|
+
__setModuleDefault(result, mod);
|
|
56
|
+
return result;
|
|
57
|
+
};
|
|
58
|
+
})();
|
|
59
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
60
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
61
|
+
};
|
|
62
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
63
|
+
exports.MeteoraDlmmAdapter = void 0;
|
|
64
|
+
const bn_js_1 = __importDefault(require("bn.js"));
|
|
65
|
+
const dlmm_1 = __importStar(require("@meteora-ag/dlmm"));
|
|
66
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
67
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
68
|
+
const config_1 = require("../helpers/config");
|
|
69
|
+
const landing_1 = require("../transactions/landing");
|
|
70
|
+
const send_rpc_1 = require("../transactions/send-rpc");
|
|
71
|
+
const types_1 = require("./types");
|
|
72
|
+
const index_1 = require("./index");
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// Constants
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
/** Known stablecoin mints with 6 decimals */
|
|
77
|
+
const SIX_DECIMAL_MINTS = new Set([
|
|
78
|
+
types_1.USDC_MINT,
|
|
79
|
+
types_1.USDT_MINT,
|
|
80
|
+
"USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB",
|
|
81
|
+
]);
|
|
82
|
+
const WSOL_PK = new web3_js_1.PublicKey(types_1.WSOL_MINT);
|
|
83
|
+
/** Default number of bins for LP positions */
|
|
84
|
+
const DEFAULT_NUM_BINS = 50;
|
|
85
|
+
/** Maximum bins allowed by the DLMM SDK per position */
|
|
86
|
+
const MAX_BINS = 70;
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Helpers
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
function quoteDecimals(quoteMintStr) {
|
|
91
|
+
return SIX_DECIMAL_MINTS.has(quoteMintStr) ? 6 : 9;
|
|
92
|
+
}
|
|
93
|
+
function amountToLamports(amount, quoteMintStr) {
|
|
94
|
+
const decimals = quoteDecimals(quoteMintStr);
|
|
95
|
+
return new bn_js_1.default(Math.floor(amount * Math.pow(10, decimals)));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Determine swap direction for DLMM pool.
|
|
99
|
+
*
|
|
100
|
+
* DLMM pools have tokenX and tokenY. swapYtoX=true means spending Y to get X.
|
|
101
|
+
* For a buy (spending quote to get base):
|
|
102
|
+
* - If quote == tokenY → swapYtoX = true (spend Y, get X)
|
|
103
|
+
* - If quote == tokenX → swapYtoX = false (spend X, get Y)
|
|
104
|
+
*
|
|
105
|
+
* The source code HARDCODES swapYtoX=true which is WRONG when the quote
|
|
106
|
+
* token is tokenX. This adapter fixes that bug.
|
|
107
|
+
*/
|
|
108
|
+
function determineSwapDirection(quoteMintStr, tokenX, tokenY) {
|
|
109
|
+
const quotePk = new web3_js_1.PublicKey(quoteMintStr);
|
|
110
|
+
if (quotePk.equals(tokenY)) {
|
|
111
|
+
// Quote is tokenY → spend Y, get X
|
|
112
|
+
return { swapYtoX: true, inToken: tokenY, outToken: tokenX };
|
|
113
|
+
}
|
|
114
|
+
else if (quotePk.equals(tokenX)) {
|
|
115
|
+
// Quote is tokenX → spend X, get Y
|
|
116
|
+
return { swapYtoX: false, inToken: tokenX, outToken: tokenY };
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Fallback: assume tokenY is quote (matches most SOL-paired pools)
|
|
120
|
+
return { swapYtoX: true, inToken: tokenY, outToken: tokenX };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Map our LpStrategy string to the SDK's StrategyType enum.
|
|
125
|
+
* The SDK has: Spot=0, Curve=1, BidAsk=2.
|
|
126
|
+
* One-sided behavior is controlled via the bin range (not a separate enum).
|
|
127
|
+
*/
|
|
128
|
+
function toSdkStrategy(strategy) {
|
|
129
|
+
switch (strategy) {
|
|
130
|
+
case "spot":
|
|
131
|
+
return dlmm_1.StrategyType.Spot;
|
|
132
|
+
case "curve":
|
|
133
|
+
return dlmm_1.StrategyType.Curve;
|
|
134
|
+
case "bid-ask":
|
|
135
|
+
return dlmm_1.StrategyType.BidAsk;
|
|
136
|
+
default:
|
|
137
|
+
return dlmm_1.StrategyType.Spot;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Adapter
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
class MeteoraDlmmAdapter {
|
|
144
|
+
name = "meteora-dlmm";
|
|
145
|
+
protocol = "dlmm";
|
|
146
|
+
capabilities = (0, types_1.defaultCapabilities)({
|
|
147
|
+
canBuy: true,
|
|
148
|
+
canSell: true,
|
|
149
|
+
canSnipe: true,
|
|
150
|
+
canFindPool: false, // DLMM has no simple PDA derivation
|
|
151
|
+
canGetPrice: true,
|
|
152
|
+
canAddLiquidity: true,
|
|
153
|
+
canRemoveLiquidity: true,
|
|
154
|
+
canClaimFees: true,
|
|
155
|
+
canListPositions: true,
|
|
156
|
+
});
|
|
157
|
+
// ----- Core: buy -----
|
|
158
|
+
async buy(params) {
|
|
159
|
+
const tokenMint = (0, types_1.requireTokenMint)(params, this.name);
|
|
160
|
+
const { amountSol, quoteMint: quoteMintParam, poolAddress, opts } = params;
|
|
161
|
+
const connection = (0, config_1.getConnection)();
|
|
162
|
+
const wallet = (0, config_1.getWallet)();
|
|
163
|
+
const quoteMintStr = quoteMintParam ?? types_1.WSOL_MINT;
|
|
164
|
+
const poolPk = poolAddress
|
|
165
|
+
? new web3_js_1.PublicKey(poolAddress)
|
|
166
|
+
: await this.resolvePool(tokenMint, quoteMintStr);
|
|
167
|
+
const dlmmPool = await dlmm_1.default.create(connection, poolPk);
|
|
168
|
+
const inputAmount = amountToLamports(amountSol, quoteMintStr);
|
|
169
|
+
// Dynamically determine swap direction (fixes source bug)
|
|
170
|
+
const { swapYtoX, inToken, outToken } = determineSwapDirection(quoteMintStr, dlmmPool.tokenX.publicKey, dlmmPool.tokenY.publicKey);
|
|
171
|
+
const binArrays = await dlmmPool.getBinArrayForSwap(swapYtoX);
|
|
172
|
+
const slippageBps = opts?.slippageBps ?? types_1.DEFAULT_SLIPPAGE_BPS;
|
|
173
|
+
const swapQuote = await dlmmPool.swapQuote(inputAmount, swapYtoX, new bn_js_1.default(slippageBps), binArrays);
|
|
174
|
+
const swapTx = await dlmmPool.swap({
|
|
175
|
+
inToken,
|
|
176
|
+
binArraysPubkey: swapQuote.binArraysPubkey,
|
|
177
|
+
inAmount: inputAmount,
|
|
178
|
+
lbPair: dlmmPool.pubkey,
|
|
179
|
+
user: wallet.publicKey,
|
|
180
|
+
minOutAmount: new bn_js_1.default(0),
|
|
181
|
+
outToken,
|
|
182
|
+
});
|
|
183
|
+
// DLMM SDK already includes ComputeBudget + ATA creation in swapTx.instructions
|
|
184
|
+
const result = await (0, send_rpc_1.sendAndConfirmVtx)(connection, swapTx.instructions, wallet, {
|
|
185
|
+
addressLookupTables: opts?.addressLookupTables,
|
|
186
|
+
});
|
|
187
|
+
return {
|
|
188
|
+
txSignature: result.txSignature,
|
|
189
|
+
confirmed: result.confirmed,
|
|
190
|
+
amountIn: amountSol,
|
|
191
|
+
amountInToken: quoteMintStr,
|
|
192
|
+
dex: this.name,
|
|
193
|
+
poolAddress: poolPk.toBase58(),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// ----- Core: sell -----
|
|
197
|
+
async sell(params) {
|
|
198
|
+
const tokenMint = (0, types_1.requireTokenMint)(params, this.name);
|
|
199
|
+
const { percentage, quoteMint: quoteMintParam, poolAddress, opts } = params;
|
|
200
|
+
const connection = (0, config_1.getConnection)();
|
|
201
|
+
const wallet = (0, config_1.getWallet)();
|
|
202
|
+
const quoteMintStr = quoteMintParam ?? types_1.WSOL_MINT;
|
|
203
|
+
const poolPk = poolAddress
|
|
204
|
+
? new web3_js_1.PublicKey(poolAddress)
|
|
205
|
+
: await this.resolvePool(tokenMint, quoteMintStr);
|
|
206
|
+
const dlmmPool = await dlmm_1.default.create(connection, poolPk);
|
|
207
|
+
const baseMintPk = new web3_js_1.PublicKey(tokenMint);
|
|
208
|
+
// Detect base token program
|
|
209
|
+
const baseMintAccInfo = await connection.getAccountInfo(baseMintPk);
|
|
210
|
+
if (!baseMintAccInfo) {
|
|
211
|
+
throw new Error(`Token mint not found: ${tokenMint}`);
|
|
212
|
+
}
|
|
213
|
+
const baseTokenProgram = baseMintAccInfo.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID)
|
|
214
|
+
? spl_token_1.TOKEN_2022_PROGRAM_ID
|
|
215
|
+
: spl_token_1.TOKEN_PROGRAM_ID;
|
|
216
|
+
// Get token balance
|
|
217
|
+
const ata = await (0, spl_token_1.getAssociatedTokenAddress)(baseMintPk, wallet.publicKey, false, baseTokenProgram);
|
|
218
|
+
const tokenAccount = await (0, spl_token_1.getAccount)(connection, ata, "confirmed", baseTokenProgram);
|
|
219
|
+
const balance = tokenAccount.amount;
|
|
220
|
+
// Calculate sell amount based on percentage
|
|
221
|
+
const sellAmount = new bn_js_1.default(Math.floor((Number(balance) * percentage) / 100).toString());
|
|
222
|
+
if (sellAmount.isZero()) {
|
|
223
|
+
throw new Error(`No balance to sell for ${tokenMint}`);
|
|
224
|
+
}
|
|
225
|
+
// Determine swap direction using the base token (being sold) as input
|
|
226
|
+
const { swapYtoX, inToken, outToken } = determineSwapDirection(tokenMint, dlmmPool.tokenX.publicKey, dlmmPool.tokenY.publicKey);
|
|
227
|
+
const binArrays = await dlmmPool.getBinArrayForSwap(swapYtoX);
|
|
228
|
+
const slippageBps = opts?.slippageBps ?? types_1.DEFAULT_SLIPPAGE_BPS;
|
|
229
|
+
const swapQuote = await dlmmPool.swapQuote(sellAmount, swapYtoX, new bn_js_1.default(slippageBps), binArrays);
|
|
230
|
+
const swapTx = await dlmmPool.swap({
|
|
231
|
+
inToken,
|
|
232
|
+
binArraysPubkey: swapQuote.binArraysPubkey,
|
|
233
|
+
inAmount: sellAmount,
|
|
234
|
+
lbPair: dlmmPool.pubkey,
|
|
235
|
+
user: wallet.publicKey,
|
|
236
|
+
minOutAmount: new bn_js_1.default(0),
|
|
237
|
+
outToken,
|
|
238
|
+
});
|
|
239
|
+
// DLMM SDK already includes ComputeBudget + ATA creation in swapTx.instructions
|
|
240
|
+
const rpcResult = await (0, send_rpc_1.sendAndConfirmVtx)(connection, swapTx.instructions, wallet, {
|
|
241
|
+
addressLookupTables: opts?.addressLookupTables,
|
|
242
|
+
});
|
|
243
|
+
// Human-readable sell amount
|
|
244
|
+
let tokenDecimals = 9;
|
|
245
|
+
try {
|
|
246
|
+
const mintData = await connection.getTokenSupply(baseMintPk);
|
|
247
|
+
tokenDecimals = mintData.value.decimals;
|
|
248
|
+
}
|
|
249
|
+
catch { /* fallback to 9 */ }
|
|
250
|
+
const humanAmount = Number(sellAmount.toString()) / Math.pow(10, tokenDecimals);
|
|
251
|
+
return {
|
|
252
|
+
txSignature: rpcResult.txSignature,
|
|
253
|
+
confirmed: rpcResult.confirmed,
|
|
254
|
+
amountIn: humanAmount,
|
|
255
|
+
amountInToken: tokenMint,
|
|
256
|
+
dex: this.name,
|
|
257
|
+
poolAddress: poolPk.toBase58(),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
// ----- Snipe -----
|
|
261
|
+
async snipe(params) {
|
|
262
|
+
const { tokenMint, amountSol, poolAddress, quoteMint: quoteMintParam, tipSol, opts } = params;
|
|
263
|
+
const connection = (0, config_1.getConnection)();
|
|
264
|
+
const wallet = (0, config_1.getWallet)();
|
|
265
|
+
const quoteMintStr = quoteMintParam ?? types_1.WSOL_MINT;
|
|
266
|
+
const poolPk = new web3_js_1.PublicKey(poolAddress);
|
|
267
|
+
const inputAmount = amountToLamports(amountSol, quoteMintStr);
|
|
268
|
+
const dlmmPool = await dlmm_1.default.create(connection, poolPk);
|
|
269
|
+
// Dynamically determine swap direction (fixes source bug)
|
|
270
|
+
const { swapYtoX, inToken, outToken } = determineSwapDirection(quoteMintStr, dlmmPool.tokenX.publicKey, dlmmPool.tokenY.publicKey);
|
|
271
|
+
const binArrays = await dlmmPool.getBinArrayForSwap(swapYtoX);
|
|
272
|
+
const swapQuote = await dlmmPool.swapQuote(inputAmount, swapYtoX, new bn_js_1.default(15 * 100), // 15% slippage for snipe (matches source)
|
|
273
|
+
binArrays);
|
|
274
|
+
const swapTx = await dlmmPool.swap({
|
|
275
|
+
inToken,
|
|
276
|
+
binArraysPubkey: swapQuote.binArraysPubkey,
|
|
277
|
+
inAmount: inputAmount,
|
|
278
|
+
lbPair: dlmmPool.pubkey,
|
|
279
|
+
user: wallet.publicKey,
|
|
280
|
+
minOutAmount: new bn_js_1.default(0), // unlimited slippage for snipe
|
|
281
|
+
outToken,
|
|
282
|
+
});
|
|
283
|
+
const computeLimit = opts?.computeUnitLimit ?? types_1.DEFAULT_COMPUTE_UNIT_LIMIT;
|
|
284
|
+
const priorityFee = opts?.priorityFeeMicroLamports ?? 40_000_000;
|
|
285
|
+
const ixs = [
|
|
286
|
+
web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: computeLimit }),
|
|
287
|
+
web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }),
|
|
288
|
+
...swapTx.instructions,
|
|
289
|
+
];
|
|
290
|
+
const blockhash = await connection.getLatestBlockhash();
|
|
291
|
+
const results = await (0, landing_1.landTransaction)(ixs, wallet, blockhash, {
|
|
292
|
+
dex: this.name,
|
|
293
|
+
operation: "snipe",
|
|
294
|
+
tipSol,
|
|
295
|
+
addressLookupTables: opts?.addressLookupTables,
|
|
296
|
+
});
|
|
297
|
+
const accepted = results.find((r) => r.accepted);
|
|
298
|
+
return {
|
|
299
|
+
txSignature: accepted?.signature ?? "",
|
|
300
|
+
confirmed: !!accepted?.accepted,
|
|
301
|
+
amountIn: amountSol,
|
|
302
|
+
amountInToken: quoteMintStr,
|
|
303
|
+
dex: this.name,
|
|
304
|
+
poolAddress,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
// ----- buildSwapIxs -----
|
|
308
|
+
async buildSwapIxs(params) {
|
|
309
|
+
if ("percentage" in params) {
|
|
310
|
+
throw new types_1.UnsupportedOperationError(this.name, "buildSwapIxs(sell)");
|
|
311
|
+
}
|
|
312
|
+
const buyParams = params;
|
|
313
|
+
const tokenMint = (0, types_1.requireTokenMint)(buyParams, this.name);
|
|
314
|
+
const connection = (0, config_1.getConnection)();
|
|
315
|
+
const wallet = (0, config_1.getWallet)();
|
|
316
|
+
const quoteMintStr = buyParams.quoteMint ?? types_1.WSOL_MINT;
|
|
317
|
+
const poolPk = buyParams.poolAddress
|
|
318
|
+
? new web3_js_1.PublicKey(buyParams.poolAddress)
|
|
319
|
+
: await this.resolvePool(tokenMint, quoteMintStr);
|
|
320
|
+
const dlmmPool = await dlmm_1.default.create(connection, poolPk);
|
|
321
|
+
const inputAmount = amountToLamports(buyParams.amountSol, quoteMintStr);
|
|
322
|
+
const { swapYtoX, inToken, outToken } = determineSwapDirection(quoteMintStr, dlmmPool.tokenX.publicKey, dlmmPool.tokenY.publicKey);
|
|
323
|
+
const binArrays = await dlmmPool.getBinArrayForSwap(swapYtoX);
|
|
324
|
+
const swapQuote = await dlmmPool.swapQuote(inputAmount, swapYtoX, new bn_js_1.default(15 * 100), binArrays);
|
|
325
|
+
const swapTx = await dlmmPool.swap({
|
|
326
|
+
inToken,
|
|
327
|
+
binArraysPubkey: swapQuote.binArraysPubkey,
|
|
328
|
+
inAmount: inputAmount,
|
|
329
|
+
lbPair: dlmmPool.pubkey,
|
|
330
|
+
user: wallet.publicKey,
|
|
331
|
+
minOutAmount: new bn_js_1.default(0),
|
|
332
|
+
outToken,
|
|
333
|
+
});
|
|
334
|
+
return {
|
|
335
|
+
instructions: swapTx.instructions,
|
|
336
|
+
signers: [],
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
// ----- findPool -----
|
|
340
|
+
async findPool(baseMint, quoteMint) {
|
|
341
|
+
const connection = (0, config_1.getConnection)();
|
|
342
|
+
const poolPk = new web3_js_1.PublicKey(baseMint); // For DLMM, baseMint is often used to search
|
|
343
|
+
const quoteMintStr = quoteMint ?? types_1.WSOL_MINT;
|
|
344
|
+
// DLMM doesn't have a simple PDA derivation like DAMM.
|
|
345
|
+
// The SDK's DLMM.create() requires a known pool address.
|
|
346
|
+
// For pool discovery, we'd need to use the DLMM program's getLbPairs() which
|
|
347
|
+
// requires anchor setup. Return null — callers should provide poolAddress.
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
// ----- getPrice -----
|
|
351
|
+
async getPrice(poolAddress) {
|
|
352
|
+
const connection = (0, config_1.getConnection)();
|
|
353
|
+
const poolPk = new web3_js_1.PublicKey(poolAddress);
|
|
354
|
+
const dlmmPool = await dlmm_1.default.create(connection, poolPk);
|
|
355
|
+
const { binId, price: rawPrice } = await dlmmPool.getActiveBin();
|
|
356
|
+
const tokenXDecimals = Number(dlmmPool.tokenX.mint.decimals);
|
|
357
|
+
const tokenYDecimals = Number(dlmmPool.tokenY.mint.decimals);
|
|
358
|
+
const priceFactor = Math.pow(10, tokenYDecimals - tokenXDecimals);
|
|
359
|
+
const adjustedPrice = Number(rawPrice) / priceFactor;
|
|
360
|
+
const price = adjustedPrice;
|
|
361
|
+
return {
|
|
362
|
+
price,
|
|
363
|
+
baseMint: dlmmPool.tokenX.publicKey.toBase58(),
|
|
364
|
+
quoteMint: dlmmPool.tokenY.publicKey.toBase58(),
|
|
365
|
+
source: "on-chain",
|
|
366
|
+
poolAddress,
|
|
367
|
+
timestamp: Date.now(),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
// ----- addLiquidity -----
|
|
371
|
+
/**
|
|
372
|
+
* Add liquidity to a Meteora DLMM pool.
|
|
373
|
+
*
|
|
374
|
+
* Three modes (determined by which amounts are provided):
|
|
375
|
+
*
|
|
376
|
+
* 1. One-sided SOL (amountSol > 0, no amountToken):
|
|
377
|
+
* Bins below active bin, filled with SOL. Most common use case.
|
|
378
|
+
*
|
|
379
|
+
* 2. One-sided token (amountToken > 0, no amountSol):
|
|
380
|
+
* Bins above active bin, filled with the non-SOL token.
|
|
381
|
+
* Useful for DCA-out strategies.
|
|
382
|
+
*
|
|
383
|
+
* 3. Balanced (both amountSol and amountToken > 0):
|
|
384
|
+
* Bins centered around active bin with both tokens.
|
|
385
|
+
*
|
|
386
|
+
* @returns TxResult with positionAddress for the created position.
|
|
387
|
+
*/
|
|
388
|
+
async addLiquidity(params) {
|
|
389
|
+
const { poolAddress, opts } = params;
|
|
390
|
+
const strategy = params.strategy ?? "spot";
|
|
391
|
+
const numBins = Math.min(Math.max(params.bins ?? DEFAULT_NUM_BINS, 1), MAX_BINS);
|
|
392
|
+
// Resolve amounts: prefer new fields, fall back to legacy amountA/amountB
|
|
393
|
+
const amountSol = params.amountSol ?? params.amountA ?? 0;
|
|
394
|
+
const amountToken = params.amountToken ?? params.amountB ?? 0;
|
|
395
|
+
const connection = (0, config_1.getConnection)();
|
|
396
|
+
const wallet = (0, config_1.getWallet)();
|
|
397
|
+
const poolPk = new web3_js_1.PublicKey(poolAddress);
|
|
398
|
+
const dlmmPool = await dlmm_1.default.create(connection, poolPk);
|
|
399
|
+
const activeBin = await dlmmPool.getActiveBin();
|
|
400
|
+
// Determine which token is SOL
|
|
401
|
+
const tokenXMint = dlmmPool.tokenX.publicKey;
|
|
402
|
+
const tokenYMint = dlmmPool.tokenY.publicKey;
|
|
403
|
+
const tokenXDecimals = Number(dlmmPool.tokenX.mint.decimals);
|
|
404
|
+
const tokenYDecimals = Number(dlmmPool.tokenY.mint.decimals);
|
|
405
|
+
const xIsSOL = tokenXMint.equals(WSOL_PK);
|
|
406
|
+
const yIsSOL = tokenYMint.equals(WSOL_PK);
|
|
407
|
+
// Calculate amounts in native units
|
|
408
|
+
// SOL goes into whichever side is WSOL; token goes into the other
|
|
409
|
+
let totalXAmount;
|
|
410
|
+
let totalYAmount;
|
|
411
|
+
if (xIsSOL) {
|
|
412
|
+
totalXAmount = new bn_js_1.default(Math.floor(amountSol * 10 ** tokenXDecimals));
|
|
413
|
+
totalYAmount = new bn_js_1.default(Math.floor(amountToken * 10 ** tokenYDecimals));
|
|
414
|
+
}
|
|
415
|
+
else if (yIsSOL) {
|
|
416
|
+
totalXAmount = new bn_js_1.default(Math.floor(amountToken * 10 ** tokenXDecimals));
|
|
417
|
+
totalYAmount = new bn_js_1.default(Math.floor(amountSol * 10 ** tokenYDecimals));
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
// Neither side is SOL — unusual, but handle gracefully
|
|
421
|
+
totalXAmount = new bn_js_1.default(Math.floor(amountSol * 10 ** tokenXDecimals));
|
|
422
|
+
totalYAmount = new bn_js_1.default(Math.floor(amountToken * 10 ** tokenYDecimals));
|
|
423
|
+
}
|
|
424
|
+
const isOneSidedSol = amountSol > 0 && amountToken === 0;
|
|
425
|
+
const isOneSidedToken = amountToken > 0 && amountSol === 0;
|
|
426
|
+
let minBinId;
|
|
427
|
+
let maxBinId;
|
|
428
|
+
let strategyType;
|
|
429
|
+
if (isOneSidedSol) {
|
|
430
|
+
// SOL-only: bins below active bin
|
|
431
|
+
minBinId = activeBin.binId - numBins;
|
|
432
|
+
maxBinId = activeBin.binId;
|
|
433
|
+
strategyType = toSdkStrategy(strategy);
|
|
434
|
+
}
|
|
435
|
+
else if (isOneSidedToken) {
|
|
436
|
+
// Token-only: bins above active bin
|
|
437
|
+
minBinId = activeBin.binId;
|
|
438
|
+
maxBinId = activeBin.binId + numBins;
|
|
439
|
+
strategyType = toSdkStrategy(strategy);
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
// Balanced: center around active bin
|
|
443
|
+
const halfBins = Math.floor(numBins / 2);
|
|
444
|
+
minBinId = activeBin.binId - halfBins;
|
|
445
|
+
maxBinId = activeBin.binId + halfBins;
|
|
446
|
+
strategyType = toSdkStrategy(strategy);
|
|
447
|
+
}
|
|
448
|
+
const newPosition = new web3_js_1.Keypair();
|
|
449
|
+
// Create position and add liquidity in one call
|
|
450
|
+
const createPositionTx = await dlmmPool.initializePositionAndAddLiquidityByStrategy({
|
|
451
|
+
positionPubKey: newPosition.publicKey,
|
|
452
|
+
user: wallet.publicKey,
|
|
453
|
+
totalXAmount,
|
|
454
|
+
totalYAmount,
|
|
455
|
+
strategy: {
|
|
456
|
+
maxBinId,
|
|
457
|
+
minBinId,
|
|
458
|
+
strategyType,
|
|
459
|
+
},
|
|
460
|
+
});
|
|
461
|
+
// Send the SDK's legacy Transaction directly with a fresh blockhash.
|
|
462
|
+
// The SDK call above can take 10-30s, so we must NOT use a stale blockhash.
|
|
463
|
+
try {
|
|
464
|
+
const result = await (0, send_rpc_1.sendAndConfirmLegacyTx)(connection, createPositionTx, wallet, {
|
|
465
|
+
extraSigners: [newPosition],
|
|
466
|
+
});
|
|
467
|
+
return {
|
|
468
|
+
txSignature: result.txSignature,
|
|
469
|
+
confirmed: result.confirmed,
|
|
470
|
+
error: result.error,
|
|
471
|
+
positionAddress: newPosition.publicKey.toBase58(),
|
|
472
|
+
poolAddress,
|
|
473
|
+
dex: this.name,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
catch (error) {
|
|
477
|
+
return {
|
|
478
|
+
txSignature: "",
|
|
479
|
+
confirmed: false,
|
|
480
|
+
error: error instanceof Error ? error.message : String(error),
|
|
481
|
+
positionAddress: newPosition.publicKey.toBase58(),
|
|
482
|
+
poolAddress,
|
|
483
|
+
dex: this.name,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
// ----- removeLiquidity -----
|
|
488
|
+
/**
|
|
489
|
+
* Remove liquidity from a Meteora DLMM pool position.
|
|
490
|
+
*
|
|
491
|
+
* If positionAddress is provided, removes from that specific position.
|
|
492
|
+
* Otherwise, removes from the first position found in the pool.
|
|
493
|
+
*
|
|
494
|
+
* If percentage is 100, also claims fees and closes the position.
|
|
495
|
+
*/
|
|
496
|
+
async removeLiquidity(params) {
|
|
497
|
+
const { poolAddress, percentage, positionAddress } = params;
|
|
498
|
+
const connection = (0, config_1.getConnection)();
|
|
499
|
+
const wallet = (0, config_1.getWallet)();
|
|
500
|
+
const poolPk = new web3_js_1.PublicKey(poolAddress);
|
|
501
|
+
const dlmmPool = await dlmm_1.default.create(connection, poolPk);
|
|
502
|
+
// Find user's position in this pool
|
|
503
|
+
const { userPositions } = await dlmmPool.getPositionsByUserAndLbPair(wallet.publicKey);
|
|
504
|
+
if (userPositions.length === 0) {
|
|
505
|
+
return {
|
|
506
|
+
txSignature: "",
|
|
507
|
+
confirmed: false,
|
|
508
|
+
error: `No positions found for user in pool ${poolAddress}`,
|
|
509
|
+
poolAddress,
|
|
510
|
+
dex: this.name,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
// Select position: by address if given, otherwise first found
|
|
514
|
+
let position;
|
|
515
|
+
if (positionAddress) {
|
|
516
|
+
const targetPk = new web3_js_1.PublicKey(positionAddress);
|
|
517
|
+
position = userPositions.find((p) => p.publicKey.equals(targetPk));
|
|
518
|
+
if (!position) {
|
|
519
|
+
return {
|
|
520
|
+
txSignature: "",
|
|
521
|
+
confirmed: false,
|
|
522
|
+
error: `Position ${positionAddress} not found in pool ${poolAddress}`,
|
|
523
|
+
poolAddress,
|
|
524
|
+
dex: this.name,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
position = userPositions[0];
|
|
530
|
+
}
|
|
531
|
+
// Calculate BPS: 100% = 10000 bps
|
|
532
|
+
const bps = new bn_js_1.default(Math.floor((percentage / 100) * 10000));
|
|
533
|
+
const shouldClaimAndClose = percentage >= 100;
|
|
534
|
+
const removeTxs = await dlmmPool.removeLiquidity({
|
|
535
|
+
position: position.publicKey,
|
|
536
|
+
user: wallet.publicKey,
|
|
537
|
+
fromBinId: position.positionData.lowerBinId,
|
|
538
|
+
toBinId: position.positionData.upperBinId,
|
|
539
|
+
bps,
|
|
540
|
+
shouldClaimAndClose,
|
|
541
|
+
});
|
|
542
|
+
// removeLiquidity returns Transaction | Transaction[], normalize to array
|
|
543
|
+
const txArray = Array.isArray(removeTxs) ? removeTxs : [removeTxs];
|
|
544
|
+
let lastSignature = "";
|
|
545
|
+
try {
|
|
546
|
+
for (const removeTx of txArray) {
|
|
547
|
+
const result = await (0, send_rpc_1.sendAndConfirmLegacyTx)(connection, removeTx, wallet);
|
|
548
|
+
lastSignature = result.txSignature;
|
|
549
|
+
if (!result.confirmed) {
|
|
550
|
+
return {
|
|
551
|
+
txSignature: lastSignature,
|
|
552
|
+
confirmed: false,
|
|
553
|
+
error: result.error,
|
|
554
|
+
positionAddress: position.publicKey.toBase58(),
|
|
555
|
+
poolAddress,
|
|
556
|
+
dex: this.name,
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return {
|
|
561
|
+
txSignature: lastSignature,
|
|
562
|
+
confirmed: true,
|
|
563
|
+
positionAddress: position.publicKey.toBase58(),
|
|
564
|
+
poolAddress,
|
|
565
|
+
dex: this.name,
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
catch (error) {
|
|
569
|
+
return {
|
|
570
|
+
txSignature: lastSignature,
|
|
571
|
+
confirmed: false,
|
|
572
|
+
error: error instanceof Error ? error.message : String(error),
|
|
573
|
+
positionAddress: position.publicKey.toBase58(),
|
|
574
|
+
poolAddress,
|
|
575
|
+
dex: this.name,
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
// ----- claimFees -----
|
|
580
|
+
/**
|
|
581
|
+
* Claim accumulated swap fees from a DLMM LP position.
|
|
582
|
+
*
|
|
583
|
+
* If positionAddress is provided, claims from that specific position.
|
|
584
|
+
* Otherwise, claims from the first position found in the pool.
|
|
585
|
+
*/
|
|
586
|
+
async claimFees(poolAddress, positionAddress) {
|
|
587
|
+
const connection = (0, config_1.getConnection)();
|
|
588
|
+
const wallet = (0, config_1.getWallet)();
|
|
589
|
+
const poolPk = new web3_js_1.PublicKey(poolAddress);
|
|
590
|
+
const dlmmPool = await dlmm_1.default.create(connection, poolPk);
|
|
591
|
+
const { userPositions } = await dlmmPool.getPositionsByUserAndLbPair(wallet.publicKey);
|
|
592
|
+
if (userPositions.length === 0) {
|
|
593
|
+
return {
|
|
594
|
+
txSignature: "",
|
|
595
|
+
confirmed: false,
|
|
596
|
+
error: `No positions found for user in pool ${poolAddress}`,
|
|
597
|
+
poolAddress,
|
|
598
|
+
dex: this.name,
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
// Select position
|
|
602
|
+
let position;
|
|
603
|
+
if (positionAddress) {
|
|
604
|
+
const targetPk = new web3_js_1.PublicKey(positionAddress);
|
|
605
|
+
position = userPositions.find((p) => p.publicKey.equals(targetPk));
|
|
606
|
+
if (!position) {
|
|
607
|
+
return {
|
|
608
|
+
txSignature: "",
|
|
609
|
+
confirmed: false,
|
|
610
|
+
error: `Position ${positionAddress} not found in pool ${poolAddress}`,
|
|
611
|
+
poolAddress,
|
|
612
|
+
dex: this.name,
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
position = userPositions[0];
|
|
618
|
+
}
|
|
619
|
+
// claimSwapFee may return null or throw "No fee to claim"
|
|
620
|
+
let claimFeeTx;
|
|
621
|
+
try {
|
|
622
|
+
claimFeeTx = await dlmmPool.claimSwapFee({
|
|
623
|
+
owner: wallet.publicKey,
|
|
624
|
+
position,
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
catch (error) {
|
|
628
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
629
|
+
// "No fee to claim" is expected for fresh positions — not a real error
|
|
630
|
+
return {
|
|
631
|
+
txSignature: "",
|
|
632
|
+
confirmed: true,
|
|
633
|
+
error: msg,
|
|
634
|
+
positionAddress: position.publicKey.toBase58(),
|
|
635
|
+
poolAddress,
|
|
636
|
+
dex: this.name,
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
if (!claimFeeTx) {
|
|
640
|
+
return {
|
|
641
|
+
txSignature: "",
|
|
642
|
+
confirmed: true,
|
|
643
|
+
error: "No fees to claim",
|
|
644
|
+
positionAddress: position.publicKey.toBase58(),
|
|
645
|
+
poolAddress,
|
|
646
|
+
dex: this.name,
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
try {
|
|
650
|
+
const result = await (0, send_rpc_1.sendAndConfirmLegacyTx)(connection, claimFeeTx, wallet);
|
|
651
|
+
return {
|
|
652
|
+
txSignature: result.txSignature,
|
|
653
|
+
confirmed: result.confirmed,
|
|
654
|
+
error: result.error,
|
|
655
|
+
positionAddress: position.publicKey.toBase58(),
|
|
656
|
+
poolAddress,
|
|
657
|
+
dex: this.name,
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
catch (error) {
|
|
661
|
+
return {
|
|
662
|
+
txSignature: "",
|
|
663
|
+
confirmed: false,
|
|
664
|
+
error: error instanceof Error ? error.message : String(error),
|
|
665
|
+
positionAddress: position.publicKey.toBase58(),
|
|
666
|
+
poolAddress,
|
|
667
|
+
dex: this.name,
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
// ----- listPositions -----
|
|
672
|
+
/**
|
|
673
|
+
* List user's LP positions in a DLMM pool.
|
|
674
|
+
*
|
|
675
|
+
* Returns position details including bin range, token amounts,
|
|
676
|
+
* unclaimed fees, and whether the position is in range.
|
|
677
|
+
*/
|
|
678
|
+
async listPositions(poolAddress) {
|
|
679
|
+
const connection = (0, config_1.getConnection)();
|
|
680
|
+
const wallet = (0, config_1.getWallet)();
|
|
681
|
+
const poolPk = new web3_js_1.PublicKey(poolAddress);
|
|
682
|
+
const dlmmPool = await dlmm_1.default.create(connection, poolPk);
|
|
683
|
+
const activeBin = await dlmmPool.getActiveBin();
|
|
684
|
+
const tokenXMint = dlmmPool.tokenX.publicKey;
|
|
685
|
+
const tokenYMint = dlmmPool.tokenY.publicKey;
|
|
686
|
+
const tokenXDecimals = Number(dlmmPool.tokenX.mint.decimals);
|
|
687
|
+
const tokenYDecimals = Number(dlmmPool.tokenY.mint.decimals);
|
|
688
|
+
const { userPositions } = await dlmmPool.getPositionsByUserAndLbPair(wallet.publicKey);
|
|
689
|
+
return userPositions.map((pos) => {
|
|
690
|
+
const binData = pos.positionData.positionBinData;
|
|
691
|
+
// Sum amounts and fees across all bins
|
|
692
|
+
let totalX = 0;
|
|
693
|
+
let totalY = 0;
|
|
694
|
+
let feeX = 0;
|
|
695
|
+
let feeY = 0;
|
|
696
|
+
for (const bin of binData) {
|
|
697
|
+
totalX += Number(bin.positionXAmount);
|
|
698
|
+
totalY += Number(bin.positionYAmount);
|
|
699
|
+
feeX += Number(bin.positionFeeXAmount);
|
|
700
|
+
feeY += Number(bin.positionFeeYAmount);
|
|
701
|
+
}
|
|
702
|
+
const lowerBinId = pos.positionData.lowerBinId;
|
|
703
|
+
const upperBinId = pos.positionData.upperBinId;
|
|
704
|
+
const inRange = activeBin.binId >= lowerBinId && activeBin.binId <= upperBinId;
|
|
705
|
+
return {
|
|
706
|
+
positionAddress: pos.publicKey.toBase58(),
|
|
707
|
+
poolAddress,
|
|
708
|
+
dex: this.name,
|
|
709
|
+
lowerBinId,
|
|
710
|
+
upperBinId,
|
|
711
|
+
amountX: totalX / 10 ** tokenXDecimals,
|
|
712
|
+
amountY: totalY / 10 ** tokenYDecimals,
|
|
713
|
+
tokenXMint: tokenXMint.toBase58(),
|
|
714
|
+
tokenYMint: tokenYMint.toBase58(),
|
|
715
|
+
feeX: feeX / 10 ** tokenXDecimals,
|
|
716
|
+
feeY: feeY / 10 ** tokenYDecimals,
|
|
717
|
+
inRange,
|
|
718
|
+
};
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
// ----- Internal helpers -----
|
|
722
|
+
async resolvePool(baseMint, quoteMint) {
|
|
723
|
+
const pool = await this.findPool(baseMint, quoteMint);
|
|
724
|
+
if (!pool) {
|
|
725
|
+
throw new types_1.PoolNotFoundError(this.name, baseMint, quoteMint);
|
|
726
|
+
}
|
|
727
|
+
return new web3_js_1.PublicKey(pool.address);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
exports.MeteoraDlmmAdapter = MeteoraDlmmAdapter;
|
|
731
|
+
// ---------------------------------------------------------------------------
|
|
732
|
+
// Auto-register
|
|
733
|
+
// ---------------------------------------------------------------------------
|
|
734
|
+
(0, index_1.registerAdapter)(new MeteoraDlmmAdapter());
|
|
735
|
+
//# sourceMappingURL=meteora-dlmm.js.map
|