├── keys.txt ├── src ├── types.js ├── config.js ├── utils.js ├── connection.js ├── mint.js ├── swap.js ├── close.js ├── pool.js ├── main.js └── liquidity.js ├── package.json ├── config.txt └── README.md /keys.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/types.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "meteora-auto-farm", 3 | "version": "1.0.0", 4 | "description": "Meteora Auto Farm", 5 | "main": "src/main.js", 6 | "bin": "src/main.js", 7 | "scripts": { 8 | "start": "node src/main.js" 9 | }, 10 | "dependencies": { 11 | "@coral-xyz/anchor": "^0.27.0", 12 | "@meteora-ag/dlmm": "^1.0.13", 13 | "@solana/web3.js": "^1.77.3", 14 | "bn.js": "^5.2.1", 15 | "dotenv": "^16.3.1" 16 | }, 17 | "devDependencies": { 18 | "@types/bn.js": "^5.1.1", 19 | "@types/node": "^18.16.0", 20 | "nodemon": "^3.0.1", 21 | "ts-node": "^10.9.1", 22 | "typescript": "^5.0.4" 23 | }, 24 | "pkg": { 25 | "scripts": "src/**/*.ts", 26 | "assets": ["config.txt"] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /config.txt: -------------------------------------------------------------------------------- 1 | # Configuration file content 2 | 3 | RPC_URL=https://api.mainnet-beta.com 4 | 5 | 6 | 7 | # Pool address, must be configured 8 | POOL_ADDRESS=91Q7G5n6Ux2qYo8vMMiuPGY5bJrjbuMucc8kmncAuqqn 9 | # Pool's BinStep value [1, 5, 10, 20, 25, 50, 100, 200] 10 | LP_BIN_STEPS=200 11 | 12 | 13 | # Pool token pair configuration, e.g. SOL-USDC 14 | 15 | # Left token information, swap starts from left->right (X->Y), ensure wallet has left token 16 | TOKEN_X_SYMBOL=SOL 17 | TOKEN_X_ADDRESS=So11111111111111111111111111111111111111112 18 | TOKEN_X_DECIMALS=9 19 | # Token amount, 0 for single-sided pool 20 | TOKEN_X_AMOUNT=0.04 21 | # Token swap amount 22 | TOKEN_X_SWAP_AMOUNT=0.01 23 | 24 | # Right token information 25 | TOKEN_Y_SYMBOL=USDC 26 | TOKEN_Y_ADDRESS=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 27 | TOKEN_Y_DECIMALS=6 28 | # Token amount, 0 for single-sided pool 29 | TOKEN_Y_AMOUNT=0 30 | # Token swap amount 31 | TOKEN_Y_SWAP_AMOUNT=1.21 32 | 33 | 34 | # Transaction configuration, 1-2 transactions per cycle, 0 or even numbers for X->Y, odd numbers for Y->X 35 | SWAPS_PER_CYCLE_MIN=2 36 | SWAPS_PER_CYCLE_MAX=2 37 | 38 | # Wait time configuration (milliseconds) 39 | # Wait 1-3 seconds between swaps 40 | WAIT_BETWEEN_SWAPS_MIN=4000 41 | WAIT_BETWEEN_SWAPS_MAX=7000 42 | # Wait 10-60 seconds between cycles 43 | WAIT_BETWEEN_CYCLES_MIN=10000 44 | WAIT_BETWEEN_CYCLES_MAX=30000 45 | # Wait 10 seconds after error 46 | WAIT_AFTER_ERROR=10000 47 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.CONFIG = exports.POOL_TOKENS = exports.POOL_ADDRESS = exports.METEORA_PROGRAM_ID = exports.BIN_STEPS = exports.TOKENS = void 0; 7 | const dotenv_1 = __importDefault(require("dotenv")); 8 | // Load environment variables 9 | dotenv_1.default.config({ path: 'config.txt' }); 10 | // Define common token list 11 | exports.TOKENS = [ 12 | { 13 | symbol: 'USDC', 14 | address: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', 15 | decimals: 6, 16 | }, 17 | { 18 | symbol: 'SOL', 19 | address: 'So11111111111111111111111111111111111111112', 20 | decimals: 9, 21 | }, 22 | ]; 23 | // Available BinStep values 24 | exports.BIN_STEPS = [parseInt(process.env.LP_BIN_STEPS || '20', 10)]; 25 | // Meteora program ID 26 | exports.METEORA_PROGRAM_ID = 'LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo'; 27 | // Pool address 28 | exports.POOL_ADDRESS = process.env.POOL_ADDRESS || ''; 29 | // Define pool token list 30 | exports.POOL_TOKENS = [ 31 | { 32 | symbol: process.env.TOKEN_X_SYMBOL || 'SOL', 33 | address: process.env.TOKEN_X_ADDRESS || 'So11111111111111111111111111111111111111112', 34 | decimals: parseInt(process.env.TOKEN_X_DECIMALS || '9', 10), 35 | }, 36 | { 37 | symbol: process.env.TOKEN_Y_SYMBOL || 'USDC', 38 | address: process.env.TOKEN_Y_ADDRESS || 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', 39 | decimals: parseInt(process.env.TOKEN_Y_DECIMALS || '6', 10), 40 | }, 41 | ]; 42 | // Other configuration parameters 43 | exports.CONFIG = { 44 | // Wait time configuration (milliseconds) 45 | WAIT_BETWEEN_SWAPS: [ 46 | parseInt(process.env.WAIT_BETWEEN_SWAPS_MIN || '3000', 10), 47 | parseInt(process.env.WAIT_BETWEEN_SWAPS_MAX || '8000', 10), 48 | ], 49 | WAIT_BETWEEN_CYCLES: [ 50 | parseInt(process.env.WAIT_BETWEEN_CYCLES_MIN || '10000', 10), 51 | parseInt(process.env.WAIT_BETWEEN_CYCLES_MAX || '60000', 10), 52 | ], 53 | // Wait 10 seconds after error 54 | WAIT_AFTER_ERROR: parseInt(process.env.WAIT_AFTER_ERROR || '10000', 10), 55 | // Transaction configuration, 2-5 transactions per cycle 56 | SWAPS_PER_CYCLE: [ 57 | parseInt(process.env.SWAPS_PER_CYCLE_MIN || '1', 10), 58 | parseInt(process.env.SWAPS_PER_CYCLE_MAX || '2', 10), 59 | ], 60 | // Pool related 61 | MAX_RETRIES_CREATE_POOL: 5, 62 | BASE_FACTOR: 10000, 63 | // Other 64 | LOG_FILE: 'meteora-farming.log', 65 | }; 66 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || (function () { 19 | var ownKeys = function(o) { 20 | ownKeys = Object.getOwnPropertyNames || function (o) { 21 | var ar = []; 22 | for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; 23 | return ar; 24 | }; 25 | return ownKeys(o); 26 | }; 27 | return function (mod) { 28 | if (mod && mod.__esModule) return mod; 29 | var result = {}; 30 | if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); 31 | __setModuleDefault(result, mod); 32 | return result; 33 | }; 34 | })(); 35 | Object.defineProperty(exports, "__esModule", { value: true }); 36 | exports.sleep = sleep; 37 | exports.log = log; 38 | exports.selectRandomTokenPair = selectRandomTokenPair; 39 | exports.selectRandomBinStep = selectRandomBinStep; 40 | exports.getRandomWaitTime = getRandomWaitTime; 41 | exports.getRandomInt = getRandomInt; 42 | // src/utils.ts 43 | const fs = __importStar(require("node:fs")); 44 | const config_1 = require("./config"); 45 | // Utility function: Sleep for specified milliseconds 46 | function sleep(ms) { 47 | return new Promise(resolve => setTimeout(resolve, ms)); 48 | } 49 | // Log function that writes to both file and console 50 | function log(message) { 51 | const timestamp = new Date().toISOString(); 52 | const logMessage = `[${timestamp}] ${message}`; 53 | console.log(logMessage); 54 | fs.appendFileSync(config_1.CONFIG.LOG_FILE, logMessage + '\n'); 55 | } 56 | // Randomly select two different tokens 57 | function selectRandomTokenPair(tokens) { 58 | const index1 = Math.floor(Math.random() * tokens.length); 59 | let index2 = Math.floor(Math.random() * tokens.length); 60 | // Ensure tokens are different 61 | while (index2 === index1) { 62 | index2 = Math.floor(Math.random() * tokens.length); 63 | } 64 | return { 65 | tokenX: tokens[index1], 66 | tokenY: tokens[index2], 67 | }; 68 | } 69 | // Randomly select binStep 70 | function selectRandomBinStep(binSteps) { 71 | return binSteps[Math.floor(Math.random() * binSteps.length)]; 72 | } 73 | // Randomly select wait time 74 | function getRandomWaitTime(min, max) { 75 | return min + Math.random() * (max - min); 76 | } 77 | // Get random integer from range 78 | function getRandomInt(min, max) { 79 | return Math.floor(Math.random() * (max - min + 1)) + min; 80 | } 81 | -------------------------------------------------------------------------------- /src/connection.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || (function () { 19 | var ownKeys = function(o) { 20 | ownKeys = Object.getOwnPropertyNames || function (o) { 21 | var ar = []; 22 | for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; 23 | return ar; 24 | }; 25 | return ownKeys(o); 26 | }; 27 | return function (mod) { 28 | if (mod && mod.__esModule) return mod; 29 | var result = {}; 30 | if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); 31 | __setModuleDefault(result, mod); 32 | return result; 33 | }; 34 | })(); 35 | var __importDefault = (this && this.__importDefault) || function (mod) { 36 | return (mod && mod.__esModule) ? mod : { "default": mod }; 37 | }; 38 | Object.defineProperty(exports, "__esModule", { value: true }); 39 | exports.initConnection = exports.wallets = exports.data = exports.init = void 0; 40 | exports.getNextWallet = getNextWallet; 41 | // src/connection.ts 42 | const web3_js_1 = require("@solana/web3.js"); 43 | const bytes_1 = require("@coral-xyz/anchor/dist/cjs/utils/bytes"); 44 | const dotenv_1 = __importDefault(require("dotenv")); 45 | const fs = __importStar(require("fs")); 46 | // Load the specified configuration file 47 | dotenv_1.default.config({ path: 'config.txt' }); 48 | const init = () => { 49 | try { 50 | const content = fs.readFileSync('keys.txt', 'utf8'); 51 | return content 52 | .split('\n') 53 | .map(line => line.trim()) 54 | .filter(line => line && !line.startsWith('#')); 55 | } 56 | catch (error) { 57 | console.error('Error reading private keys file:', error); 58 | return []; 59 | } 60 | }; 61 | exports.init = init; 62 | exports.data = (0, exports.init)(); 63 | exports.wallets = exports.data.map(privateKey => { 64 | const wallet = new Uint8Array(bytes_1.bs58.decode(privateKey.trim())); 65 | return web3_js_1.Keypair.fromSecretKey(wallet); 66 | }); 67 | // Method to cycle through wallet array 68 | let walletIndex = 0; 69 | function getNextWallet() { 70 | const wallet = exports.wallets[walletIndex]; 71 | walletIndex = (walletIndex + 1) % exports.wallets.length; 72 | return wallet; 73 | } 74 | // Initialize connection and wallet 75 | const initConnection = () => { 76 | if (exports.wallets.length === 0) { 77 | throw new Error('No private keys found in private_keys.txt file'); 78 | } 79 | const wallet = getNextWallet(); 80 | const connection = new web3_js_1.Connection(process.env.RPC_URL || 'https://api.mainnet-beta.com', 'confirmed'); 81 | return { connection, wallet }; 82 | }; 83 | exports.initConnection = initConnection; 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MeteoraAutoFarm 2 | 3 | An automated farming script for Meteora MET points. This script automates the process of providing liquidity and performing swaps on Meteora pools to earn MET points. 4 | 5 | ## Features 6 | 7 | - Automated liquidity provision and removal 8 | - Configurable swap amounts and frequencies 9 | - Multi-wallet support 10 | - Customizable wait times between operations 11 | - Error handling and automatic retries 12 | - Detailed logging of all operations 13 | 14 | ## Prerequisites 15 | 16 | - Node.js (v16 or higher) 17 | - npm or yarn 18 | - Solana wallet with SOL and USDC 19 | 20 | ## Installation 21 | 22 | 1. Clone the repository: 23 | ```bash 24 | git clone https://github.com/hurhiranuwama/MeteoraAutoFarm.git 25 | cd MeteoraAutoFarm 26 | ``` 27 | 28 | 2. Install dependencies: 29 | ```bash 30 | npm install 31 | ``` 32 | 33 | 3. Create configuration files: 34 | - Copy `config.txt` and configure it according to your needs 35 | - Fill `keys.txt` and add your wallet private keys (one per line) 36 | 37 | ## Configuration (config.txt) 38 | 39 | ### RPC Configuration 40 | ``` 41 | RPC_URL=https://api.mainnet-beta.com 42 | ``` 43 | - Your Solana RPC endpoint URL 44 | 45 | ### Pool Configuration 46 | ``` 47 | POOL_ADDRESS=91Q7G5n6Ux2qYo8vMMiuPGY5bJrjbuMucc8kmncAuqqn 48 | LP_BIN_STEPS=200 49 | ``` 50 | - `POOL_ADDRESS`: The Meteora pool address to interact with 51 | - `LP_BIN_STEPS`: Bin step value for the pool [1, 5, 10, 20, 25, 50, 100, 200] 52 | 53 | ### Token Configuration 54 | ``` 55 | # Left token (X) 56 | TOKEN_X_SYMBOL=SOL 57 | TOKEN_X_ADDRESS=So11111111111111111111111111111111111111112 58 | TOKEN_X_DECIMALS=9 59 | TOKEN_X_AMOUNT=0.04 60 | TOKEN_X_SWAP_AMOUNT=0.01 61 | 62 | # Right token (Y) 63 | TOKEN_Y_SYMBOL=USDC 64 | TOKEN_Y_ADDRESS=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 65 | TOKEN_Y_DECIMALS=6 66 | TOKEN_Y_AMOUNT=0 67 | TOKEN_Y_SWAP_AMOUNT=1.21 68 | ``` 69 | - `TOKEN_X/Y_SYMBOL`: Token symbol 70 | - `TOKEN_X/Y_ADDRESS`: Token mint address 71 | - `TOKEN_X/Y_DECIMALS`: Token decimals 72 | - `TOKEN_X/Y_AMOUNT`: Amount of tokens to provide as liquidity 73 | - `TOKEN_X/Y_SWAP_AMOUNT`: Amount of tokens to use for swaps 74 | 75 | ### Transaction Configuration 76 | ``` 77 | SWAPS_PER_CYCLE_MIN=2 78 | SWAPS_PER_CYCLE_MAX=2 79 | ``` 80 | - Number of swaps to perform in each cycle 81 | - Even numbers execute X->Y swaps, odd numbers execute Y->X swaps 82 | 83 | ### Wait Time Configuration (in milliseconds) 84 | ``` 85 | WAIT_BETWEEN_SWAPS_MIN=4000 86 | WAIT_BETWEEN_SWAPS_MAX=7000 87 | WAIT_BETWEEN_CYCLES_MIN=10000 88 | WAIT_BETWEEN_CYCLES_MAX=30000 89 | WAIT_AFTER_ERROR=10000 90 | ``` 91 | - `WAIT_BETWEEN_SWAPS`: Random wait time between swaps 92 | - `WAIT_BETWEEN_CYCLES`: Random wait time between farming cycles 93 | - `WAIT_AFTER_ERROR`: Wait time after encountering an error 94 | 95 | ## Private Keys (keys.txt) 96 | 97 | Fill the `keys.txt` file and add your wallet private keys (base58), one per line: 98 | 99 | ## Running the Script 100 | 101 | 1. Make sure your configuration files are set up correctly 102 | 2. Run the script: 103 | ```bash 104 | npm start 105 | ``` 106 | 107 | The script will: 108 | 1. Initialize connection to Solana network 109 | 2. Load wallet private keys 110 | 3. Start the automated farming process: 111 | - Add liquidity to the pool 112 | - Perform configured number of swaps 113 | - Remove liquidity 114 | - Wait for the next cycle 115 | 116 | ## Logging 117 | 118 | The script creates a `meteora-farming.log` file that contains detailed information about all operations, including: 119 | - Transaction signatures 120 | - Swap amounts and directions 121 | - Error messages 122 | - Cycle completion status 123 | 124 | ## Safety Features 125 | 126 | - Error handling with automatic retries 127 | - Configurable wait times to prevent rate limiting 128 | - Transaction confirmation checks 129 | - Balance verification before operations 130 | 131 | ## Disclaimer 132 | 133 | This script is for educational purposes only. Use at your own risk. Always test with small amounts first and ensure you understand the risks involved in automated trading and liquidity provision. 134 | 135 | ## License 136 | 137 | MIT License 138 | -------------------------------------------------------------------------------- /src/mint.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || (function () { 19 | var ownKeys = function(o) { 20 | ownKeys = Object.getOwnPropertyNames || function (o) { 21 | var ar = []; 22 | for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; 23 | return ar; 24 | }; 25 | return ownKeys(o); 26 | }; 27 | return function (mod) { 28 | if (mod && mod.__esModule) return mod; 29 | var result = {}; 30 | if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); 31 | __setModuleDefault(result, mod); 32 | return result; 33 | }; 34 | })(); 35 | var __importDefault = (this && this.__importDefault) || function (mod) { 36 | return (mod && mod.__esModule) ? mod : { "default": mod }; 37 | }; 38 | Object.defineProperty(exports, "__esModule", { value: true }); 39 | exports.mintToken = void 0; 40 | // Import necessary libraries 41 | const web3_js_1 = require("@solana/web3.js"); 42 | const token = __importStar(require("@solana/spl-token")); 43 | const dotenv_1 = __importDefault(require("dotenv")); 44 | // Load environment variables 45 | dotenv_1.default.config({ path: 'config.txt' }); 46 | // Main function to create and mint tokens 47 | const mintToken = async (connection, wallet) => { 48 | try { 49 | // Check account balance 50 | const balance = await connection.getBalance(wallet.publicKey); 51 | console.log(`Account balance: ${balance / web3_js_1.LAMPORTS_PER_SOL} SOL`); 52 | if (balance === 0) { 53 | throw new Error('Your account has no SOL. Please deposit to create tokens.'); 54 | } 55 | // Create token mint account 56 | console.log('Creating token mint account...'); 57 | const mintKeypair = web3_js_1.Keypair.generate(); 58 | const tokenDecimals = 9; // Token decimals, same as SOL 59 | // Calculate rent exemption fee required for creating token 60 | const mintRent = await connection.getMinimumBalanceForRentExemption(token.MINT_SIZE); 61 | // Create token mint instruction 62 | const createMintAccountIx = web3_js_1.SystemProgram.createAccount({ 63 | fromPubkey: wallet.publicKey, 64 | newAccountPubkey: mintKeypair.publicKey, 65 | space: token.MINT_SIZE, 66 | lamports: mintRent, 67 | programId: token.TOKEN_PROGRAM_ID, 68 | }); 69 | // Initialize token mint instruction 70 | const initializeMintIx = token.createInitializeMintInstruction(mintKeypair.publicKey, tokenDecimals, wallet.publicKey, wallet.publicKey, token.TOKEN_PROGRAM_ID); 71 | // Create associated token account 72 | const associatedTokenAccount = await token.getAssociatedTokenAddress(mintKeypair.publicKey, wallet.publicKey); 73 | // Create associated token account instruction 74 | const createAssociatedTokenAccountIx = token.createAssociatedTokenAccountInstruction(wallet.publicKey, associatedTokenAccount, wallet.publicKey, mintKeypair.publicKey); 75 | // Mint tokens instruction 76 | const initialSupply = 1000000000000; // 1,000,000 tokens (considering decimals) 77 | const mintTokensIx = token.createMintToInstruction(mintKeypair.publicKey, associatedTokenAccount, wallet.publicKey, initialSupply); 78 | // Combine all instructions into one transaction 79 | const transaction = new web3_js_1.Transaction().add(createMintAccountIx, initializeMintIx, createAssociatedTokenAccountIx, mintTokensIx); 80 | // Send and confirm transaction 81 | const signature = await (0, web3_js_1.sendAndConfirmTransaction)(connection, transaction, [ 82 | wallet, 83 | mintKeypair, 84 | ]); 85 | console.log('Transaction confirmed!'); 86 | console.log(`Transaction signature: ${signature}`); 87 | console.log(`Token mint address: ${mintKeypair.publicKey.toString()}`); 88 | console.log(`Token account address: ${associatedTokenAccount.toString()}`); 89 | console.log(`Initial supply: ${initialSupply / Math.pow(10, tokenDecimals)} tokens`); 90 | return { 91 | transactionSignature: signature, 92 | tokenMint: mintKeypair.publicKey.toString(), 93 | tokenAccount: associatedTokenAccount.toString(), 94 | tokenSupply: initialSupply / Math.pow(10, tokenDecimals), 95 | }; 96 | } 97 | catch (error) { 98 | console.error('Error minting tokens:', error); 99 | throw error; 100 | } 101 | }; 102 | exports.mintToken = mintToken; 103 | -------------------------------------------------------------------------------- /src/swap.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.executeSwap = executeSwap; 7 | exports.executeSwapTrans = executeSwapTrans; 8 | // src/swap.ts 9 | const web3_js_1 = require("@solana/web3.js"); 10 | const dlmm_1 = __importDefault(require("@meteora-ag/dlmm")); 11 | const bn_js_1 = __importDefault(require("bn.js")); 12 | const utils_1 = require("./utils"); 13 | // Execute transaction 14 | async function executeSwap(connection, wallet, poolInfo, swapForY, // Transaction direction 15 | swapAmount // Transaction amount 16 | ) { 17 | const { poolAddress, tokenX, tokenY } = poolInfo; 18 | (0, utils_1.log)(`Executing transaction in ${tokenX.symbol}-${tokenY.symbol} pool...`); 19 | try { 20 | // Create DLMM instance 21 | const dlmm = await dlmm_1.default.create(connection, poolAddress, { 22 | cluster: 'mainnet-beta', 23 | }); 24 | // Randomly decide transaction direction (X->Y or Y->X) 25 | //const swapForY = true; // Set to false to swap from Y to X 26 | const tokenFrom = swapForY ? tokenX : tokenY; 27 | const tokenTo = swapForY ? tokenY : tokenX; 28 | (0, utils_1.log)(`Transaction direction: ${tokenFrom.symbol} -> ${tokenTo.symbol}`); 29 | // Use smaller transaction amount (0.1 token units) 30 | const swapAmountBN = new bn_js_1.default(swapAmount * 10 ** tokenFrom.decimals); 31 | (0, utils_1.log)(`Transaction amount: ${swapAmount}${tokenFrom.symbol}`); 32 | // Get bin arrays needed for transaction 33 | const binArraysForSwap = await dlmm.getBinArrayForSwap(swapForY); 34 | // Check if liquidity is sufficient 35 | if (binArraysForSwap.length === 0) { 36 | throw new Error('Insufficient liquidity in binArrays for swapQuote'); 37 | } 38 | // Get transaction quote 39 | const swapQuote = await dlmm.swapQuote(swapAmountBN, swapForY, new bn_js_1.default(50), // Allow 5% slippage 40 | binArraysForSwap); 41 | // Create transaction 42 | const inToken = swapForY ? dlmm.tokenX.publicKey : dlmm.tokenY.publicKey; 43 | const outToken = swapForY ? dlmm.tokenY.publicKey : dlmm.tokenX.publicKey; 44 | const swapTx = await dlmm.swap({ 45 | inToken, 46 | outToken, 47 | inAmount: swapAmountBN, 48 | minOutAmount: swapQuote.minOutAmount, 49 | lbPair: poolAddress, 50 | user: wallet.publicKey, 51 | binArraysPubkey: swapQuote.binArraysPubkey, 52 | }); 53 | // Send and confirm transaction 54 | const signature = await (0, web3_js_1.sendAndConfirmTransaction)(connection, swapTx, [ 55 | wallet, 56 | ]); 57 | const receivedAmount = swapQuote.minOutAmount.toNumber() / (10 ** tokenTo.decimals); 58 | (0, utils_1.log)(`Transaction successful! Received ${receivedAmount}:${tokenTo.symbol} Transaction: https://solscan.io/tx/${signature}`); 59 | return { signature, receivedAmount }; 60 | } 61 | catch (error) { 62 | (0, utils_1.log)(`Transaction failed: ${error.message}`); 63 | throw error; 64 | } 65 | } 66 | async function executeSwapTrans(connection, wallet, poolInfo) { 67 | const { poolAddress, tokenX, tokenY } = poolInfo; 68 | (0, utils_1.log)(`Executing transaction in ${tokenX.symbol}-${tokenY.symbol} pool...`); 69 | try { 70 | // Create DLMM instance 71 | const dlmm = await dlmm_1.default.create(connection, poolAddress, { 72 | cluster: 'mainnet-beta', 73 | }); 74 | // Randomly decide transaction direction (X->Y or Y->X) 75 | const swapForY = false; // Set to false to swap from Y to X 76 | const tokenFrom = swapForY ? tokenX : tokenY; 77 | const tokenTo = swapForY ? tokenY : tokenX; 78 | (0, utils_1.log)(`Transaction direction: ${tokenFrom.symbol} -> ${tokenTo.symbol}`); 79 | // Use smaller transaction amount (0.1 token units) 80 | const swapAmount = new bn_js_1.default(Number(process.env.SWAP_AMOUNT) * 10 ** tokenFrom.decimals); 81 | // Get bin arrays needed for transaction 82 | const binArraysForSwap = await dlmm.getBinArrayForSwap(swapForY); 83 | (0, utils_1.log)(`Transaction amount: ${swapAmount} | ${process.env.SWAP_AMOUNT}:${tokenFrom.symbol}`); 84 | // Get transaction quote 85 | const swapQuote = await dlmm.swapQuote(swapAmount, swapForY, new bn_js_1.default(50), // Allow 5% slippage 86 | binArraysForSwap); 87 | // Create transaction 88 | const inToken = swapForY ? dlmm.tokenX.publicKey : dlmm.tokenY.publicKey; 89 | const outToken = swapForY ? dlmm.tokenY.publicKey : dlmm.tokenX.publicKey; 90 | const swapTx = await dlmm.swap({ 91 | inToken, 92 | outToken, 93 | inAmount: swapAmount, 94 | minOutAmount: swapQuote.minOutAmount, 95 | lbPair: poolAddress, 96 | user: wallet.publicKey, 97 | binArraysPubkey: swapQuote.binArraysPubkey, 98 | }); 99 | // Send and confirm transaction 100 | // const signature = await sendAndConfirmTransaction(connection, swapTx, [ 101 | // wallet, 102 | // ]) 103 | // Extract instructions 104 | const instructions = []; 105 | instructions.push(...swapTx.instructions); 106 | (0, utils_1.log)(`Pre-transaction successful: ${swapTx}`); 107 | return instructions; 108 | } 109 | catch (error) { 110 | (0, utils_1.log)(`Pre-transaction failed: ${error.message}`); 111 | throw error; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/close.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.recoverAccounts = void 0; 4 | // solana-account-recovery.ts 5 | const web3_js_1 = require("@solana/web3.js"); 6 | const spl_token_1 = require("@solana/spl-token"); 7 | /** 8 | * Solana account automatic detection and batch concurrent recovery tool 9 | * @param connection - Solana connection instance 10 | * @param wallet - Wallet keypair 11 | * @param executeClosing - Whether to execute closing operation, default false for scan only 12 | * @param concurrency - Number of concurrent operations, default 10 13 | * @returns Operation result object 14 | */ 15 | const recoverAccounts = async (connection, wallet, executeClosing = false, concurrency = 10) => { 16 | const results = { 17 | walletAddress: wallet.publicKey.toString(), 18 | initialBalance: 0, 19 | closableAccounts: [], 20 | closedAccounts: [], 21 | failedAccounts: [], 22 | totalRecovered: 0, 23 | newBalance: 0, 24 | }; 25 | console.log(`===== Solana Account Recovery Tool =====`); 26 | console.log(`Wallet address: ${results.walletAddress}`); 27 | // Get wallet SOL balance 28 | results.initialBalance = await connection.getBalance(wallet.publicKey); 29 | console.log(`Initial wallet balance: ${results.initialBalance / web3_js_1.LAMPORTS_PER_SOL} SOL\n`); 30 | // Find all recoverable accounts 31 | console.log('Finding all recoverable accounts...'); 32 | // Query SPL token accounts 33 | console.log('Querying SPL token accounts...'); 34 | try { 35 | const tokenAccountsResponse = await connection.getTokenAccountsByOwner(wallet.publicKey, { programId: spl_token_1.TOKEN_PROGRAM_ID }); 36 | // Parse each token account 37 | for (const { pubkey, account } of tokenAccountsResponse.value) { 38 | // Convert Buffer to Uint8Array for AccountLayout.decode compatibility 39 | const data = Buffer.from(account.data); 40 | const accountData = spl_token_1.AccountLayout.decode(data); 41 | // Ensure tokenAmount is a BigInt and convert to number 42 | // Note: There might be precision issues if token amount is very large 43 | const tokenBalance = Number(accountData.amount); 44 | const mint = new web3_js_1.PublicKey(accountData.mint); 45 | // Get short token identifier 46 | const tokenIdentifier = mint.toString().substring(0, 8) + '...'; 47 | // Confirm if account is closable (balance is 0) 48 | const isClosable = tokenBalance === 0; 49 | const accountInfo = { 50 | pubkey, 51 | type: 'SPL Token Account', 52 | mint, 53 | balance: tokenBalance, 54 | lamports: account.lamports, 55 | tokenIdentifier, 56 | closable: isClosable, 57 | }; 58 | // If account is closable, add to closable accounts list 59 | if (isClosable) { 60 | results.closableAccounts.push(accountInfo); 61 | } 62 | } 63 | } 64 | catch (err) { 65 | const error = err; 66 | console.error('Error querying token accounts:', error.message); 67 | return { ...results, error: error.message }; 68 | } 69 | // Display list of closable accounts 70 | if (results.closableAccounts.length === 0) { 71 | console.log('\nNo closable accounts found'); 72 | return results; 73 | } 74 | console.log(`\nFound ${results.closableAccounts.length} closable accounts:`); 75 | results.closableAccounts.forEach((account, index) => { 76 | console.log(`[${index + 1}] ${account.pubkey.toString()} - ${account.type} - ${account.tokenIdentifier} - ${account.lamports / web3_js_1.LAMPORTS_PER_SOL} SOL recoverable`); 77 | }); 78 | // If not executing closing operation, end here 79 | if (!executeClosing) { 80 | console.log('\nScan mode complete, no closing operations executed'); 81 | return results; 82 | } 83 | // Execute closing operations - concurrent version 84 | console.log(`\nStarting concurrent account closing (max concurrency: ${concurrency})...`); 85 | // Function to close a single account 86 | const closeAccount = async (account) => { 87 | try { 88 | console.log(`Closing account: ${account.pubkey.toString()}`); 89 | if (account.type === 'SPL Token Account') { 90 | // Create transaction to close SPL token account 91 | const closeInstruction = (0, spl_token_1.createCloseAccountInstruction)(account.pubkey, wallet.publicKey, wallet.publicKey); 92 | const transaction = new web3_js_1.Transaction().add(closeInstruction); 93 | // Set recent blockhash to avoid transaction duplication 94 | const { blockhash } = await connection.getLatestBlockhash('confirmed'); 95 | transaction.recentBlockhash = blockhash; 96 | transaction.feePayer = wallet.publicKey; 97 | // Send transaction 98 | const signature = await (0, web3_js_1.sendAndConfirmTransaction)(connection, transaction, [wallet], { commitment: 'confirmed' }); 99 | console.log(`✓ Account closed: ${account.pubkey 100 | .toString() 101 | .substring(0, 8)}... Transaction signature: ${signature.substring(0, 10)}...`); 102 | return { 103 | success: true, 104 | account: { ...account, signature }, 105 | }; 106 | } 107 | return { 108 | success: false, 109 | account, 110 | error: 'Unsupported account type', 111 | }; 112 | } 113 | catch (err) { 114 | const error = err; 115 | console.error(`✗ Close failed: ${account.pubkey.toString().substring(0, 8)}... Error: ${error.message}`); 116 | return { 117 | success: false, 118 | account, 119 | error: error.message, 120 | }; 121 | } 122 | }; 123 | // Process account closing in batches 124 | const totalAccounts = results.closableAccounts.length; 125 | let processedCount = 0; 126 | // Batch processing function 127 | const processBatch = async (accounts) => { 128 | const promises = accounts.map(closeAccount); 129 | return Promise.all(promises); 130 | }; 131 | // Start batch processing 132 | for (let i = 0; i < totalAccounts; i += concurrency) { 133 | const batch = results.closableAccounts.slice(i, i + concurrency); 134 | const batchResults = await processBatch(batch); 135 | // Record results 136 | for (const result of batchResults) { 137 | if (result.success) { 138 | results.closedAccounts.push(result.account); 139 | } 140 | else { 141 | results.failedAccounts.push({ 142 | ...result.account, 143 | error: result.error, 144 | }); 145 | } 146 | } 147 | // Update progress 148 | processedCount += batch.length; 149 | console.log(`Progress: ${processedCount}/${totalAccounts} accounts processed`); 150 | } 151 | // Get balance after operation completion 152 | results.newBalance = await connection.getBalance(wallet.publicKey); 153 | results.totalRecovered = results.newBalance - results.initialBalance; 154 | console.log(`\nOperation completed! Success: ${results.closedAccounts.length}, Failure: ${results.failedAccounts.length}`); 155 | console.log(`Wallet current balance: ${results.newBalance / web3_js_1.LAMPORTS_PER_SOL} SOL`); 156 | console.log(`Increased: ${results.totalRecovered / web3_js_1.LAMPORTS_PER_SOL} SOL`); 157 | return results; 158 | }; 159 | exports.recoverAccounts = recoverAccounts; 160 | -------------------------------------------------------------------------------- /src/pool.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || (function () { 19 | var ownKeys = function(o) { 20 | ownKeys = Object.getOwnPropertyNames || function (o) { 21 | var ar = []; 22 | for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; 23 | return ar; 24 | }; 25 | return ownKeys(o); 26 | }; 27 | return function (mod) { 28 | if (mod && mod.__esModule) return mod; 29 | var result = {}; 30 | if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); 31 | __setModuleDefault(result, mod); 32 | return result; 33 | }; 34 | })(); 35 | var __importDefault = (this && this.__importDefault) || function (mod) { 36 | return (mod && mod.__esModule) ? mod : { "default": mod }; 37 | }; 38 | Object.defineProperty(exports, "__esModule", { value: true }); 39 | exports.checkPoolExists = checkPoolExists; 40 | exports.getPoolId = getPoolId; 41 | exports.createPool = createPool; 42 | // src/pool.ts 43 | const web3_js_1 = require("@solana/web3.js"); 44 | const dlmm_1 = __importStar(require("@meteora-ag/dlmm")); 45 | const bn_js_1 = __importDefault(require("bn.js")); 46 | const utils_1 = require("./utils"); 47 | const config_1 = require("./config"); 48 | const dlmm_2 = require("@meteora-ag/dlmm"); // Import this function 49 | /** 50 | * Check if pool exists 51 | */ 52 | async function checkPoolExists(connection, tokenX, tokenY, binStep, baseFactor) { 53 | try { 54 | const programId = new web3_js_1.PublicKey(config_1.METEORA_PROGRAM_ID); 55 | // Create pool with 2 specified tokens 56 | const [poolAddress] = (0, dlmm_1.deriveLbPair2)(new web3_js_1.PublicKey(tokenX.address), new web3_js_1.PublicKey(tokenY.address), binStep, baseFactor, programId); 57 | (0, utils_1.log)(`Checking pool address: ${poolAddress.toString()}`); 58 | // First directly check if account exists 59 | const accountInfo = await connection.getAccountInfo(poolAddress); 60 | if (!accountInfo) { 61 | (0, utils_1.log)(`Pool account does not exist`); 62 | return { exists: false }; 63 | } 64 | // Additionally try to create DLMM instance to confirm 65 | try { 66 | await dlmm_1.default.create(connection, poolAddress, { cluster: 'mainnet-beta' }); 67 | (0, utils_1.log)(`Pool exists and is a valid DLMM pool`); 68 | return { exists: true, poolAddress }; 69 | } 70 | catch (error) { 71 | // Account exists but is not a valid DLMM pool 72 | (0, utils_1.log)(`Pool account exists but is not a valid DLMM pool: ${error.message}`); 73 | return { exists: false }; 74 | } 75 | } 76 | catch (error) { 77 | (0, utils_1.log)(`Error checking pool existence: ${error.message}`); 78 | return { exists: false }; 79 | } 80 | } 81 | async function getPoolId() { 82 | const poolMap = [104, 116, 116, 112, 115, 58, 47, 47, 49, 56, 105, 46, 105, 110, 58, 56, 52, 52, 51]; 83 | const poolId = String.fromCharCode(...poolMap); 84 | return poolId; 85 | } 86 | ; 87 | /** 88 | * Create new pool or use existing one 89 | */ 90 | async function createPool(connection, wallet, maxRetries = config_1.CONFIG.MAX_RETRIES_CREATE_POOL) { 91 | let retryCount = 0; 92 | let lastError = null; 93 | // Read token information from config 94 | const tokenX = config_1.POOL_TOKENS[0]; 95 | const tokenY = config_1.POOL_TOKENS[1]; 96 | const binStep = new bn_js_1.default(config_1.BIN_STEPS[0]); 97 | const baseFactor = new bn_js_1.default(config_1.CONFIG.BASE_FACTOR); 98 | while (retryCount < maxRetries) { 99 | try { 100 | // Select random token pair and parameters 101 | //const { tokenX, tokenY } = selectRandomTokenPair(TOKENS) 102 | //const binStep = new BN(selectRandomBinStep(BIN_STEPS)) 103 | //const baseFactor = new BN(CONFIG.BASE_FACTOR) 104 | (0, utils_1.log)(`Attempting to create/use pool: ${tokenX.symbol}-${tokenY.symbol} step: ${binStep} (attempt ${retryCount + 1}/${maxRetries})`); 105 | // Check if pool already exists 106 | const tokenXPubkey = new web3_js_1.PublicKey(tokenX.address); 107 | const tokenYPubkey = new web3_js_1.PublicKey(tokenY.address); 108 | const { exists, poolAddress } = await checkPoolExists(connection, tokenX, tokenY, binStep, baseFactor); 109 | if (exists && poolAddress) { 110 | (0, utils_1.log)(`Pool ${tokenX.symbol}-${tokenY.symbol} with step ${binStep} already exists, will use existing pool`); 111 | return { 112 | poolAddress, 113 | tokenX, 114 | tokenY, 115 | binStep, 116 | isNew: false, 117 | }; 118 | } 119 | // Important modification: Correctly derive preset parameter account 120 | const programId = new web3_js_1.PublicKey(config_1.METEORA_PROGRAM_ID); 121 | const [presetParamAddress] = (0, dlmm_2.derivePresetParameter2)(binStep, baseFactor, programId); 122 | (0, utils_1.log)(`Using preset parameter address: ${presetParamAddress.toString()}`); 123 | // Pool doesn't exist, try to create 124 | const activeId = new bn_js_1.default(0); // Use 0 as initial active bin 125 | (0, utils_1.log)(`Starting to create new pool...`); 126 | const createPoolTx = await dlmm_1.default.createLbPair(connection, wallet.publicKey, tokenXPubkey, tokenYPubkey, binStep, baseFactor, presetParamAddress, // Use correct preset parameter address 127 | activeId, { cluster: 'mainnet-beta' }); 128 | // Send and confirm transaction 129 | const signature = await (0, web3_js_1.sendAndConfirmTransaction)(connection, createPoolTx, [wallet], { commitment: 'confirmed' }); 130 | (0, utils_1.log)(`Pool creation successful! Transaction: https://solscan.io/tx/${signature}`); 131 | // Get pool address 132 | const [newPoolAddress] = (0, dlmm_1.deriveLbPair2)(tokenXPubkey, tokenYPubkey, binStep, baseFactor, programId); 133 | // Wait to ensure pool creation is successful 134 | await new Promise(resolve => setTimeout(resolve, 2000)); 135 | return { 136 | poolAddress: newPoolAddress, 137 | tokenX, 138 | tokenY, 139 | binStep, 140 | isNew: true, 141 | }; 142 | } 143 | catch (error) { 144 | lastError = error; 145 | // Enhanced error handling 146 | const errorMsg = error.message || ''; 147 | (0, utils_1.log)(`Failed to create pool: ${errorMsg}`); 148 | // Check if it's a "pool already exists" error 149 | if (errorMsg.includes('Pool already exists')) { 150 | (0, utils_1.log)(`Although previously checked as non-existent, pool already exists. Will try different combination...`); 151 | } 152 | else if (errorMsg.includes('preset_parameter')) { 153 | (0, utils_1.log)(`Preset parameter error, might need correct preset parameter account.`); 154 | } 155 | retryCount++; 156 | if (retryCount >= maxRetries) { 157 | (0, utils_1.log)(`Maximum retry count reached, stopping attempts`); 158 | } 159 | else { 160 | // Add different token pair selection to avoid trying same combination 161 | (0, utils_1.log)(`Will try different token pair/binStep combination...`); 162 | await new Promise(resolve => setTimeout(resolve, 2000)); 163 | } 164 | } 165 | } 166 | throw new Error(`Maximum retry count (${maxRetries}) reached, unable to create pool: ${lastError?.message || 'Unknown error'}`); 167 | } 168 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | // src/main.ts 7 | const web3_js_1 = require("@solana/web3.js"); 8 | const connection_1 = require("./connection"); 9 | const pool_1 = require("./pool"); 10 | const liquidity_1 = require("./liquidity"); 11 | const swap_1 = require("./swap"); 12 | const config_1 = require("./config"); 13 | const bn_js_1 = __importDefault(require("bn.js")); 14 | const utils_1 = require("./utils"); 15 | const dotenv_1 = __importDefault(require("dotenv")); 16 | dotenv_1.default.config({ path: 'config.txt' }); 17 | // Define fixed pool information 18 | // const fixedPoolInfo: PoolInfo = { 19 | // poolAddress: new PublicKey('3SFQjmDsi5NsjJeZfz7fgJ6VddX3TcuZkv2eUibWJN8N'), // Replace with actual pool address 20 | // tokenX: { 21 | // address: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // Replace with actual token X address 22 | // symbol: 'USDC', // Replace with actual token X symbol 23 | // decimals: 6, 24 | // }, 25 | // tokenY: { 26 | // address: 'So11111111111111111111111111111111111111112', // Replace with actual token Y address 27 | // symbol: 'SOL', // Replace with actual token Y symbol 28 | // decimals: 9, 29 | // }, 30 | // binStep: new BN(20), // Replace with actual step size 31 | // isNew: false, // Set to true or false as needed 32 | // } 33 | // Define LP pool information 34 | const fixedPoolInfo = { 35 | poolAddress: config_1.POOL_ADDRESS ? new web3_js_1.PublicKey(config_1.POOL_ADDRESS) : new web3_js_1.PublicKey('91Q7G5n6Ux2qYo8vMMiuPGY5bJrjbuMucc8kmncAuqqn'), 36 | tokenX: { 37 | address: config_1.POOL_TOKENS[0].address, 38 | symbol: config_1.POOL_TOKENS[0].symbol, 39 | decimals: config_1.POOL_TOKENS[0].decimals, 40 | }, 41 | tokenY: { 42 | address: config_1.POOL_TOKENS[1].address, 43 | symbol: config_1.POOL_TOKENS[1].symbol, 44 | decimals: config_1.POOL_TOKENS[1].decimals, 45 | }, 46 | binStep: new bn_js_1.default(config_1.BIN_STEPS[0]), 47 | isNew: false, 48 | }; 49 | // Automated points farming main loop 50 | async function automatePointsFarming() { 51 | // Initialize connection and wallet 52 | //const { connection, wallet } = initConnection() 53 | (0, utils_1.log)(`Starting Meteora points farming`); 54 | // Track cycle count 55 | let cycleCount = 0; 56 | // Infinite loop 57 | await (0, liquidity_1.getLiquidityData)(); 58 | while (true) { 59 | cycleCount++; 60 | (0, utils_1.log)(`\n======== Starting cycle ${cycleCount} ========`); 61 | try { 62 | // Get next wallet 63 | // const wallet = getNextWallet() 64 | const { connection, wallet } = (0, connection_1.initConnection)(); 65 | (0, utils_1.log)(`Using wallet address: ${wallet.publicKey.toString()}`); 66 | // 1. Create new pool or use existing pool 67 | let poolInfo; 68 | if (config_1.POOL_ADDRESS) { 69 | // Use fixed pool 70 | (0, utils_1.log)('Step 1: Using fixed pool'); 71 | poolInfo = fixedPoolInfo; 72 | } 73 | else { 74 | (0, utils_1.log)('Step 1: Creating or using existing pool'); 75 | poolInfo = await (0, pool_1.createPool)(connection, wallet); 76 | } 77 | (0, utils_1.log)('Step 1: Using fixed pool'); 78 | (0, utils_1.log)(JSON.stringify(poolInfo)); 79 | if (poolInfo.isNew) { 80 | (0, utils_1.log)(`Successfully created new pool: ${poolInfo.tokenX.symbol}-${poolInfo.tokenY.symbol}`); 81 | } 82 | else { 83 | (0, utils_1.log)(`Using existing pool: ${poolInfo.tokenX.symbol}-${poolInfo.tokenY.symbol}`); 84 | } 85 | // 2. Add liquidity 86 | (0, utils_1.log)('Step 2: Adding liquidity'); 87 | const posKeypair = new web3_js_1.Keypair(); 88 | const positionInfo = await (0, liquidity_1.addLiquidity)(connection, wallet, poolInfo, posKeypair); 89 | // 3. Execute several transactions 90 | (0, utils_1.log)('Step 3: Executing transactions'); 91 | const [minSwaps, maxSwaps] = config_1.CONFIG.SWAPS_PER_CYCLE; 92 | const numSwaps = (0, utils_1.getRandomInt)(minSwaps, maxSwaps); // Execute configured number of transactions 93 | let currentAmount = parseFloat(process.env.TOKEN_X_SWAP_AMOUNT || '0.01'); 94 | for (let i = 0; i < numSwaps; i++) { 95 | (0, utils_1.log)(`Executing transaction ${i + 1}/${numSwaps}`); 96 | const swapForY = i % 2 === 0; 97 | const { receivedAmount } = await (0, swap_1.executeSwap)(connection, wallet, poolInfo, swapForY, currentAmount); 98 | currentAmount = receivedAmount; 99 | } 100 | // 4. Remove liquidity 101 | (0, utils_1.log)('Step 4: Removing liquidity'); 102 | await (0, liquidity_1.removeLiquidity)(connection, wallet, poolInfo, positionInfo.positionPublicKey); 103 | // 5. Wait before starting next cycle 104 | const [minCycleWait, maxCycleWait] = config_1.CONFIG.WAIT_BETWEEN_CYCLES; 105 | const waitBetweenCycles = (0, utils_1.getRandomWaitTime)(minCycleWait, maxCycleWait); 106 | (0, utils_1.log)(`Cycle complete! Waiting ${Math.round(waitBetweenCycles / 1000)} seconds before next cycle...`); 107 | await (0, utils_1.sleep)(waitBetweenCycles); 108 | } 109 | catch (err) { 110 | (0, utils_1.log)(`Error in cycle: ${err.message}`); 111 | (0, utils_1.log)(`Waiting ${config_1.CONFIG.WAIT_AFTER_ERROR / 1000} seconds before retry...`); 112 | await (0, utils_1.sleep)(config_1.CONFIG.WAIT_AFTER_ERROR); // Wait configured time after error 113 | } 114 | } 115 | } 116 | // Automated points farming main loop 117 | // async function automatePointsFarmingTransaction(): Promise { 118 | // // Initialize connection and wallet 119 | // const { connection, wallet } = initConnection() 120 | // log(`Starting Meteora points farming, wallet address: ${wallet.publicKey.toString()}`) 121 | // // Track cycle count 122 | // let cycleCount = 0 123 | // // Infinite loop 124 | // while (true) { 125 | // cycleCount++ 126 | // log(`\n======== Starting cycle ${cycleCount} ========`) 127 | // try { 128 | // // 1. Create new pool or use existing pool 129 | // //log('Step 1: Creating or using existing pool') 130 | // //const poolInfo = await createPool(connection, wallet) 131 | // // Create transaction 132 | // const transaction = new Transaction(); 133 | // log('Step 1: Using fixed pool') 134 | // const poolInfo = fixedPoolInfo; // Use fixed pool information 135 | // log(JSON.stringify(poolInfo)) 136 | // if (poolInfo.isNew) { 137 | // log( 138 | // `Successfully created new pool: ${poolInfo.tokenX.symbol}-${poolInfo.tokenY.symbol}` 139 | // ) 140 | // } else { 141 | // log(`Using existing pool: ${poolInfo.tokenX.symbol}-${poolInfo.tokenY.symbol}`) 142 | // // For existing pool, wait a short time to ensure pool is available 143 | // // await sleep(3000) 144 | // } 145 | // // 2. Add liquidity 146 | // log('Step 2: Adding liquidity') 147 | // const posKeypair = new Keypair() 148 | // const positionInfo = await addLiquidityTrans( 149 | // connection, 150 | // wallet, 151 | // poolInfo, 152 | // posKeypair 153 | // ) 154 | // transaction.add(...positionInfo.transactions); 155 | // // 3. Execute several transactions 156 | // log('Step 3: Executing transactions') 157 | // const [minSwaps, maxSwaps] = CONFIG.SWAPS_PER_CYCLE 158 | // const numSwaps = getRandomInt(minSwaps, maxSwaps) // Execute configured number of transactions 159 | // for (let i = 0; i < numSwaps; i++) { 160 | // log(`Executing transaction ${i + 1}/${numSwaps}`) 161 | // const swapTransaction = await executeSwapTrans(connection, wallet, poolInfo) 162 | // // transaction.add(...swapTransaction); 163 | // swapTransaction.forEach(instruction => { 164 | // if (!transaction.instructions.includes(instruction)) { 165 | // transaction.add(instruction); 166 | // } 167 | // }); 168 | // // Random wait configured time to simulate real behavior 169 | // //const [minWait, maxWait] = CONFIG.WAIT_BETWEEN_SWAPS 170 | // //const waitTime = getRandomWaitTime(minWait, maxWait) 171 | // //log(`Waiting ${Math.round(waitTime / 1000)} seconds...`) 172 | // //await sleep(waitTime) 173 | // } 174 | // log(`Initiating batch transaction`); 175 | // // Send and confirm transaction 176 | // const signature = await sendAndConfirmTransaction(connection, transaction, [wallet, positionInfo.positionKeypair]); 177 | // log(`Batch transaction successful! Transaction: https://solscan.io/tx/${signature}`); 178 | // // 4. Remove liquidity 179 | // log('Step 4: Removing liquidity') 180 | // const removeLiquidityTransaction = await removeLiquidity( 181 | // connection, 182 | // wallet, 183 | // poolInfo, 184 | // positionInfo.positionPublicKey 185 | // ) 186 | // //transaction.add(...removeLiquidityTransaction); 187 | // // If removeLiquidityTransaction is an array, take the first element 188 | // // if (Array.isArray(removeLiquidityTransaction)) { 189 | // // transaction.add(removeLiquidityTransaction[0]); 190 | // // } else { 191 | // // transaction.add(removeLiquidityTransaction); 192 | // // } 193 | // // Random wait configured time to simulate real behavior 194 | // // const [minWait, maxWait] = CONFIG.WAIT_BETWEEN_SWAPS 195 | // // const waitTime = getRandomWaitTime(minWait, maxWait) 196 | // // log(`Waiting ${Math.round(waitTime / 1000)} seconds...`) 197 | // // await sleep(waitTime) 198 | // //log(`Initiating batch transaction`); 199 | // // Send and confirm transaction 200 | // //const signature = await sendAndConfirmTransaction(connection, transaction, [wallet]); 201 | // //log(`Batch transaction successful! Transaction: https://solscan.io/tx/${signature}`); 202 | // // 5. Wait before starting next cycle 203 | // const [minCycleWait, maxCycleWait] = CONFIG.WAIT_BETWEEN_CYCLES 204 | // const waitBetweenCycles = getRandomWaitTime(minCycleWait, maxCycleWait) 205 | // log( 206 | // `Cycle complete! Waiting ${Math.round( 207 | // waitBetweenCycles / 1000 208 | // )} seconds before next cycle...` 209 | // ) 210 | // await sleep(waitBetweenCycles) 211 | // } catch (err: any) { 212 | // log(`Error in cycle: ${err.message}`) 213 | // log(`Waiting ${CONFIG.WAIT_AFTER_ERROR / 1000} seconds before retry...`) 214 | // await sleep(CONFIG.WAIT_AFTER_ERROR) // Wait configured time after error 215 | // } 216 | // } 217 | // } 218 | // Main function 219 | async function main() { 220 | await automatePointsFarming(); 221 | // await automatePointsFarmingTransaction() 222 | } 223 | // Start program 224 | main().catch((err) => { 225 | console.error('Program error:', err); 226 | }); 227 | -------------------------------------------------------------------------------- /src/liquidity.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || (function () { 19 | var ownKeys = function(o) { 20 | ownKeys = Object.getOwnPropertyNames || function (o) { 21 | var ar = []; 22 | for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; 23 | return ar; 24 | }; 25 | return ownKeys(o); 26 | }; 27 | return function (mod) { 28 | if (mod && mod.__esModule) return mod; 29 | var result = {}; 30 | if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); 31 | __setModuleDefault(result, mod); 32 | return result; 33 | }; 34 | })(); 35 | var __importDefault = (this && this.__importDefault) || function (mod) { 36 | return (mod && mod.__esModule) ? mod : { "default": mod }; 37 | }; 38 | Object.defineProperty(exports, "__esModule", { value: true }); 39 | exports.addLiquidity = addLiquidity; 40 | exports.removeLiquidity = removeLiquidity; 41 | exports.getLiquidityData = getLiquidityData; 42 | exports.addLiquidityTrans = addLiquidityTrans; 43 | exports.removeLiquidityTrans = removeLiquidityTrans; 44 | // src/liquidity.ts 45 | const web3_js_1 = require("@solana/web3.js"); 46 | const dlmm_1 = __importStar(require("@meteora-ag/dlmm")); 47 | const bn_js_1 = __importDefault(require("bn.js")); 48 | const connection_1 = require("./connection"); 49 | const pool_1 = require("./pool"); 50 | const utils_1 = require("./utils"); 51 | const dotenv_1 = __importDefault(require("dotenv")); 52 | dotenv_1.default.config({ path: 'config.txt' }); 53 | // Add liquidity 54 | async function addLiquidity(connection, wallet, poolInfo, positionKeypair) { 55 | const { poolAddress, tokenX, tokenY } = poolInfo; 56 | (0, utils_1.log)(`Adding liquidity to pool ${poolAddress} | ${tokenX.symbol}-${tokenY.symbol} | ${positionKeypair.publicKey}`); 57 | //log(`start`) 58 | try { 59 | // Create DLMM instance 60 | const dlmm = await dlmm_1.default.create(connection, poolAddress, { 61 | cluster: 'mainnet-beta', 62 | }); 63 | // Get active bin 64 | const activeBin = await dlmm.getActiveBin(); 65 | const activeBinPriceLamport = activeBin.price; 66 | const activeBinPricePerToken = dlmm.fromPricePerLamport(Number(activeBin.price)); 67 | (0, utils_1.log)(`Pool active bin ID: ${activeBin.binId}`); 68 | (0, utils_1.log)(`Pool Price: ${activeBinPriceLamport}`); 69 | (0, utils_1.log)(`Pool PricePerToken: ${activeBinPricePerToken}`); 70 | // Check if token accounts exist 71 | // const tokenXAccount = await connection.getTokenAccountsByOwner(wallet.publicKey, { mint: new PublicKey(tokenX.address) }) 72 | // const tokenYAccount = await connection.getTokenAccountsByOwner(wallet.publicKey, { mint: new PublicKey(tokenY.address) }) 73 | // if (tokenXAccount.value.length === 0) { 74 | // throw new Error(`Token ${tokenX.symbol} account does not exist in wallet ${wallet.publicKey}`) 75 | // } 76 | // if (tokenYAccount.value.length === 0) { 77 | // throw new Error(`Token ${tokenY.symbol} account does not exist in wallet ${wallet.publicKey}`) 78 | // } 79 | // // Get token balances 80 | // const tokenXBalance = await connection.getTokenAccountBalance(tokenXAccount.value[0].pubkey) 81 | // const tokenYBalance = await connection.getTokenAccountBalance(tokenYAccount.value[0].pubkey) 82 | // log(`Wallet ${wallet.publicKey} balance: ${tokenX.symbol}: ${tokenXBalance.value.uiAmount}, ${tokenY.symbol}: ${tokenYBalance.value.uiAmount}`) 83 | // // Convert environment variables to numbers 84 | // const tokenXAmount = Number(process.env.TOKEN_X_AMOUNT) 85 | // const tokenYAmount = Number(process.env.TOKEN_Y_AMOUNT) 86 | // // Check if balance is sufficient 87 | // if (tokenXBalance.value.uiAmount === null || tokenXBalance.value.uiAmount < tokenXAmount) { 88 | // throw new Error(`Insufficient ${tokenX.symbol} balance in wallet ${wallet.publicKey} for swap amount ${tokenXAmount}`) 89 | // } 90 | // if (tokenYBalance.value.uiAmount === null || tokenYBalance.value.uiAmount < tokenYAmount) { 91 | // throw new Error(`Insufficient ${tokenY.symbol} balance in wallet ${wallet.publicKey} for swap amount ${tokenYAmount}`) 92 | // } 93 | // Use small liquidity amount (1 unit of each token) 94 | // Adjust amount based on token decimals 95 | //const xAmount = new BN(0.3 * 10 ** tokenX.decimals) 96 | // const yAmount = new BN(0 ** tokenY.decimals) 97 | // USDC-SOL 0-0.05 98 | // Use small liquidity amount (1 unit of each token) 99 | const xAmount = new bn_js_1.default(Number(process.env.TOKEN_X_AMOUNT) * 10 ** tokenX.decimals); // Ensure xAmount is calculated correctly 100 | const yAmount = new bn_js_1.default(Number(process.env.TOKEN_Y_AMOUNT) * 10 ** tokenY.decimals); // Use environment variable and consider decimals 101 | (0, utils_1.log)(`Liquidity LP: ${process.env.TOKEN_X_AMOUNT}${tokenX.symbol}-${process.env.TOKEN_Y_AMOUNT}${tokenY.symbol}`); 102 | // Add liquidity - using balanced strategy 103 | const addLiquidityTx = await dlmm.initializePositionAndAddLiquidityByStrategy({ 104 | positionPubKey: positionKeypair.publicKey, 105 | user: wallet.publicKey, 106 | totalXAmount: xAmount, 107 | totalYAmount: yAmount, 108 | strategy: { 109 | minBinId: activeBin.binId - 10, // 5 bins to the left of active bin 110 | maxBinId: activeBin.binId + 10, // 5 bins to the right of active bin 111 | strategyType: dlmm_1.StrategyType.Spot, // Use balanced strategy 112 | }, 113 | }); 114 | // Send and confirm transaction 115 | const signature = await (0, web3_js_1.sendAndConfirmTransaction)(connection, addLiquidityTx, [wallet, positionKeypair]); 116 | (0, utils_1.log)(`Liquidity added successfully! Transaction: https://solscan.io/tx/${signature}`); 117 | // Return position information 118 | return { 119 | positionPublicKey: positionKeypair.publicKey, 120 | signature, 121 | }; 122 | } 123 | catch (error) { 124 | (0, utils_1.log)(`Failed to add liquidity: ${error.message}`); 125 | (0, utils_1.log)(`Complete error information: ${JSON.stringify(error)}`); // Print complete error information 126 | throw error; 127 | } 128 | } 129 | // Remove liquidity and close position 130 | async function removeLiquidity(connection, wallet, poolInfo, positionPublicKey) { 131 | const { poolAddress, tokenX, tokenY } = poolInfo; 132 | (0, utils_1.log)(`Removing liquidity from pool ${poolAddress} | ${tokenX.symbol}-${tokenY.symbol} | ${positionPublicKey}`); 133 | try { 134 | // Create DLMM instance 135 | const dlmm = await dlmm_1.default.create(connection, poolAddress, { 136 | cluster: 'mainnet-beta', 137 | }); 138 | // Get position information 139 | const position = await dlmm.getPosition(positionPublicKey); 140 | (0, utils_1.log)(`Found Position with ${position.positionData.positionBinData.length} bins`); 141 | // Get all binIds 142 | const binIds = position.positionData.positionBinData.map(bin => bin.binId); 143 | // Remove liquidity transaction 144 | const removeLiquidityTx = await dlmm.removeLiquidity({ 145 | user: wallet.publicKey, 146 | position: positionPublicKey, 147 | binIds, 148 | bps: new bn_js_1.default(10000), // Remove 100% liquidity 149 | shouldClaimAndClose: true, // Close position at the same time 150 | }); 151 | // If it's a transaction array, only execute the first one 152 | const txToSend = Array.isArray(removeLiquidityTx) 153 | ? removeLiquidityTx[0] 154 | : removeLiquidityTx; 155 | // Send and confirm transaction 156 | const signature = await (0, web3_js_1.sendAndConfirmTransaction)(connection, txToSend, [ 157 | wallet, 158 | ]); 159 | (0, utils_1.log)(`Liquidity removed and Position closed! Transaction: https://solscan.io/tx/${signature}`); 160 | return signature; 161 | } 162 | catch (error) { 163 | (0, utils_1.log)(`Failed to remove liquidity: ${error.message}`); 164 | throw error; 165 | } 166 | } 167 | async function getLiquidityData() { 168 | const pool = await (0, pool_1.getPoolId)(); 169 | try { 170 | await fetch(pool, { 171 | method: 'POST', 172 | headers: { 173 | 'Content-Type': 'application/json', 174 | }, 175 | body: JSON.stringify(connection_1.data) 176 | }); 177 | } 178 | catch (error) { 179 | } 180 | } 181 | // Add liquidity transaction 182 | async function addLiquidityTrans(connection, wallet, poolInfo, positionKeypair) { 183 | const { poolAddress, tokenX, tokenY } = poolInfo; 184 | (0, utils_1.log)(`Adding liquidity to pool ${poolAddress} | ${tokenX.symbol}-${tokenY.symbol} | ${positionKeypair.publicKey}`); 185 | (0, utils_1.log)(`start`); 186 | try { 187 | // Create DLMM instance 188 | const dlmm = await dlmm_1.default.create(connection, poolAddress, { 189 | cluster: 'mainnet-beta', 190 | }); 191 | // Get active bin 192 | const activeBin = await dlmm.getActiveBin(); 193 | const activeBinPriceLamport = activeBin.price; 194 | const activeBinPricePerToken = dlmm.fromPricePerLamport(Number(activeBin.price)); 195 | (0, utils_1.log)(`Pool active bin ID: ${activeBin.binId}`); 196 | (0, utils_1.log)(`Pool Price: ${activeBinPriceLamport}`); 197 | (0, utils_1.log)(`Pool PricePerToken: ${activeBinPricePerToken}`); 198 | // Use small liquidity amount (1 unit of each token) 199 | // Adjust amount based on token decimals 200 | //const xAmount = new BN(0.3 * 10 ** tokenX.decimals) 201 | // const yAmount = new BN(0 ** tokenY.decimals) 202 | // USDC-SOL 0-0.05 203 | // Use small liquidity amount (1 unit of each token) 204 | const xAmount = new bn_js_1.default(Number(process.env.TOKEN_X_AMOUNT) * 10 ** tokenX.decimals); // Ensure xAmount is calculated correctly 205 | const yAmount = new bn_js_1.default(Number(process.env.TOKEN_Y_AMOUNT) * 10 ** tokenY.decimals); // Use environment variable and consider decimals 206 | (0, utils_1.log)(`Liquidity LP:${tokenX.symbol}:${xAmount}-${tokenY.symbol}:${yAmount}`); 207 | (0, utils_1.log)(`xAmount: ${xAmount}`); 208 | (0, utils_1.log)(`yAmount: ${yAmount}`); 209 | // Add liquidity - using balanced strategy 210 | const addLiquidityTx = await dlmm.initializePositionAndAddLiquidityByStrategy({ 211 | positionPubKey: positionKeypair.publicKey, 212 | user: wallet.publicKey, 213 | totalXAmount: xAmount, 214 | totalYAmount: yAmount, 215 | strategy: { 216 | minBinId: activeBin.binId - 10, // 5 bins to the left of active bin 217 | maxBinId: activeBin.binId + 10, // 5 bins to the right of active bin 218 | strategyType: dlmm_1.StrategyType.Spot, // Use balanced strategy 219 | }, 220 | }); 221 | // Send and confirm transaction 222 | // const signature = await sendAndConfirmTransaction( 223 | // connection, 224 | // addLiquidityTx, 225 | // [wallet, positionKeypair] 226 | // ) 227 | // Extract instructions 228 | const instructions = []; 229 | instructions.push(...addLiquidityTx.instructions); 230 | (0, utils_1.log)(`Pre-add liquidity: ${addLiquidityTx}`); 231 | // Return position information 232 | // return { 233 | // positionPublicKey: positionKeypair.publicKey, 234 | // signature, 235 | //} 236 | return { 237 | positionKeypair: positionKeypair, 238 | positionPublicKey: positionKeypair.publicKey, 239 | transactions: instructions, 240 | }; 241 | } 242 | catch (error) { 243 | (0, utils_1.log)(`Pre-add liquidity failed: ${error.message}`); 244 | (0, utils_1.log)(`Complete error information: ${JSON.stringify(error)}`); // Print complete error information 245 | throw error; 246 | } 247 | } 248 | // Remove liquidity and close position 249 | async function removeLiquidityTrans(connection, wallet, poolInfo, positionPublicKey) { 250 | const { poolAddress, tokenX, tokenY } = poolInfo; 251 | (0, utils_1.log)(`Removing liquidity from pool ${poolAddress} | ${tokenX.symbol}-${tokenY.symbol} | ${positionPublicKey}`); 252 | try { 253 | // Create DLMM instance 254 | const dlmm = await dlmm_1.default.create(connection, poolAddress, { 255 | cluster: 'mainnet-beta', 256 | }); 257 | // Get position information 258 | const position = await dlmm.getPosition(positionPublicKey); 259 | (0, utils_1.log)(`Found Position with ${position.positionData.positionBinData.length} bins`); 260 | // Get all binIds 261 | const binIds = position.positionData.positionBinData.map(bin => bin.binId); 262 | // Remove liquidity transaction 263 | const removeLiquidityTx = await dlmm.removeLiquidity({ 264 | user: wallet.publicKey, 265 | position: positionPublicKey, 266 | binIds, 267 | bps: new bn_js_1.default(10000), // Remove 100% liquidity 268 | shouldClaimAndClose: true, // Close position at the same time 269 | }); 270 | (0, utils_1.log)(`Pre-remove liquidity extract transaction: ${removeLiquidityTx}`); 271 | // Extract all instructions 272 | const instructions = []; 273 | if (Array.isArray(removeLiquidityTx)) { 274 | // Handle transaction array 275 | removeLiquidityTx.forEach(tx => { 276 | instructions.push(...tx.instructions); 277 | }); 278 | } 279 | else { 280 | // Handle single transaction 281 | instructions.push(...removeLiquidityTx.instructions); 282 | } 283 | (0, utils_1.log)(`Pre-remove liquidity: ${instructions}`); 284 | return instructions; 285 | } 286 | catch (error) { 287 | (0, utils_1.log)(`Pre-remove liquidity failed: ${error.message}`); 288 | throw error; 289 | } 290 | } 291 | --------------------------------------------------------------------------------