├── wallets ├── lutAddress.txt └── wallets.json ├── .gitignore ├── src ├── images │ └── 1.jpg ├── logger.ts ├── structs.ts ├── legacy.ts ├── jitoWithAxios.ts ├── budget.ts ├── burnLp.ts ├── types.ts ├── execute.ts ├── createPoolAndBundleBuy.ts ├── extendLutSimulate.ts ├── getMarketAccountSizes.ts ├── sell.ts ├── get_balance.ts ├── revokeMintAuthority.ts ├── revokeFreezeAuthority.ts ├── removeLiquidity.ts ├── build_a_sendtxn.ts ├── sendBulkToken.ts ├── getPoolKeys.ts ├── createTokenPinata.ts ├── poolAll.ts ├── createMarket.ts ├── utils.ts └── swapOnlyAmm.ts ├── .env.example ├── layout ├── poolBuy.ts ├── simulation.ts ├── createMarket.ts ├── createToken.ts ├── manualSell.ts ├── manualAllSell.ts ├── removeLiquidity.ts ├── rebuy.ts ├── solGather.ts ├── walletCreate.ts └── createLutAta.ts ├── tsconfig.json ├── package.json ├── menu └── menu.ts ├── README.md ├── burnWalletToken.ts ├── settings.ts └── index.ts /wallets/lutAddress.txt: -------------------------------------------------------------------------------- 1 | 6RbedbePGcUCPazG4rYhsquhNf4fWNU8Qf54UaymKcbw -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | config.ts 3 | 4 | node_modules/ 5 | node_modules/* 6 | 7 | data.json -------------------------------------------------------------------------------- /src/images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeudev/solana-raydium-bundler/HEAD/src/images/1.jpg -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | CLUSTER=mainnet 2 | MAINNET_RPC_URL= 3 | MAINNET_WEBSOCKET_URL= 4 | DEVNET_RPC_URL= 5 | PINATA_API_KEY=ec5b5284edda9aba9e93 6 | PINATA_SECRET_API_KEY=5901d6e2827c770cb3265512ff5ddd79d0fa54fb27e086d8768e38104ef2279b 7 | # POOL_EACH_INTERVAL=60 # Time interval with seconds unit between new pools created from tokens 8 | 9 | # recovery mode 10 | RECOVERY_MODE=false 11 | 12 | # monitoring mode 13 | # SOL_GATHERED=2 # SOL 14 | # POOL_MAX_LIVE_TIME=60 # seconds 15 | 16 | # jito 17 | JITO_KEY=66xqL9aFZJ8k9YpjNBexNASfuoDgNE1ZpGRXB28zoTfS4u2czzVBhMNMqgZYFeMN8FnUi6gMzXWgVYRHkTZ6yuLC 18 | BLOCKENGINE_URL=tokyo.mainnet.block-engine.jito.wtf 19 | JITO_FEE=0.005 -------------------------------------------------------------------------------- /layout/poolBuy.ts: -------------------------------------------------------------------------------- 1 | import { mainMenuWaiting, sleep } from "../src/utils" 2 | import { getWalletTokenAccount } from "../src/get_balance"; 3 | import { txCreateNewPoolAndBundleBuy } from "../src/createPoolAndBundleBuy"; 4 | 5 | type WalletTokenAccounts = Awaited> 6 | 7 | const execute = async () => { 8 | try { 9 | txCreateNewPoolAndBundleBuy() 10 | } catch (error) { 11 | console.log("Error happened in one of the token flow", error) 12 | } 13 | } 14 | 15 | export const bundle_pool_buy = async () => { 16 | console.log("Creating Pool and Bundle Buy Process Started...") 17 | await execute() 18 | await sleep(45000) 19 | mainMenuWaiting() 20 | } 21 | -------------------------------------------------------------------------------- /layout/simulation.ts: -------------------------------------------------------------------------------- 1 | import { mainMenuWaiting, sleep } from "../src/utils" 2 | import { getWalletTokenAccount } from "../src/get_balance"; 3 | import { extendLutSimulate } from "../src/extendLutSimulate"; 4 | 5 | type WalletTokenAccounts = Awaited> 6 | 7 | const execute = async () => { 8 | try { 9 | extendLutSimulate() 10 | } catch (error) { 11 | console.log("Error happened in one of the token flow", error) 12 | } 13 | } 14 | 15 | export const simulate = async () => { 16 | console.log("Creating Pool and Bundle Buy Simulation and Extending Lookuptable Process Started...") 17 | await execute() 18 | await sleep(10000) 19 | mainMenuWaiting() 20 | } 21 | -------------------------------------------------------------------------------- /src/logger.ts: -------------------------------------------------------------------------------- 1 | import { createLogger, transports, format } from 'winston'; 2 | 3 | export const logger = createLogger({ 4 | transports: [new transports.Console()], 5 | format: format.combine( 6 | format.colorize(), 7 | format.timestamp(), 8 | format.printf(({ timestamp, level, message, meta }) => { 9 | let metaStr = ''; 10 | if (meta) { 11 | metaStr = typeof meta === 'object' ? JSON.stringify(meta) : String(meta); 12 | } 13 | return `[${timestamp}] ${level}: ${message} ${metaStr}`; 14 | }) 15 | ), 16 | }); 17 | 18 | export const setLogLevel = (logLevel: string) => { 19 | logger.level = logLevel; 20 | }; 21 | 22 | 23 | export function exitProcess(exitCode: number): void { 24 | process.exit(exitCode); 25 | } 26 | -------------------------------------------------------------------------------- /src/structs.ts: -------------------------------------------------------------------------------- 1 | import { u8, u32, struct } from '@solana/buffer-layout'; 2 | import { u64, publicKey } from '@solana/buffer-layout-utils'; 3 | 4 | export const SPL_MINT_LAYOUT = struct([ 5 | u32('mintAuthorityOption'), 6 | publicKey('mintAuthority'), 7 | u64('supply'), 8 | u8('decimals'), 9 | u8('isInitialized'), 10 | u32('freezeAuthorityOption'), 11 | publicKey('freezeAuthority') 12 | ]); 13 | 14 | export const SPL_ACCOUNT_LAYOUT = struct([ 15 | publicKey('mint'), 16 | publicKey('owner'), 17 | u64('amount'), 18 | u32('delegateOption'), 19 | publicKey('delegate'), 20 | u8('state'), 21 | u32('isNativeOption'), 22 | u64('isNative'), 23 | u64('delegatedAmount'), 24 | u32('closeAuthorityOption'), 25 | publicKey('closeAuthority') 26 | ]); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 4 | "module": "commonjs", /* Specify what module code is generated. */ 5 | "resolveJsonModule": true, /* Enable importing .json files. */ 6 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 7 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 8 | "strict": true, /* Enable all strict type-checking options. */ 9 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.ts", 6 | "scripts": { 7 | "start": "ts-node index.ts", 8 | "lut": "ts-node lutTransferTest.ts" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@metaplex-foundation/js": "^0.19.4", 15 | "@metaplex-foundation/mpl-token-metadata": "^2.9.1", 16 | "@openbook-dex/openbook": "^0.0.9", 17 | "@project-serum/serum": "^0.13.65", 18 | "@raydium-io/raydium-sdk": "^1.3.1-beta.48", 19 | "@solana/spl-token": "^0.4.7", 20 | "@solana/web3.js": "^1.94.0", 21 | "axios": "^1.7.2", 22 | "bn.js": "^5.2.1", 23 | "bs58": "^6.0.0", 24 | "dotenv": "^16.4.1", 25 | "jito-ts": "^4.1.0", 26 | "typescript": "^5.5.3", 27 | "winston": "^3.11.0" 28 | }, 29 | "devDependencies": { 30 | "@types/bn.js": "^5.1.5", 31 | "prettier": "^3.2.4", 32 | "ts-node": "^10.9.2", 33 | "tsconfig-paths": "^4.2.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/legacy.ts: -------------------------------------------------------------------------------- 1 | import { VersionedTransaction } from "@solana/web3.js"; 2 | import { connection } from "../config"; 3 | 4 | 5 | interface Blockhash { 6 | blockhash: string; 7 | lastValidBlockHeight: number; 8 | } 9 | 10 | export const execute = async (transaction: VersionedTransaction, latestBlockhash: Blockhash, isBuy: boolean | 1 = true) => { 11 | 12 | const signature = await connection.sendRawTransaction(transaction.serialize(), { skipPreflight: true }) 13 | const confirmation = await connection.confirmTransaction( 14 | { 15 | signature, 16 | lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, 17 | blockhash: latestBlockhash.blockhash, 18 | } 19 | ); 20 | 21 | if (confirmation.value.err) { 22 | console.log("Confirmation error") 23 | return "" 24 | } else { 25 | if(isBuy === 1){ 26 | return signature 27 | } else if (isBuy) 28 | console.log(`Success in buy transaction: https://solscan.io/tx/${signature}`) 29 | else 30 | console.log(`Success in Sell transaction: https://solscan.io/tx/${signature}`) 31 | } 32 | return signature 33 | } 34 | -------------------------------------------------------------------------------- /menu/menu.ts: -------------------------------------------------------------------------------- 1 | import readline from "readline" 2 | import fs from 'fs' 3 | import { sleep } from "../src/utils"; 4 | 5 | export const rl = readline.createInterface({ 6 | input: process.stdin, 7 | output: process.stdout 8 | }) 9 | 10 | export const screen_clear = () => { 11 | console.clear(); 12 | } 13 | 14 | export const main_menu_display = () => { 15 | console.log('\t[1] - Create Token'); 16 | console.log('\t[2] - Create Market'); 17 | console.log('\t[3] - Security Checks'); 18 | console.log('\t[4] - Create Wallets to BundleBuy'); 19 | console.log('\t[5] - Create LookUpTable'); 20 | console.log('\t[6] - Extend LookUpTable and Simulate'); 21 | console.log('\t[7] - Create Pool And BundleBuy'); 22 | console.log('\t[8] - Sell all tokens'); 23 | console.log('\t[9] - Gather Sol from bundler wallets'); 24 | console.log('\t[10] - Exit'); 25 | } 26 | 27 | export const security_checks_display = () => { 28 | console.log('\t[1] - Remove Mint Authority'); 29 | console.log('\t[2] - Remove Freeze Authority'); 30 | console.log('\t[3] - Burn LP Token'); 31 | console.log('\t[4] - Back'); 32 | console.log('\t[5] - Exit'); 33 | } -------------------------------------------------------------------------------- /layout/createMarket.ts: -------------------------------------------------------------------------------- 1 | import { Keypair, PublicKey } from "@solana/web3.js" 2 | import base58 from "bs58" 3 | 4 | import { tokens } from "../settings" 5 | import { createMarket } from "../src/createMarket" 6 | import { mainMenuWaiting, outputBalance, readJson, retrieveEnvVariable, saveDataToFile, sleep } from "../src/utils" 7 | import { PoolInfo, UserToken } from '../src/types' 8 | import { 9 | getWalletTokenAccount, 10 | } from "../src/get_balance"; 11 | import { init } from ".." 12 | 13 | type WalletTokenAccounts = Awaited> 14 | 15 | const recoveryMode = retrieveEnvVariable("RECOVERY_MODE") == "true" 16 | 17 | const execute = async (token: UserToken) => { 18 | let params: PoolInfo 19 | try { 20 | // contact with savant-cat 21 | } catch (error) { 22 | console.log("Error happened in one of the token flow", error) 23 | await sleep(3000) 24 | } 25 | } 26 | 27 | export const create_market = async () => { 28 | for (let i = 0; i < tokens.length; i++) { 29 | const token = tokens[i] 30 | console.log(`Market ${i + 1} is to be created`) 31 | await execute(token) 32 | console.log("One Market process is ended, and go for next one") 33 | mainMenuWaiting() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/jitoWithAxios.ts: -------------------------------------------------------------------------------- 1 | import { Keypair, LAMPORTS_PER_SOL, PublicKey, SystemProgram, TransactionMessage, VersionedTransaction } from "@solana/web3.js"; 2 | import base58 from "bs58"; 3 | import axios, { AxiosError } from "axios"; 4 | import { JITO_FEE, connection } from "../config"; 5 | 6 | interface Blockhash { 7 | blockhash: string; 8 | lastValidBlockHeight: number; 9 | } 10 | 11 | 12 | export const jitoWithAxios = async (transactions: VersionedTransaction[], payer: Keypair) => { 13 | 14 | console.log('Starting Jito transaction execution...'); 15 | const tipAccounts = [ 16 | 'Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY', 17 | 'DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL', 18 | '96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5', 19 | '3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT', 20 | 'HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe', 21 | 'ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49', 22 | 'ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt', 23 | 'DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh', 24 | ]; 25 | const jitoFeeWallet = new PublicKey(tipAccounts[Math.floor(tipAccounts.length * Math.random())]) 26 | 27 | console.log(`Selected Jito fee wallet: ${jitoFeeWallet.toBase58()}`); 28 | 29 | try { 30 | // Contact with savant-cat 31 | } 32 | } 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /layout/createToken.ts: -------------------------------------------------------------------------------- 1 | import { Keypair } from "@solana/web3.js" 2 | import base58 from "bs58" 3 | 4 | import { LP_wallet_private_key, tokens } from "../settings" 5 | // import { LP_wallet_private_key } from "../settings" 6 | import { createTokenWithMetadata } from "../src/createTokenPinata" 7 | import { mainMenuWaiting, outputBalance, readJson, retrieveEnvVariable, saveDataToFile, sleep } from "../src/utils" 8 | import { PoolInfo, UserToken } from '../src/types' 9 | import { 10 | getWalletTokenAccount, 11 | } from "../src/get_balance"; 12 | import { init } from ".." 13 | 14 | type WalletTokenAccounts = Awaited> 15 | 16 | const recoveryMode = retrieveEnvVariable("RECOVERY_MODE") == "true" 17 | 18 | const data = readJson() 19 | 20 | const execute = async (token: UserToken) => { 21 | let params: PoolInfo 22 | try { 23 | // Contact with savant-cat 24 | 25 | } catch (error) { 26 | console.log("Error happened in one of the token flow", error) 27 | } 28 | } 29 | 30 | export const create_token = async () => { 31 | for (let i = 0; i < tokens.length; i++) { 32 | const token = tokens[i] 33 | console.log(`Token is to be created`) 34 | await execute(token) 35 | await sleep(5000) 36 | console.log("One token creating process is ended, and go for next step") 37 | mainMenuWaiting() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /layout/manualSell.ts: -------------------------------------------------------------------------------- 1 | import { connection } from "../config" 2 | import base58 from "bs58" 3 | import readline from "readline" 4 | import { 5 | ComputeBudgetProgram, 6 | Keypair, 7 | PublicKey, 8 | SystemProgram, 9 | Transaction, 10 | TransactionInstruction, 11 | sendAndConfirmTransaction 12 | } from "@solana/web3.js" 13 | import { 14 | LiquidityPoolKeysV4, 15 | SPL_ACCOUNT_LAYOUT, 16 | TokenAccount 17 | } from "@raydium-io/raydium-sdk"; 18 | import { 19 | NATIVE_MINT, 20 | TOKEN_PROGRAM_ID, 21 | createAssociatedTokenAccountIdempotentInstruction, 22 | createCloseAccountInstruction, 23 | createTransferCheckedInstruction, 24 | getAssociatedTokenAddress, 25 | getAssociatedTokenAddressSync 26 | } from "@solana/spl-token"; 27 | import { LP_wallet_keypair } from "../settings"; 28 | import { getSellTx } from "../src/swapOnlyAmm"; 29 | import { mainMenuWaiting, readJson, sleep } from "../src/utils"; 30 | import { execute } from "../src/legacy"; 31 | import { rl } from "../menu/menu"; 32 | 33 | // let rl = readline.createInterface({ 34 | // input: process.stdin, 35 | // output: process.stdout 36 | // }) 37 | 38 | const mainKp = LP_wallet_keypair 39 | const mainPk = mainKp.publicKey 40 | const data = readJson() 41 | 42 | export const manualSell = async () => { 43 | try { 44 | // Contact with savant-cat 45 | } 46 | catch (error) { 47 | console.log(error); 48 | } 49 | } -------------------------------------------------------------------------------- /layout/manualAllSell.ts: -------------------------------------------------------------------------------- 1 | import { AddressLookupTableProgram, ComputeBudgetProgram, Keypair, PublicKey, sendAndConfirmRawTransaction, Transaction, TransactionInstruction, TransactionMessage, VersionedTransaction } from "@solana/web3.js" 2 | import { 3 | DEVNET_PROGRAM_ID, 4 | jsonInfo2PoolKeys, 5 | Liquidity, 6 | MAINNET_PROGRAM_ID, 7 | MARKET_STATE_LAYOUT_V3, LiquidityPoolKeys, 8 | } from "@raydium-io/raydium-sdk" 9 | import { getAssociatedTokenAddress, getAssociatedTokenAddressSync, getMint, NATIVE_MINT, unpackMint } from "@solana/spl-token"; 10 | import bs58 from "bs58" 11 | import BN from "bn.js" 12 | 13 | import { mainMenuWaiting, outputBalance, readBundlerWallets, readJson, readLUTAddressFromFile, readWallets, retrieveEnvVariable, saveDataToFile, sleep } from "../src/utils" 14 | import { 15 | getTokenAccountBalance, 16 | assert, 17 | getWalletTokenAccount, 18 | } from "../src/get_balance"; 19 | import { 20 | connection, 21 | cluster, 22 | } from "../config"; 23 | import { 24 | quote_Mint_amount, 25 | input_baseMint_tokens_percentage, 26 | bundlerWalletName, 27 | batchSize 28 | } from "../settings" 29 | 30 | import { executeVersionedTx } from "../src/execute"; 31 | import { jitoWithAxios } from "../src/jitoWithAxios"; 32 | // import { createLookupTable } from "../layout/createLUT"; 33 | 34 | const programId = cluster == "devnet" ? DEVNET_PROGRAM_ID : MAINNET_PROGRAM_ID 35 | 36 | export async function manual_all_sell() { 37 | // Contact with savant-cat 38 | } -------------------------------------------------------------------------------- /src/budget.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { ComputeBudgetConfig } from '@raydium-io/raydium-sdk'; 3 | import { sell_remove_fees } from '../config'; 4 | 5 | 6 | interface SolanaFeeInfo { 7 | min: number; 8 | max: number; 9 | avg: number; 10 | priorityTx: number; 11 | nonVotes: number; 12 | priorityRatio: number; 13 | avgCuPerBlock: number; 14 | blockspaceUsageRatio: number; 15 | } 16 | type SolanaFeeInfoJson = { 17 | '1': SolanaFeeInfo; 18 | '5': SolanaFeeInfo; 19 | '15': SolanaFeeInfo; 20 | }; 21 | 22 | export async function getComputeBudgetConfig(): Promise { 23 | 24 | const response = await axios.get('https://solanacompass.com/api/fees'); 25 | const json = response.data; 26 | const { avg } = json?.[15] ?? {}; 27 | if (!avg) return undefined; // fetch error 28 | return { 29 | units: 600_000, 30 | microLamports: Math.min(Math.ceil((avg * 1000000) / 600000), 25000), 31 | } as ComputeBudgetConfig; 32 | } 33 | 34 | 35 | export async function getComputeBudgetConfigHigh(): Promise { 36 | 37 | const response = await axios.get('https://solanacompass.com/api/fees'); 38 | const json = response.data; 39 | const { avg } = json?.[15] ?? {}; 40 | if (!avg) return undefined; // fetch error 41 | return { 42 | units: sell_remove_fees, 43 | microLamports: Math.min(Math.ceil((avg * 1000000) / 600000), 25000), 44 | } as ComputeBudgetConfig; 45 | } -------------------------------------------------------------------------------- /src/burnLp.ts: -------------------------------------------------------------------------------- 1 | import { Keypair, PublicKey, TransactionMessage, VersionedTransaction } from "@solana/web3.js" 2 | import { createBurnCheckedInstruction, getAssociatedTokenAddress, unpackMint } from "@solana/spl-token"; 3 | 4 | import { cluster } from "../config" 5 | import { tokens } from "../settings" 6 | import { burnLpQuantityPercent } from "../settings" 7 | import { readJson, securityCheckWaiting, sleep } from "./utils" 8 | import { PoolInfo, UserToken } from './types' 9 | import { getWalletTokenAccount } from "./get_balance"; 10 | import { connection } from "../config"; 11 | 12 | import bs58 from 'bs58' 13 | import { security_checks } from ".."; 14 | 15 | type WalletTokenAccounts = Awaited> 16 | 17 | const execute = async (token: UserToken) => { 18 | let params: PoolInfo 19 | try { 20 | // Contact with savant-cat 21 | if (confirmation.value.err) { throw new Error(" ❌ - Transaction not confirmed.") } 22 | console.log('🔥 SUCCESSFUL BURN!🔥', '\n', `https://explorer.solana.com/tx/${txid}${cluster == "devnet" ? "?cluster=devnet" : ""}`); 23 | 24 | } catch (error) { 25 | console.log("Error happened in one of the token flow", error) 26 | } 27 | } 28 | 29 | export const burn_lp = async () => { 30 | for (let i = 0; i < tokens.length; i++) { 31 | const token = tokens[i] 32 | console.log(`Token ${i + 1} is to be burnt`) 33 | await execute(token) 34 | console.log("One token process is ended, and go for next one") 35 | await sleep(5000) 36 | securityCheckWaiting() 37 | } 38 | } 39 | 40 | // burn_lp() 41 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { LiquidityPoolKeysV4 } from "@raydium-io/raydium-sdk" 2 | import { Keypair, PublicKey } from "@solana/web3.js" 3 | import { web3 } from "@project-serum/anchor" 4 | import { RawMint } from "@solana/spl-token" 5 | 6 | 7 | export interface UserToken { 8 | name: string 9 | symbol: string 10 | decimals: number 11 | description: string 12 | uiAmount: number 13 | image: string 14 | extensions?: Extension 15 | tags?: string[] 16 | creator?: Creator 17 | } 18 | 19 | interface Extension { 20 | website?: string 21 | twitter?: string 22 | telegram?: string 23 | } 24 | 25 | export interface Metadata { 26 | name: string 27 | symbol: string 28 | description: string 29 | image: string 30 | extensions?: Extension 31 | tags?: string[] 32 | creator?: Creator 33 | } 34 | 35 | export interface Creator { 36 | name: string 37 | site: string 38 | } 39 | 40 | export interface PoolInfo { 41 | mint: null | PublicKey 42 | marketId: null | PublicKey 43 | poolId: null | PublicKey 44 | mainKp: null | string 45 | poolKeys: null | LiquidityPoolKeysV4 46 | removed: null | boolean 47 | } 48 | 49 | export interface PoolInfoStr { 50 | mint: null | string 51 | marketId: null | string 52 | poolId: null | string 53 | mainKp: null | string 54 | poolKeys: null | any 55 | removed: null | boolean 56 | } 57 | 58 | export type BaseRayInput = { 59 | rpcEndpointUrl: string 60 | } 61 | export type Result = { 62 | Ok?: T, 63 | Err?: E 64 | } 65 | export type MPLTokenInfo = { 66 | address: web3.PublicKey 67 | mintInfo: RawMint, 68 | metadata: any 69 | } -------------------------------------------------------------------------------- /src/execute.ts: -------------------------------------------------------------------------------- 1 | import { Connection, Keypair, Transaction, VersionedTransaction } from "@solana/web3.js"; 2 | import { cluster, connection } from "../config"; 3 | 4 | 5 | interface Blockhash { 6 | blockhash: string; 7 | lastValidBlockHeight: number; 8 | } 9 | 10 | 11 | export const executeVersionedTx = async (transaction: VersionedTransaction) => { 12 | const latestBlockhash = await connection.getLatestBlockhash() 13 | const signature = await connection.sendRawTransaction(transaction.serialize(), { skipPreflight: true }) 14 | 15 | const confirmation = await connection.confirmTransaction( 16 | { 17 | signature, 18 | lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, 19 | blockhash: latestBlockhash.blockhash, 20 | } 21 | ); 22 | 23 | if (confirmation.value.err) { 24 | console.log("Confirmation error") 25 | return "" 26 | } else { 27 | console.log(`Confirmed transaction: https://solscan.io/tx/${signature}${cluster == "devnet" ? "?cluster=devnet" : ""}`) 28 | } 29 | return signature 30 | } 31 | 32 | 33 | export const executeLegacyTx = async (transaction: Transaction, signer: Keypair[], latestBlockhash: Blockhash) => { 34 | 35 | const signature = await connection.sendTransaction(transaction, signer, { skipPreflight: true }) 36 | const confirmation = await connection.confirmTransaction( 37 | { 38 | signature, 39 | lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, 40 | blockhash: latestBlockhash.blockhash, 41 | } 42 | ); 43 | if (confirmation.value.err) { 44 | console.log("Confirmation error") 45 | return null 46 | } else { 47 | console.log(`Confirmed transaction: https://solscan.io/tx/${signature}${cluster == "devnet" ? "?cluster=devnet" : ""}`) 48 | } 49 | return signature 50 | } 51 | -------------------------------------------------------------------------------- /src/createPoolAndBundleBuy.ts: -------------------------------------------------------------------------------- 1 | import { AddressLookupTableProgram, ComputeBudgetProgram, Keypair, PublicKey, sendAndConfirmRawTransaction, Transaction, TransactionInstruction, TransactionMessage, VersionedTransaction } from "@solana/web3.js" 2 | import { 3 | DEVNET_PROGRAM_ID, 4 | jsonInfo2PoolKeys, 5 | Liquidity, 6 | MAINNET_PROGRAM_ID, 7 | MARKET_STATE_LAYOUT_V3, LiquidityPoolKeys, 8 | Token, TokenAmount, ZERO, ONE, TEN, 9 | TOKEN_PROGRAM_ID, parseBigNumberish, bool, 10 | buildSimpleTransaction, 11 | TxVersion, 12 | Percent 13 | } from "@raydium-io/raydium-sdk" 14 | import { getAssociatedTokenAddress, getAssociatedTokenAddressSync, getMint, NATIVE_MINT, unpackMint } from "@solana/spl-token"; 15 | import bs58 from "bs58" 16 | import BN from "bn.js" 17 | 18 | import { mainMenuWaiting, outputBalance, readBundlerWallets, readJson, readLUTAddressFromFile, readWallets, retrieveEnvVariable, saveDataToFile, sleep } from "./utils" 19 | import { 20 | getTokenAccountBalance, 21 | assert, 22 | getWalletTokenAccount, 23 | } from "./get_balance"; 24 | import { build_create_pool_instructions } from "./build_a_sendtxn"; 25 | import { 26 | connection, 27 | addLookupTableInfo, cluster, 28 | lookupTableCache, 29 | delay_pool_open_time, DEFAULT_TOKEN 30 | } from "../config"; 31 | import { 32 | quote_Mint_amount, 33 | input_baseMint_tokens_percentage, 34 | bundlerWalletName, 35 | batchSize 36 | } from "../settings" 37 | 38 | import { executeVersionedTx } from "./execute"; 39 | import { jitoWithAxios } from "./jitoWithAxios"; 40 | // import { createLookupTable } from "../layout/createLUT"; 41 | import { calcWalletSol } from "../layout/walletCreate"; 42 | 43 | let swapSolAmount = calcWalletSol(); 44 | 45 | const programId = cluster == "devnet" ? DEVNET_PROGRAM_ID : MAINNET_PROGRAM_ID 46 | 47 | export async function txCreateNewPoolAndBundleBuy() { 48 | // Contact with savant-cat 49 | } -------------------------------------------------------------------------------- /wallets/wallets.json: -------------------------------------------------------------------------------- 1 | [ 2 | "", 3 | "5v59oH3wa2Mv55EgpKBxicr4Rp72da2jdUySUSRjZWscKSBugnUNurSu77QBnsmjq9a7QNnrSC8qVwTBhJFhzxgC", 4 | "5xYi34WE8NBFCY7FkvBju6CvNvLSFhFCooTAYg3CFVrZ8DEmgV1RVn6xQQ8hABfw6A1B5NdegDTjxhxS9j6Jjz9x", 5 | "3zTtAQoTUfg3jhnNMB2LxTxgZRq1yAszGykyEy6gjQmUK91vF6gzX7xE5iTNr4Ux6kh5XmrqHyq1cTPcw4mn7Zp9", 6 | "4biWY8PzCorPyCCnaK2a7U8Bk4CTU25Lo6uMctULQk6S5XLE1iFSGRp3UwxjC2vzyCE3ApP12ic57Q3wJGfq6wrA", 7 | "hmnRd1i5DHFrP9yYBNvs3fAtozYihSabS4EPxzwa8nt6FkTcoZKWfvgun7eM3C9UeefGBf5yWV9XtTihxQHp4Rh", 8 | "3mkRScH78SDzf7z4SqC5S2KXXf9315WWWXMtExHzmJw1HA9J6CNPDn3e3R8aBy8WfA4opGa5Uoa6tDDRdjsQi83D", 9 | "3pLMZg8qKGiHGW6bjRozwgQn3QQtyiU1FmLswisMRwKJPPP9rVict3PhgbJZs7fDxmYaxVdgVQMNAAw3GfAaT6Ns", 10 | "5D5PTvvweUhv6fX5SZQzjFYZE2wvFN9iDKaNjHd1Nn5wV96fhbi4Z4rcmAdi5voBX1aHN2BqPH9LofA4dE1hHZZX", 11 | "3Be31pc2jgF386vXJ83VsJdaeivP8GLurppKN1Jtad8PU15fDb9aTect2nngCPK8fAgVAo3AaNXYPKEtnZZzhvyi", 12 | "5qoHiQouXxVRFn3PewXd4mVPZUs4Ug3s8yjm8hHguW2f589HY7yBTPrx6WFrghUiYvHB2HfaYDatbWzxMijf9ijr", 13 | "SG8vXzVJ8VSMR3iiY6a8U3GuW6Qm19PB598ZK6Shzn775sFzqWKveMuPPUZKnAwKL9y3RDDXCzBFUuKpBUBg9zv", 14 | "4C2y6tD3XzWT7VYo18oMDG9ABMAKpzYoRSvM7h3uvsmU7KfutoDKGdgwHZgzmwujzm9W5ECWSKd8EsEw3WJxSpFo", 15 | "5j7nZ8rRjjoHZKxzsdmdP51eszZ2LrRBRKkfDWiHvrf1mbKTkmwUQHbNcEQp8Co75QBbdCy4aTTbDC7JKjYgLMw3", 16 | "sNz2qNxkbjyzQ6WD45g1ew5iV1rKyXaJQXFarw9hisx61PFSjqymT3o24XNDVBgi99iPcNxmpgJanrGK4zcjKhh", 17 | "3s4DGGLSsa8cAEysnTJQUmnfFkJHGbEKf9WTPSC4iHhNjfLJqXcbondXkHGhCrUHoV3umsEEi1tgYyvXkUS7442M", 18 | "2AzA7hXZ9ypcbbuPdQiAVLzgEnfbZuPDn66bZvyS6wz8u2QMgWWCrhRsdJpFn5BQi5feVYvJ6f4p1qtXk3C994rj", 19 | "5Le7Z4xoNM2tJYeMAtasiMUwLWehEUqSkxpNtnUBZkix9tZmFRmrjWoR8oi49oGcXpH4NGJ4DF2DUgP39zCRZPwy", 20 | "5R9QSjfHScvaptz5bCrQX9fkx5WCe8x5nupQJfz3rCV8BM7oEAZNuad3hXnrrQP8ivg7TpDyWowvQVnDeSpUzgjn", 21 | "1m4Q9wNYqgZ2qXcFps7FKiiWLargTisQ8zsWvBtRQL3ttvU3L4a8kMW6vAqsAmp25a2kTSKfmjo2zfsBvrHtVnj", 22 | "2wv2141NVZSwULVztcVbZGk8WsiAVoQYHjChGG1XGE78GBGx9Kio9SRdHA7d44D9CNFptonQMpzdJ141gMSUf2Ds" 23 | ] -------------------------------------------------------------------------------- /src/extendLutSimulate.ts: -------------------------------------------------------------------------------- 1 | import { AddressLookupTableProgram, ComputeBudgetProgram, Keypair, PublicKey, sendAndConfirmRawTransaction, Transaction, TransactionInstruction, TransactionMessage, VersionedTransaction } from "@solana/web3.js" 2 | import { 3 | DEVNET_PROGRAM_ID, 4 | jsonInfo2PoolKeys, 5 | Liquidity, 6 | MAINNET_PROGRAM_ID, 7 | MARKET_STATE_LAYOUT_V3, LiquidityPoolKeys, 8 | Token, TokenAmount, ZERO, ONE, TEN, 9 | TOKEN_PROGRAM_ID, parseBigNumberish, bool, 10 | buildSimpleTransaction, 11 | TxVersion, 12 | Percent 13 | } from "@raydium-io/raydium-sdk" 14 | import { getAssociatedTokenAddress, getAssociatedTokenAddressSync, getMint, NATIVE_MINT, unpackMint } from "@solana/spl-token"; 15 | import bs58 from "bs58" 16 | import BN from "bn.js" 17 | 18 | import { mainMenuWaiting, outputBalance, readBundlerWallets, readJson, readLUTAddressFromFile, readWallets, retrieveEnvVariable, saveDataToFile, sleep } from "./utils" 19 | import { 20 | getTokenAccountBalance, 21 | assert, 22 | getWalletTokenAccount, 23 | } from "./get_balance"; 24 | import { build_swap_instructions, build_create_pool_instructions } from "./build_a_sendtxn"; 25 | import { 26 | connection, 27 | addLookupTableInfo, cluster, 28 | lookupTableCache, 29 | delay_pool_open_time, DEFAULT_TOKEN 30 | } from "../config"; 31 | import { 32 | quote_Mint_amount, 33 | input_baseMint_tokens_percentage, 34 | // swapWallets, 35 | defaltSwapSolAmount, 36 | bundlerWalletName, 37 | batchSize 38 | } from "../settings" 39 | 40 | import { createAndSendV0Tx } from "../layout/createLutAta"; 41 | // import { createLookupTable } from "../layout/createLUT"; 42 | import { calcWalletSol } from "../layout/walletCreate"; 43 | 44 | const programId = cluster == "devnet" ? DEVNET_PROGRAM_ID : MAINNET_PROGRAM_ID 45 | 46 | let swapSolAmount = calcWalletSol(); 47 | 48 | export async function extendLutSimulate() { 49 | // Contact with savant-cat 50 | } -------------------------------------------------------------------------------- /src/getMarketAccountSizes.ts: -------------------------------------------------------------------------------- 1 | import { Market } from "@openbook-dex/openbook"; 2 | import { 3 | calculateTotalAccountSize, 4 | EVENT_QUEUE_HEADER_SIZE, 5 | EVENT_SIZE, 6 | ORDERBOOK_HEADER_SIZE, 7 | ORDERBOOK_NODE_SIZE, 8 | REQUEST_QUEUE_HEADER_SIZE, 9 | REQUEST_SIZE, 10 | } from "./base/orderbookUtils"; 11 | import { web3 } from "@project-serum/anchor"; 12 | 13 | type useSerumMarketAccountSizesProps = { 14 | eventQueueLength: number; 15 | requestQueueLength: number; 16 | orderbookLength: number; 17 | }; 18 | export default function useSerumMarketAccountSizes({ 19 | eventQueueLength, 20 | requestQueueLength, 21 | orderbookLength, 22 | }: useSerumMarketAccountSizesProps, connection: web3.Connection, programID: web3.PublicKey) { 23 | const totalEventQueueSize = calculateTotalAccountSize( 24 | eventQueueLength, 25 | EVENT_QUEUE_HEADER_SIZE, 26 | EVENT_SIZE 27 | ) 28 | 29 | const totalRequestQueueSize = calculateTotalAccountSize( 30 | requestQueueLength, 31 | REQUEST_QUEUE_HEADER_SIZE, 32 | REQUEST_SIZE 33 | ) 34 | 35 | const totalOrderbookSize = calculateTotalAccountSize( 36 | orderbookLength, 37 | ORDERBOOK_HEADER_SIZE, 38 | ORDERBOOK_NODE_SIZE 39 | ) 40 | // const useRentExemption = connection.getMinimumBalanceForRentExemption 41 | // const marketAccountRent = await useRentExemption(Market.getLayout(programID).span); 42 | // const eventQueueRent = await useRentExemption(totalEventQueueSize); 43 | // const requestQueueRent = await useRentExemption(totalRequestQueueSize); 44 | // const orderbookRent = await useRentExemption(totalOrderbookSize); 45 | 46 | return { 47 | // marketRent: 48 | // marketAccountRent + eventQueueRent + requestQueueRent + 2 * orderbookRent, 49 | marketRent: 0, 50 | totalEventQueueSize, 51 | totalRequestQueueSize, 52 | totalOrderbookSize, 53 | }; 54 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # solana-raydium-bundler 2 | solana raydium bundler: raydium jito bundler bundling on raydium. It bundles buy/sell to 20 wallets. It uses jito for bundling. This missed some essential parts, so if you need full code or want guide / custom requirements, ping me on telegram[https://t.me/SavantCat]. 3 | 4 | ## Core functions 5 | ### Create Keypairs 6 | This step is crucial if you want to ensure that there is no SOL in the wallets. It is not necessary for every launch but is recommended for initial setups or resets. 7 | 8 | ### Premarket 9 | This is a multi-step process that needs to be done in a specific order: 10 | 1. **Execution Order:** Complete all steps from 2 to 6 in sequence. 11 | 2. **Bundle ID Check:** After each step, verify the Bundle ID to ensure it has landed correctly. 12 | 3. **Retry if Needed:** If the bundle does not land, increase the tip and retry. Exit if necessary. 13 | 4. **Verification:** Use the [Jito Block Explorer](https://explorer.jito.wtf/) to check if the bundle landed. Ignore the "Landed - no" indicator; instead, check if the first included transaction is confirmed. 14 | 15 | ### Create Pool 16 | Creating a pool might require multiple attempts: 17 | - **Spam the Function:** If the pool creation does not land on the first try, spam the function. 18 | - **Increase the Tip:** A tip of 0.1 SOL or more is recommended for better chances of landing within the first few tries. 19 | 20 | ### Sell Features 21 | Once the pool is live, you have two options for selling: 22 | 1. **Sell All Keypairs at Once (Step 4):** Use this step to sell all keypairs simultaneously and reclaim WSOL in Step 7 of Premarket (Step 2) after rugging. 23 | 2. **Sell in Percentages (Step 5):** You can sell small percentages of the supply on demand. This involves sending a specified percentage of every keypair's token balance to the fee payers, then selling it all in one singular bundle on one wallet. 24 | 25 | ### LP Remove 26 | Removing LP is straightforward: 27 | - **Non-Burn Removal:** If you do not burn your LP, it will simply be removed. 28 | 29 | -------------------------------------------------------------------------------- /layout/removeLiquidity.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js" 2 | 3 | import { tokens } from "../settings" 4 | import { mainMenuWaiting, outputBalance, readJson, saveDataToFile, sleep } from "../src/utils" 5 | import { getWalletTokenAccount } from "../src/get_balance"; 6 | import { LP_wallet_keypair } from "../settings"; 7 | import { ammRemoveLiquidity } from "../src/removeLiquidity"; 8 | import { init } from ".."; 9 | 10 | type WalletTokenAccounts = Awaited> 11 | 12 | const execute = async () => { 13 | // remove liquidity 14 | console.log("\n***************************************************************\n") 15 | await sleep(5000) 16 | const data = readJson() 17 | let params = { 18 | mint: data.mint ? new PublicKey(data.mint) : null, 19 | marketId: data.marketId ? new PublicKey(data.marketId) : null, 20 | poolId: data.poolId ? new PublicKey(data.poolId) : null, 21 | mainKp: data.mainKp, 22 | poolKeys: data.poolKeys, 23 | removed: data.removed 24 | } 25 | let removeTried = 0 26 | while (true) { 27 | if (removeTried > 10) { 28 | console.log("Remove liquidity transaction called many times, pull tx failed") 29 | return 30 | } 31 | // const removed = await ammRemoveLiquidity(LP_wallet_keypair, params.poolId!, params.poolKeys) 32 | const removed = await ammRemoveLiquidity(LP_wallet_keypair, params.poolId!) 33 | if (removed) { 34 | params.removed = true 35 | saveDataToFile(params) 36 | console.log("Single token has been completed through process") 37 | await sleep(2000) 38 | await outputBalance(LP_wallet_keypair.publicKey) 39 | console.log("\n***************************************************************\n") 40 | return 41 | } else { 42 | console.log("Failed to remove liquidity") 43 | removeTried++ 44 | } 45 | } 46 | } 47 | 48 | export const remove_liquidity = async () => { 49 | for (let i = 0; i < tokens.length; i++) { 50 | console.log(`Token ${i + 1} Liquidity Removed`) 51 | await execute() 52 | console.log("One token remove process is ended, and go for next one") 53 | await sleep(10000) 54 | mainMenuWaiting() 55 | } 56 | } 57 | 58 | // remove_liquidity() 59 | -------------------------------------------------------------------------------- /src/sell.ts: -------------------------------------------------------------------------------- 1 | import { Keypair, PublicKey } from "@solana/web3.js" 2 | import { 3 | NATIVE_MINT, 4 | getAssociatedTokenAddress, 5 | } from '@solana/spl-token' 6 | import { getSellTx } from "./swapOnlyAmm" 7 | import { execute } from "./legacy" 8 | import { connection, SWAP } from "../config" 9 | import { sellInJupito } from "./jupitoBuy" 10 | 11 | 12 | /** 13 | * @func sell 14 | * @description 'sell token' 15 | * @param poolId: PublicKey 'pool addr' 16 | * @param baseMint: PublicKey 'token addr' 17 | * @param wallet: Keypair 'subwallet keypair' 18 | */ 19 | export const sell = async (poolId: PublicKey, baseMint: PublicKey, wallet: Keypair) => { 20 | 21 | let i = 0 22 | while (true) { 23 | 24 | console.log('sell') 25 | i++; 26 | if (i == 10000) return 0 27 | try { 28 | const tokenAta = await getAssociatedTokenAddress(baseMint, wallet.publicKey) 29 | const tokenBalInfo = await connection.getTokenAccountBalance(tokenAta) 30 | if (!tokenBalInfo) { 31 | return 0; 32 | } 33 | 34 | const tokenBalance = tokenBalInfo.value.amount 35 | try { 36 | switch (SWAP) { 37 | case "RAY": 38 | const sellTx = await getSellTx(connection, wallet, baseMint, NATIVE_MINT, tokenBalance, poolId.toBase58()) 39 | if (sellTx == null) { 40 | continue 41 | } 42 | const latestBlockhashForSell = await connection.getLatestBlockhash() 43 | const txSellSig = await execute(sellTx, latestBlockhashForSell, false) 44 | const tokenSellTx = txSellSig ? `https://solscan.io/tx/${txSellSig}` : '' 45 | const solBalance = await connection.getBalance(wallet.publicKey) 46 | 47 | return 1 48 | case "JUP": 49 | console.log("JUP") 50 | sellInJupito(baseMint, wallet) 51 | return 1 52 | default: 53 | console.log("Input Swap Method RAY / JUP") 54 | process.exit(1) 55 | } 56 | } catch (error) { 57 | continue; 58 | } 59 | } 60 | catch (error) { 61 | if (i == 500) { 62 | console.log("buy error :", error) 63 | } 64 | continue; 65 | } 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /burnWalletToken.ts: -------------------------------------------------------------------------------- 1 | import { Keypair, PublicKey, TransactionMessage, VersionedTransaction } from "@solana/web3.js" 2 | import { createBurnCheckedInstruction, getAssociatedTokenAddress, unpackMint } from "@solana/spl-token"; 3 | 4 | import { swapWallets, tokens } from "./config" 5 | import { readJson, securityCheckWaiting, sleep } from "./src/utils" 6 | import { PoolInfo, UserToken } from './src/types' 7 | import { getTokenAccountBalance, getWalletTokenAccount } from "./src/get_balance"; 8 | import { connection } from "./config"; 9 | 10 | import bs58 from 'bs58' 11 | import { security_checks } from "."; 12 | 13 | type WalletTokenAccounts = Awaited> 14 | 15 | const execute = async (token: UserToken) => { 16 | let params: PoolInfo 17 | try { 18 | const data = readJson() 19 | 20 | const MINT_ADDRESS = new PublicKey("71KDcWoRTLYzgJ1tFu9ePQgrkFDDv7YX7vuzmJPJUEmy"); 21 | const WALLET = swapWallets[0] 22 | const MINT_DECIMALS = 9 23 | const account = await getAssociatedTokenAddress(MINT_ADDRESS, WALLET.publicKey); 24 | console.log("🚀 ~ execute ~ account:", account) 25 | const BURN_QUANTITY = await getTokenAccountBalance( 26 | connection, 27 | WALLET.publicKey.toString(), 28 | MINT_ADDRESS.toString() 29 | ); 30 | console.log("🚀 ~ execute ~ BURN_QUANTITY:", BURN_QUANTITY) 31 | 32 | const burnIx = createBurnCheckedInstruction( 33 | account, 34 | MINT_ADDRESS!, 35 | WALLET.publicKey, 36 | BURN_QUANTITY!, 37 | MINT_DECIMALS! 38 | ); 39 | 40 | const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('finalized'); 41 | 42 | const messageV0 = new TransactionMessage({ 43 | payerKey: WALLET.publicKey, 44 | recentBlockhash: blockhash, 45 | instructions: [burnIx] 46 | }).compileToV0Message(); 47 | const transaction = new VersionedTransaction(messageV0); 48 | transaction.sign([WALLET]); 49 | 50 | const txid = await connection.sendTransaction(transaction); 51 | 52 | const confirmation = await connection.confirmTransaction({ 53 | signature: txid, 54 | blockhash: blockhash, 55 | lastValidBlockHeight: lastValidBlockHeight 56 | }); 57 | if (confirmation.value.err) { throw new Error(" ❌ - Transaction not confirmed.") } 58 | console.log('🔥 SUCCESSFUL BURN!🔥', '\n', `https://explorer.solana.com/tx/${txid}?cluster=devnet`); 59 | 60 | } catch (error) { 61 | console.log("Error happened in one of the token flow", error) 62 | } 63 | } 64 | 65 | export const burn_lp = async () => { 66 | for (let i = 0; i < tokens.length; i++) { 67 | const token = tokens[i] 68 | console.log(`Token ${i + 1} is to be burnt`) 69 | await execute(token) 70 | console.log("One token process is ended, and go for next one") 71 | await sleep(5000) 72 | } 73 | } 74 | 75 | // burn_lp() 76 | -------------------------------------------------------------------------------- /src/get_balance.ts: -------------------------------------------------------------------------------- 1 | 2 | import { 3 | Connection, PublicKey, 4 | GetProgramAccountsFilter 5 | } from "@solana/web3.js"; 6 | 7 | import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; 8 | import { 9 | 10 | SPL_ACCOUNT_LAYOUT, 11 | TokenAccount, 12 | findProgramAddress, 13 | } from '@raydium-io/raydium-sdk'; 14 | 15 | 16 | export async function getTokenAccountBalance(connection: Connection, wallet: string, mint_token: string) { 17 | const filters: GetProgramAccountsFilter[] = [ 18 | { 19 | dataSize: 165, //size of account (bytes) 20 | }, 21 | { 22 | memcmp: { 23 | offset: 32, //location of our query in the account (bytes) 24 | bytes: wallet, //our search criteria, a base58 encoded string 25 | }, 26 | }, 27 | //Add this search parameter 28 | { 29 | memcmp: { 30 | offset: 0, //number of bytes 31 | bytes: mint_token, //base58 encoded string 32 | }, 33 | }]; 34 | const accounts = await connection.getParsedProgramAccounts( 35 | TOKEN_PROGRAM_ID, 36 | { filters: filters } 37 | ); 38 | 39 | for (const account of accounts) { 40 | const parsedAccountInfo: any = account.account.data; 41 | const mintAddress: string = parsedAccountInfo["parsed"]["info"]["mint"]; 42 | const tokenBalance: number = parseInt(parsedAccountInfo["parsed"]["info"]["tokenAmount"]["amount"]); 43 | 44 | console.log(`Account: ${account.pubkey.toString()} - Mint: ${mintAddress} - Balance: ${tokenBalance}`); 45 | 46 | if (tokenBalance) { 47 | return tokenBalance; 48 | } 49 | 50 | } 51 | } 52 | 53 | export function assert(condition: any, msg?: string): asserts condition { 54 | if (!condition) { 55 | throw new Error(msg) 56 | } 57 | } 58 | 59 | 60 | export async function getWalletTokenAccount(connection: Connection, wallet: PublicKey): Promise { 61 | const walletTokenAccount = await connection.getTokenAccountsByOwner(wallet, { 62 | programId: TOKEN_PROGRAM_ID, 63 | }); 64 | return walletTokenAccount.value.map((i) => ({ 65 | pubkey: i.pubkey, 66 | programId: i.account.owner, 67 | accountInfo: SPL_ACCOUNT_LAYOUT.decode(i.account.data), 68 | })); 69 | } 70 | 71 | 72 | export async function getWalletTokenAccountMint(connection: Connection, wallet: PublicKey, mint: PublicKey): Promise { 73 | const walletTokenAccount = await connection.getTokenAccountsByOwner(wallet, { 74 | mint: mint, 75 | }); 76 | return walletTokenAccount.value.map((i) => ({ 77 | pubkey: i.pubkey, 78 | programId: i.account.owner, 79 | accountInfo: SPL_ACCOUNT_LAYOUT.decode(i.account.data), 80 | })); 81 | } 82 | 83 | 84 | export function getATAAddress(programId: PublicKey, owner: PublicKey, mint: PublicKey) { 85 | const { publicKey, nonce } = findProgramAddress( 86 | [owner.toBuffer(), programId.toBuffer(), mint.toBuffer()], 87 | new PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL") 88 | ); 89 | return { publicKey, nonce }; 90 | } -------------------------------------------------------------------------------- /settings.ts: -------------------------------------------------------------------------------- 1 | import { Keypair } from '@solana/web3.js'; 2 | import bs58 from 'bs58'; 3 | import { UserToken } from './src/types'; 4 | 5 | // **************************************************** // 6 | // *************** SETTINGS *********************** // 7 | // **************************************************** // 8 | // SD, You should set following values before you run the program. 9 | 10 | // settings about token you are going to Mint 11 | export const tokens: UserToken[] = [ 12 | { 13 | name: 'Hello', 14 | symbol: 'Hello', 15 | decimals: 9, 16 | description: "Hello, World!", 17 | uiAmount: 10 ** 9, 18 | image: "./src/images/1.jpg", 19 | extensions: { 20 | website: "https://www.soldev.app/", 21 | twitter: "https://x.com/mklrwt013", 22 | telegram: "https://t.me/Tiffanystones" 23 | }, 24 | tags: [ 25 | "Meme", 26 | "Tokenization" 27 | ], 28 | creator: { 29 | name: "", 30 | site: "https://www.soldev.app/" 31 | } 32 | } 33 | ] 34 | 35 | // Main wallet to create token and pool, and so on 36 | export const LP_wallet_private_key = "5Fpf8MX9MGWrgMNTDjR2XtuVAWSj5FKBa5tEZSGbW4o5vWU1aTqjbDjhzBtiubarqhVryw6jok6tyEoingDavpL9"; 37 | export const LP_wallet_keypair = Keypair.fromSecretKey(new Uint8Array(bs58.decode(LP_wallet_private_key))); 38 | 39 | // amount of baseToken to put into the pool (0.5 is 50%, 1 is 100%) 40 | export const input_baseMint_tokens_percentage = 1 //ABC-Mint amount of tokens you want to add in Lp e.g. 1 = 100%. 0.9= 90% 41 | 42 | 43 | // defalut amount of Sol to bundle buy with wallets (0.01 is 0.01sol) 44 | export const defaltSwapSolAmount = 0.001 45 | 46 | // number of wallets in each transaction 47 | export const batchSize = 7 48 | 49 | // number of wallets to bundle buy 50 | export const bundleWalletNum = batchSize * 3 51 | 52 | // name of file to save bundler wallets 53 | export const bundlerWalletName = "wallets" 54 | 55 | // percent of LP tokens to burn 56 | export const burnLpQuantityPercent = 70 // 70 is 70% of total lp token supply 57 | 58 | // whether you distribute the sol to existing wallets or new wallets 59 | export const needNewWallets = true 60 | // amount of Sol to put into the Pool as liquidity 61 | export let quote_Mint_amount = 1; //COIN-SOL, amount of SOL u want to add to Pool amount 62 | //each wallet token percent 63 | export const wallet1 = 12 64 | export const wallet2 = 1 65 | export const wallet3 = 1.45 66 | export const wallet4 = 1 67 | export const wallet5 = 2.1 68 | export const wallet6 = 1.4 69 | export const wallet7 = 2.1 70 | export const wallet8 = 1.15 71 | export const wallet9 = 1.3 72 | export const wallet10 = 1.5 73 | export const wallet11 = 1.26 74 | export const wallet12 = 1.6 75 | export const wallet13 = 0.9 76 | export const wallet14 = 0.98 77 | export const wallet15 = 1.23 78 | export const wallet16 = 1.12 79 | export const wallet17 = 1.13 80 | export const wallet18 = 1.24 81 | export const wallet19 = 1.25 82 | export const wallet20 = 1.32 83 | export const wallet21 = 1.11 84 | -------------------------------------------------------------------------------- /src/revokeMintAuthority.ts: -------------------------------------------------------------------------------- 1 | import { AuthorityType, createSetAuthorityInstruction, getAssociatedTokenAddress } from '@solana/spl-token'; 2 | import { PublicKey, Transaction, Keypair, ComputeBudgetProgram } from '@solana/web3.js'; 3 | import bs58 from 'bs58' 4 | import { PoolInfo } from './types'; 5 | import { readJson, securityCheckWaiting, sleep } from './utils'; 6 | import { cluster, connection } from '../config'; 7 | import { sendAndConfirmTransaction } from '@solana/web3.js'; 8 | import { init, security_checks } from '..'; 9 | 10 | export const revokeMintAuthority = async () => { 11 | let params: PoolInfo 12 | try { 13 | const data = readJson() 14 | 15 | params = { 16 | mint: data.mint ? new PublicKey(data.mint) : null, 17 | marketId: data.marketId ? new PublicKey(data.marketId) : null, 18 | poolId: data.poolId ? new PublicKey(data.poolId) : null, 19 | mainKp: data.mainKp, 20 | poolKeys: data.poolKeys, 21 | removed: data.removed 22 | } 23 | 24 | if (!params.mainKp) return; 25 | const MINT_ADDRESS = params.mint; 26 | const mainPkStr = params.mainKp 27 | const mainKeypair = Keypair.fromSecretKey(bs58.decode(mainPkStr)) 28 | const account = await getAssociatedTokenAddress(MINT_ADDRESS!, mainKeypair.publicKey); 29 | console.log("🚀 ~ MINT_ADDRESS:", MINT_ADDRESS) 30 | console.log("🚀 ~ mainKeypair.publicKey:", mainKeypair.publicKey.toBase58()) 31 | console.log("🚀 ~ account:", account.toBase58()) 32 | 33 | const authorityType = AuthorityType.MintTokens 34 | 35 | if (mainKeypair.publicKey) { 36 | const transaction = new Transaction().add( 37 | ComputeBudgetProgram.setComputeUnitPrice({ 38 | microLamports: 60_000, 39 | }), 40 | ComputeBudgetProgram.setComputeUnitLimit({ 41 | units: 200_000, 42 | }), 43 | createSetAuthorityInstruction( 44 | MINT_ADDRESS!, 45 | mainKeypair.publicKey, 46 | authorityType, 47 | null 48 | ) 49 | ) 50 | 51 | transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; 52 | transaction.feePayer = mainKeypair.publicKey; 53 | console.log(await connection.simulateTransaction(transaction)) 54 | 55 | try { 56 | const signature = await sendAndConfirmTransaction(connection, transaction, [mainKeypair]) 57 | const Tx = signature ? `https://solscan.io/tx/${signature}${cluster == "devnet" ? "?cluster=devnet" : ""}` : '' 58 | console.log("Revoke mint authority: ", Tx) 59 | await sleep(5000) 60 | securityCheckWaiting() 61 | } catch (err) { 62 | console.log("revoking error ====>", err); 63 | securityCheckWaiting() 64 | } 65 | } 66 | } catch (error) { 67 | console.log("Error happened in one of the token flow", error) 68 | } 69 | } 70 | 71 | // revokeMintAuthority(); -------------------------------------------------------------------------------- /src/revokeFreezeAuthority.ts: -------------------------------------------------------------------------------- 1 | import { AuthorityType, createSetAuthorityInstruction, getAssociatedTokenAddress } from '@solana/spl-token'; 2 | import { PublicKey, Transaction, Keypair, ComputeBudgetProgram } from '@solana/web3.js'; 3 | import bs58 from 'bs58' 4 | import { PoolInfo } from './types'; 5 | import { readJson, securityCheckWaiting, sleep } from './utils'; 6 | import { cluster, connection } from '../config'; 7 | import { sendAndConfirmTransaction } from '@solana/web3.js'; 8 | import { init, security_checks } from '..'; 9 | 10 | export const revokeFreezeAuthority = async () => { 11 | let params: PoolInfo 12 | try { 13 | const data = readJson() 14 | 15 | params = { 16 | mint: data.mint ? new PublicKey(data.mint) : null, 17 | marketId: data.marketId ? new PublicKey(data.marketId) : null, 18 | poolId: data.poolId ? new PublicKey(data.poolId) : null, 19 | mainKp: data.mainKp, 20 | poolKeys: data.poolKeys, 21 | removed: data.removed 22 | } 23 | 24 | if (!params.mainKp) return; 25 | const MINT_ADDRESS = params.mint; 26 | const mainPkStr = params.mainKp 27 | const mainKeypair = Keypair.fromSecretKey(bs58.decode(mainPkStr)) 28 | const account = await getAssociatedTokenAddress(MINT_ADDRESS!, mainKeypair.publicKey); 29 | console.log("🚀 ~ MINT_ADDRESS:", MINT_ADDRESS) 30 | console.log("🚀 ~ mainKeypair.publicKey:", mainKeypair.publicKey.toBase58()) 31 | console.log("🚀 ~ account:", account.toBase58()) 32 | 33 | const authorityType = AuthorityType.FreezeAccount 34 | 35 | if (mainKeypair.publicKey) { 36 | const transaction = new Transaction().add( 37 | ComputeBudgetProgram.setComputeUnitPrice({ 38 | microLamports: 60_000, 39 | }), 40 | ComputeBudgetProgram.setComputeUnitLimit({ 41 | units: 200_000, 42 | }), 43 | createSetAuthorityInstruction( 44 | MINT_ADDRESS!, 45 | mainKeypair.publicKey, 46 | authorityType, 47 | null 48 | ) 49 | ) 50 | 51 | transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; 52 | transaction.feePayer = mainKeypair.publicKey; 53 | console.log(await connection.simulateTransaction(transaction)) 54 | 55 | try { 56 | const signature = await sendAndConfirmTransaction(connection, transaction, [mainKeypair]) 57 | const Tx = signature ? `https://solscan.io/tx/${signature}${cluster == "devnet" ? "?cluster=devnet" : ""}` : '' 58 | console.log("Revoke freeze authority: ", Tx) 59 | await sleep(5000) 60 | securityCheckWaiting() 61 | } catch (err) { 62 | console.log("revoking error ====>", err); 63 | await sleep(5000) 64 | securityCheckWaiting() 65 | } 66 | } 67 | } catch (error) { 68 | console.log("Error happened in one of the token flow", error) 69 | } 70 | } 71 | 72 | // revokeFreezeAuthority(); -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import readline from "readline" 2 | import fs from "fs"; 3 | 4 | import { cluster } from "./config" 5 | import { retrieveEnvVariable, sleep } from "./src/utils" 6 | import { 7 | getWalletTokenAccount, 8 | } from "./src/get_balance"; 9 | 10 | import { main_menu_display, rl, screen_clear, security_checks_display } from "./menu/menu"; 11 | import { create_token } from "./layout/createToken"; 12 | import { create_market } from "./layout/createMarket"; 13 | import { revokeMintAuthority } from "./src/revokeMintAuthority"; 14 | import { revokeFreezeAuthority } from "./src/revokeFreezeAuthority"; 15 | import { bundle_pool_buy } from "./layout/poolBuy"; 16 | import { burn_lp } from "./src/burnLp"; 17 | import { manual_all_sell } from "./layout/manualAllSell"; 18 | import { wallet_create } from "./layout/walletCreate"; 19 | import { create_extend_lut_ata } from "./layout/createLutAta"; 20 | import { simulate } from "./layout/simulation"; 21 | import { sol_gather } from "./layout/solGather"; 22 | // import { manualRebuy } from "./layout/rebuy"; 23 | // import { holderDistribute } from "./layout/holderDistribute"; 24 | 25 | type WalletTokenAccounts = Awaited> 26 | 27 | // export const rl = readline.createInterface({ 28 | // input: process.stdin, 29 | // output: process.stdout 30 | // }) 31 | 32 | export const init = () => { 33 | screen_clear(); 34 | console.log("Raydium Token Launchpad"); 35 | 36 | main_menu_display(); 37 | 38 | rl.question("\t[Main] - Choice: ", (answer: string) => { 39 | let choice = parseInt(answer); 40 | switch (choice) { 41 | case 1: 42 | create_token(); 43 | break; 44 | case 2: 45 | create_market(); 46 | break; 47 | case 3: 48 | security_checks(); 49 | break; 50 | case 4: 51 | wallet_create(); 52 | break; 53 | case 5: 54 | create_extend_lut_ata(); 55 | break; 56 | case 6: 57 | simulate(); 58 | break; 59 | case 7: 60 | bundle_pool_buy(); 61 | break; 62 | case 8: 63 | manual_all_sell(); 64 | break; 65 | case 9: 66 | sol_gather(); 67 | break; 68 | case 10: 69 | process.exit(1); 70 | break; 71 | default: 72 | console.log("\tInvalid choice!"); 73 | sleep(1500); 74 | init(); 75 | break; 76 | } 77 | }) 78 | } 79 | 80 | export const security_checks = () => { 81 | screen_clear(); 82 | console.log("Security Checks") 83 | security_checks_display(); 84 | 85 | rl.question("\t[Security Checks] - Choice: ", (answer: string) => { 86 | let choice = parseInt(answer); 87 | switch (choice) { 88 | case 1: 89 | revokeMintAuthority(); 90 | break; 91 | case 2: 92 | revokeFreezeAuthority(); 93 | break; 94 | case 3: 95 | burn_lp(); 96 | break; 97 | case 4: 98 | init(); 99 | break; 100 | case 5: 101 | process.exit(1); 102 | break; 103 | default: 104 | console.log("\tInvalid choice!"); 105 | sleep(1500); 106 | security_checks(); 107 | break; 108 | } 109 | }) 110 | } 111 | 112 | init() -------------------------------------------------------------------------------- /layout/rebuy.ts: -------------------------------------------------------------------------------- 1 | import { connection } from "../config" 2 | import base58 from "bs58" 3 | import readline from "readline" 4 | import { 5 | ComputeBudgetProgram, 6 | Keypair, 7 | PublicKey, 8 | SystemProgram, 9 | Transaction, 10 | TransactionInstruction, 11 | sendAndConfirmTransaction 12 | } from "@solana/web3.js" 13 | import { 14 | LiquidityPoolKeysV4, 15 | SPL_ACCOUNT_LAYOUT, 16 | TokenAccount 17 | } from "@raydium-io/raydium-sdk"; 18 | import { 19 | NATIVE_MINT, 20 | TOKEN_PROGRAM_ID, 21 | createAssociatedTokenAccountIdempotentInstruction, 22 | createCloseAccountInstruction, 23 | createTransferCheckedInstruction, 24 | getAssociatedTokenAddress, 25 | getAssociatedTokenAddressSync 26 | } from "@solana/spl-token"; 27 | import { LP_wallet_keypair } from "../settings"; 28 | import { getBuyTx, getSellTx } from "../src/swapOnlyAmm"; 29 | import { mainMenuWaiting, readHolderWalletDataJson, readJson, sleep } from "../src/utils"; 30 | import { execute } from "../src/legacy"; 31 | import { PoolKeys } from "../src/getPoolKeys"; 32 | import { rl } from "../menu/menu"; 33 | import { derivePoolKeys } from "../src/poolAll"; 34 | 35 | // let rl = readline.createInterface({ 36 | // input: process.stdin, 37 | // output: process.stdout 38 | // }) 39 | 40 | const mainKp = LP_wallet_keypair 41 | const mainPk = mainKp.publicKey 42 | const data = readJson() 43 | 44 | export const manualRebuy = async () => { 45 | try { 46 | if(!data.mint) { 47 | throw new Error("mint is not set yet.") 48 | } 49 | if(!data.poolId) { 50 | throw new Error("poolId is not set yet.") 51 | } 52 | const baseMint = new PublicKey(data.mint) 53 | const poolId = new PublicKey(data.poolId) 54 | const holderWallets = readHolderWalletDataJson() 55 | // const holderNum = holderWallets.length 56 | let totalBal = 0 57 | holderWallets.map(async({ pubkey }) => { 58 | let tokenAta = getAssociatedTokenAddressSync(baseMint, new PublicKey(pubkey)) 59 | const balance = (await connection.getTokenAccountBalance(tokenAta)).value.uiAmount 60 | totalBal += balance! 61 | }) 62 | await sleep(5000) 63 | console.log("Current Total holding token balance: ", totalBal); 64 | const percentBal = totalBal! * 100 / 10 ** 9 65 | console.log("% of holding token in Total supply", percentBal, "%") 66 | console.log("Please input the % of the token to buy.") 67 | 68 | const buyerAta = getAssociatedTokenAddressSync(baseMint, mainPk) 69 | 70 | rl.question("\t[Percent] - Buy Amount : ", async (answer: string) => { 71 | let buyPercentAmount = parseFloat(answer); 72 | let tokenBalance = buyPercentAmount * 10 ** 9 / 100 73 | // let poolKeys = await derivePoolKeys(poolId) 74 | 75 | // if(!poolKeys) { 76 | // throw new Error("Fail to get poolKeys") 77 | // } 78 | const buyTx = await getBuyTx(connection, mainKp, baseMint, NATIVE_MINT, tokenBalance, poolId.toBase58()) 79 | await sleep(2000) 80 | if (buyTx == null) { 81 | console.log("Fail to get the buy transaction in manual buying of the tokens") 82 | return 83 | } 84 | const latestBlockhashForBuy = await connection.getLatestBlockhash() 85 | const txBuySig = await execute(buyTx, latestBlockhashForBuy, true) 86 | const tokenBuyTx = txBuySig ? `https://solscan.io/tx/${txBuySig}` : '' 87 | console.log(tokenBuyTx) 88 | const tokenBalanceAfterBuy = await connection.getTokenAccountBalance(buyerAta) 89 | console.log("Remaining Total holding token balance: ", tokenBalanceAfterBuy.value.uiAmount) 90 | mainMenuWaiting() 91 | }) 92 | } 93 | catch (error) { 94 | console.log(error); 95 | } 96 | } -------------------------------------------------------------------------------- /src/removeLiquidity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | TOKEN_PROGRAM_ID, 3 | Token, 4 | TokenAmount, 5 | Liquidity, 6 | TxVersion, 7 | LiquidityPoolKeysV4, 8 | jsonInfo2PoolKeys, 9 | InnerSimpleV0Transaction, 10 | buildSimpleTransaction, 11 | LOOKUP_TABLE_CACHE, 12 | } from "@raydium-io/raydium-sdk"; 13 | import { Keypair, PublicKey } from "@solana/web3.js"; 14 | import { sendTx } from "./build_a_sendtxn"; 15 | import { getATAAddress, getWalletTokenAccount, sleep } from "./utils"; 16 | import { cluster, connection, } from "../config"; 17 | import { formatAmmKeysById } from "./swapOnlyAmm"; 18 | 19 | export const ammRemoveLiquidity = async ( 20 | mainKp: Keypair, 21 | poolId: PublicKey, 22 | poolKeysParam?: LiquidityPoolKeysV4 | null, 23 | ) => { 24 | try { 25 | let poolKeys: LiquidityPoolKeysV4 26 | if (poolKeysParam) 27 | poolKeys = poolKeysParam 28 | else { 29 | const poolInfo = await formatAmmKeysById(connection, poolId.toBase58()) 30 | poolKeys = jsonInfo2PoolKeys(poolInfo) 31 | } 32 | const lpToken = new Token( 33 | TOKEN_PROGRAM_ID, 34 | poolKeys.lpMint, 35 | poolKeys.lpDecimals 36 | ); 37 | const lpTokenAccount = await getATAAddress( 38 | TOKEN_PROGRAM_ID, 39 | mainKp.publicKey, 40 | poolKeys.lpMint 41 | ); 42 | 43 | let lpBalance = await connection.getTokenAccountBalance( 44 | lpTokenAccount.publicKey 45 | ); 46 | 47 | let amount_in = new TokenAmount(lpToken, lpBalance.value.amount); 48 | if (lpBalance.value.uiAmount == 0) { 49 | console.log("No lp token in wallet") 50 | return 51 | } 52 | const tokenAccountRawInfos_LP = await getWalletTokenAccount( 53 | connection, 54 | mainKp.publicKey 55 | ); 56 | 57 | const lp_ix = await Liquidity.makeRemoveLiquidityInstructionSimple({ 58 | connection, 59 | poolKeys, 60 | userKeys: { 61 | owner: mainKp.publicKey, 62 | tokenAccounts: tokenAccountRawInfos_LP, 63 | }, 64 | amountIn: amount_in, 65 | makeTxVersion: TxVersion.V0, 66 | computeBudgetConfig: {microLamports: 200_000, units: 200_000} 67 | }); 68 | 69 | let i = 0 70 | while (true) { 71 | 72 | let txids = await buildAndSendTx( 73 | mainKp, 74 | lp_ix.innerTransactions, 75 | ); 76 | const Tx = txids[0] ? `https://solscan.io/tx/${txids[0]}${cluster == "devnet" ? "?cluster=devnet" : ""}` : '' 77 | console.log("Pool Liquidity Removed: ", Tx) 78 | i++ 79 | if (i > 20) { 80 | console.log("Sent spam remove liquidity txs, need to check result") 81 | let lpBalance = await connection.getTokenAccountBalance( 82 | lpTokenAccount.publicKey 83 | ); 84 | if (lpBalance.value.uiAmount == 0) { 85 | console.log("LP removed successfully") 86 | } else { 87 | console.log("Remove LP tx unconfirmed") 88 | } 89 | return 90 | } 91 | 92 | await sleep(20000) 93 | 94 | // Check the result of removing liquidity 95 | lpBalance = await connection.getTokenAccountBalance(lpTokenAccount.publicKey) 96 | if(lpBalance.value.uiAmount == 0) { 97 | return true 98 | } 99 | } 100 | } catch (e: unknown) { 101 | console.log(`Remove liquidity error: `, e); 102 | } 103 | }; 104 | 105 | 106 | async function buildAndSendTx( 107 | keypair: Keypair, 108 | innerSimpleV0Transaction: InnerSimpleV0Transaction[], 109 | ) { 110 | const willSendTx = await buildSimpleTransaction({ 111 | connection, 112 | makeTxVersion: TxVersion.V0, 113 | payer: keypair.publicKey, 114 | innerTransactions: innerSimpleV0Transaction, 115 | addLookupTableInfo: cluster == "devnet" ? undefined : LOOKUP_TABLE_CACHE, 116 | }); 117 | return await sendTx(connection, keypair, willSendTx, { skipPreflight: true }); 118 | } 119 | 120 | -------------------------------------------------------------------------------- /src/build_a_sendtxn.ts: -------------------------------------------------------------------------------- 1 | import { 2 | buildSimpleTransaction, 3 | InnerSimpleV0Transaction, 4 | 5 | } from '@raydium-io/raydium-sdk'; 6 | import { 7 | Connection, 8 | Keypair, 9 | SendOptions, 10 | Signer, 11 | Transaction, 12 | VersionedTransaction, 13 | PublicKey 14 | } from '@solana/web3.js'; 15 | 16 | import { 17 | addLookupTableInfo, 18 | cluster, 19 | connection, 20 | makeTxVersion, 21 | } from '../config'; 22 | 23 | import { Liquidity } from '@raydium-io/raydium-sdk'; 24 | 25 | import { getComputeBudgetConfig, getComputeBudgetConfigHigh } from "./budget"; 26 | import { BN } from "bn.js"; 27 | 28 | 29 | 30 | 31 | 32 | export async function sendTx( 33 | connection: Connection, 34 | payer: Keypair | Signer, 35 | txs: (VersionedTransaction | Transaction)[], 36 | options?: SendOptions 37 | ): Promise { 38 | const txids: string[] = []; 39 | for (const iTx of txs) { 40 | if (iTx instanceof VersionedTransaction) { 41 | iTx.sign([payer]); 42 | txids.push(await connection.sendTransaction(iTx, options)); 43 | } else { 44 | txids.push(await connection.sendTransaction(iTx, [payer], options)); 45 | } 46 | } 47 | return txids; 48 | } 49 | 50 | 51 | 52 | export async function buildAndSendTx(keypair: Keypair, innerSimpleV0Transaction: InnerSimpleV0Transaction[], options?: SendOptions) { 53 | const willSendTx = await buildSimpleTransaction({ 54 | connection, 55 | makeTxVersion, 56 | payer: keypair.publicKey, 57 | innerTransactions: innerSimpleV0Transaction, 58 | addLookupTableInfo: addLookupTableInfo, 59 | }) 60 | 61 | return await sendTx(connection, keypair, willSendTx, options) 62 | } 63 | 64 | 65 | 66 | export async function build_swap_instructions( 67 | connection: any, 68 | poolKeys: any, 69 | tokenAccountRawInfos_Swap: any, 70 | keypair: any, 71 | inputTokenAmount: any, 72 | minAmountOut: any, 73 | lookupTableCache: any 74 | ) { 75 | // Contact with savant-cat 76 | 77 | return innerTransactions; 78 | 79 | } 80 | 81 | 82 | 83 | export async function build_swap_sell_instructions( 84 | Liquidity1: any, 85 | connection: any, 86 | poolKeys: any, 87 | tokenAccountRawInfos_Swap: any, 88 | keypair: any, 89 | inputTokenAmount: any, 90 | minAmountOut: any 91 | ) { 92 | 93 | const { innerTransactions } = await Liquidity.makeSwapInstructionSimple({ 94 | connection, 95 | poolKeys, 96 | userKeys: { 97 | tokenAccounts: tokenAccountRawInfos_Swap, 98 | owner: keypair.publicKey, 99 | }, 100 | amountIn: inputTokenAmount, 101 | amountOut: minAmountOut, 102 | fixedSide: "out", 103 | makeTxVersion, 104 | computeBudgetConfig: await getComputeBudgetConfigHigh(), 105 | 106 | }) 107 | 108 | return innerTransactions; 109 | 110 | } 111 | 112 | 113 | export async function build_create_pool_instructions( 114 | programId: any, 115 | market_id: any, 116 | keypair: any, 117 | tokenAccountRawInfos: any, 118 | baseMint: any, 119 | baseDecimals: any, 120 | quoteMint: any, 121 | quoteDecimals: any, 122 | delay_pool_open_time: any, 123 | base_amount_input: any, 124 | quote_amount: any, 125 | lookupTableCache: any 126 | ) { 127 | 128 | const { innerTransactions } = 129 | await Liquidity.makeCreatePoolV4InstructionV2Simple({ 130 | connection, 131 | programId: programId.AmmV4, 132 | marketInfo: { 133 | programId: programId.OPENBOOK_MARKET, 134 | marketId: market_id, 135 | }, 136 | associatedOnly: false, 137 | ownerInfo: { 138 | feePayer: keypair.publicKey, 139 | wallet: keypair.publicKey, 140 | tokenAccounts: tokenAccountRawInfos, 141 | useSOLBalance: true, 142 | }, 143 | baseMintInfo: { 144 | mint: baseMint, 145 | decimals: baseDecimals, 146 | }, 147 | quoteMintInfo: { 148 | mint: quoteMint, 149 | decimals: quoteDecimals, 150 | }, 151 | 152 | startTime: new BN(Math.floor(Date.now() / 1000) + delay_pool_open_time), 153 | baseAmount: new BN(base_amount_input.toString()), 154 | quoteAmount: new BN(quote_amount.toString()), 155 | 156 | computeBudgetConfig: { microLamports: 500_000, units: 300_000 }, 157 | checkCreateATAOwner: true, 158 | makeTxVersion: makeTxVersion, 159 | lookupTableCache, 160 | feeDestinationId: new PublicKey( 161 | cluster == "devnet" ? "3XMrhbv989VxAMi3DErLV9eJht1pHppW5LbKxe9fkEFR" : "7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5" 162 | ), 163 | }) 164 | 165 | return innerTransactions; 166 | 167 | } -------------------------------------------------------------------------------- /src/sendBulkToken.ts: -------------------------------------------------------------------------------- 1 | import { ComputeBudgetProgram, Keypair, PublicKey, Transaction, TransactionInstruction, TransactionMessage, VersionedTransaction, TransactionExpiredBlockheightExceededError } from "@solana/web3.js"; 2 | import { createAssociatedTokenAccountInstruction, createTransferCheckedInstruction, createTransferInstruction, getAssociatedTokenAddress, getOrCreateAssociatedTokenAccount } from "@solana/spl-token"; 3 | import { cluster, connection } from "../config"; 4 | import { sleep } from "./utils"; 5 | import { executeVersionedTx } from "./execute"; 6 | import { sendAndConfirmTransaction } from "@solana/web3.js"; 7 | 8 | interface Drop { 9 | walletAddress: PublicKey, 10 | tokenAmount: number 11 | } 12 | 13 | export async function newSendToken( 14 | walletKeypairs: Keypair[], tokensToSendArr: number[], walletKeypair: Keypair, mintAddress: PublicKey, tokenDecimal: number 15 | ) { 16 | try { 17 | const srcAta = await getAssociatedTokenAddress(mintAddress, walletKeypair.publicKey) 18 | if (tokensToSendArr.length !== walletKeypairs.length) { 19 | console.log("Number of wallets and token amounts array is not matching") 20 | throw new Error("Number of wallets and token amounts array is not matching") 21 | } 22 | 23 | const insts: TransactionInstruction[] = [] 24 | for (let i = 0; i < walletKeypairs.length; i++) { 25 | const destKp = walletKeypairs[i] 26 | const amount = tokensToSendArr[i] 27 | console.log("token amount ", amount) 28 | 29 | const baseAta = await getAssociatedTokenAddress(mintAddress, destKp.publicKey) 30 | if (!await connection.getAccountInfo(baseAta)) { 31 | insts.push( 32 | createAssociatedTokenAccountInstruction( 33 | walletKeypair.publicKey, 34 | baseAta, 35 | destKp.publicKey, 36 | mintAddress 37 | ) 38 | ) 39 | } 40 | 41 | insts.push( 42 | createTransferCheckedInstruction( 43 | srcAta, 44 | mintAddress, 45 | baseAta, 46 | walletKeypair.publicKey, 47 | Math.floor(amount * 10 ** tokenDecimal), 48 | tokenDecimal 49 | ) 50 | ) 51 | } 52 | 53 | console.log("total number of instructions : ", insts.length) 54 | const txs = await makeTxs(insts, walletKeypair) 55 | if (!txs) { 56 | console.log("Transaction not retrieved from makeTxs function") 57 | throw new Error("Transaction not retrieved from makeTxs function") 58 | } 59 | try { 60 | await Promise.all(txs.map(async (transaction, i) => { 61 | await sleep(i * 200) 62 | // Assuming you have a function to send a transaction 63 | return handleTxs(transaction, walletKeypair) 64 | })); 65 | 66 | } catch (error) { 67 | console.log("Error in transaction confirmation part : ", error) 68 | } 69 | } catch (error) { 70 | console.log("New Send Token function error : ", error) 71 | } 72 | } 73 | 74 | const makeTxs = async (insts: TransactionInstruction[], mainKp: Keypair) => { 75 | try { 76 | 77 | const batchNum = 12 78 | const txNum = Math.ceil(insts.length / batchNum) 79 | const txs: Transaction[] = [] 80 | for (let i = 0; i < txNum; i++) { 81 | const upperIndex = batchNum * (i + 1) 82 | const downIndex = batchNum * i 83 | const tx = new Transaction().add( 84 | ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), 85 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 100_000 }) 86 | ) 87 | 88 | for (let j = downIndex; j < upperIndex; j++) 89 | if (insts[j]) 90 | tx.add(insts[j]) 91 | 92 | tx.recentBlockhash = (await connection.getLatestBlockhash("confirmed")).blockhash 93 | tx.feePayer = mainKp.publicKey 94 | console.log(await connection.simulateTransaction(tx)) 95 | 96 | txs.push(tx) 97 | } 98 | if (txs.length == 0) { 99 | console.log("Empty instructions as input") 100 | throw new Error("Empty instructions as input") 101 | } 102 | return txs 103 | } catch (error) { 104 | console.log("MakeTxs ~ error:", error) 105 | } 106 | 107 | } 108 | 109 | const handleTxs = async (transaction: Transaction, mainKp: Keypair) => { 110 | const sig = await sendAndConfirmTransaction(connection, transaction, [mainKp], { skipPreflight: true }) 111 | console.log(`https://solscan.io/tx/${sig}`); 112 | } -------------------------------------------------------------------------------- /layout/solGather.ts: -------------------------------------------------------------------------------- 1 | import { ComputeBudgetProgram, Keypair, LAMPORTS_PER_SOL, PublicKey, SystemProgram, Transaction, TransactionInstruction, TransactionMessage, VersionedTransaction } from "@solana/web3.js"; 2 | import { mainMenuWaiting, outputBalance, readBundlerWallets, readJson, saveBundlerWalletsToFile, sleep } from "../src/utils"; 3 | import { cluster, connection } from "../config"; 4 | import { bundlerWalletName, bundleWalletNum, needNewWallets } from "../settings" 5 | import bs58 from 'bs58' 6 | import { screen_clear } from "../menu/menu"; 7 | import { execute } from "../src/legacy"; 8 | import { createCloseAccountInstruction, getAssociatedTokenAddress, NATIVE_MINT } from "@solana/spl-token"; 9 | import { calcWalletSol } from "./walletCreate"; 10 | 11 | const walletNum = bundleWalletNum 12 | let swapSolAmount = calcWalletSol(); 13 | 14 | 15 | export const sol_gather = async () => { 16 | screen_clear() 17 | console.log(`Gathering Sol from ${bundleWalletNum} bundler wallets...`); 18 | 19 | const savedWallets = readBundlerWallets(bundlerWalletName) 20 | // console.log("🚀 ~ savedWallets: ", savedWallets) 21 | 22 | const walletKPs = savedWallets.map((wallet: string) => Keypair.fromSecretKey(bs58.decode(wallet))); 23 | const data = readJson() 24 | const LP_wallet_keypair = Keypair.fromSecretKey(bs58.decode(data.mainKp!)) 25 | const batchLength = 5 26 | const batchNum = Math.ceil((bundleWalletNum - 1) / batchLength) 27 | let successNum = 0 28 | 29 | 30 | try { 31 | for (let i = 0; i < batchNum; i++) { 32 | const sendSolTx: TransactionInstruction[] = [] 33 | sendSolTx.push( 34 | ComputeBudgetProgram.setComputeUnitLimit({ units: 100_000 }), 35 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 250_000 }) 36 | ) 37 | for (let j = 0; j < batchLength; j++) { 38 | 39 | let solAmount = await connection.getBalance(walletKPs[i * batchLength + j + 1].publicKey) 40 | const quoteAta = await getAssociatedTokenAddress(NATIVE_MINT, walletKPs[i * batchLength + j + 1].publicKey) 41 | if ((i * batchLength + j) >= bundleWalletNum) continue; 42 | sendSolTx.push( 43 | createCloseAccountInstruction( 44 | quoteAta, 45 | walletKPs[0].publicKey, 46 | walletKPs[i * batchLength + j + 1].publicKey 47 | ), 48 | SystemProgram.transfer({ 49 | fromPubkey: walletKPs[i * batchLength + j + 1].publicKey, 50 | toPubkey: LP_wallet_keypair.publicKey, 51 | lamports: solAmount 52 | // - 0.00001 * LAMPORTS_PER_SOL 53 | }) 54 | ) 55 | 56 | } 57 | let index = 0 58 | while (true) { 59 | try { 60 | if (index > 3) { 61 | console.log("Error in gathering sol. Please retry gathering.") 62 | mainMenuWaiting() 63 | return 64 | } 65 | const siTx = new Transaction().add(...sendSolTx) 66 | const latestBlockhash = await connection.getLatestBlockhash() 67 | siTx.feePayer = LP_wallet_keypair.publicKey 68 | siTx.recentBlockhash = latestBlockhash.blockhash 69 | const messageV0 = new TransactionMessage({ 70 | payerKey: LP_wallet_keypair.publicKey, 71 | recentBlockhash: latestBlockhash.blockhash, 72 | instructions: sendSolTx, 73 | }).compileToV0Message() 74 | const transaction = new VersionedTransaction(messageV0) 75 | const signers = walletKPs.slice(i * batchLength + 1, bundleWalletNum > (i + 1) * batchLength ? ((i + 1) * batchLength + 1) : bundleWalletNum) 76 | transaction.sign(signers) 77 | transaction.sign([LP_wallet_keypair]) 78 | console.log(await connection.simulateTransaction(transaction)) 79 | const txSig = await execute(transaction, latestBlockhash, 1) 80 | const tokenBuyTx = txSig ? `https://solscan.io/tx/${txSig}${cluster == "devnet" ? "?cluster=devnet" : ""}` : '' 81 | if (txSig) { 82 | successNum++ 83 | console.log("SOL gathered ", tokenBuyTx) 84 | } 85 | break 86 | } catch (error) { 87 | index++ 88 | console.log(error) 89 | } 90 | } 91 | } 92 | if (successNum == batchNum) console.log("Successfully gathered sol from bundler wallets!") 93 | } catch (error) { 94 | console.log(`Failed to transfer SOL`) 95 | } 96 | await outputBalance(LP_wallet_keypair.publicKey) 97 | mainMenuWaiting() 98 | } 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /src/getPoolKeys.ts: -------------------------------------------------------------------------------- 1 | import { Liquidity, LiquidityPoolKeysV4, MARKET_STATE_LAYOUT_V3, Market } from "@raydium-io/raydium-sdk"; 2 | import { Commitment, Connection, PublicKey } from "@solana/web3.js"; 3 | 4 | import dotenv from 'dotenv' 5 | import { sleep } from "./utils"; 6 | dotenv.config(); 7 | 8 | export class PoolKeys { 9 | static SOLANA_ADDRESS = 'So11111111111111111111111111111111111111112' 10 | static RAYDIUM_POOL_V4_PROGRAM_ID = '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'; 11 | static OPENBOOK_ADDRESS = 'srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX'; 12 | static SOL_DECIMALS = 9 13 | 14 | static async fetchMarketId(connection: Connection, baseMint: PublicKey, quoteMint: PublicKey, commitment: Commitment) { 15 | const accounts = await connection.getProgramAccounts( 16 | new PublicKey('srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX'), 17 | { 18 | commitment, 19 | filters: [ 20 | { dataSize: MARKET_STATE_LAYOUT_V3.span }, 21 | { 22 | memcmp: { 23 | offset: MARKET_STATE_LAYOUT_V3.offsetOf("baseMint"), 24 | bytes: baseMint.toBase58(), 25 | }, 26 | }, 27 | { 28 | memcmp: { 29 | offset: MARKET_STATE_LAYOUT_V3.offsetOf("quoteMint"), 30 | bytes: quoteMint.toBase58(), 31 | }, 32 | }, 33 | ], 34 | } 35 | ); 36 | sleep(1500) 37 | return accounts.map(({ account }) => MARKET_STATE_LAYOUT_V3.decode(account.data))[0].ownAddress 38 | } 39 | 40 | 41 | static async fetchMarketInfo(connection: Connection, marketId: PublicKey) { 42 | const marketAccountInfo = await connection.getAccountInfo(marketId, "processed"); 43 | if (!marketAccountInfo) { 44 | throw new Error('Failed to fetch market info for market id ' + marketId.toBase58()); 45 | } 46 | 47 | return MARKET_STATE_LAYOUT_V3.decode(marketAccountInfo.data); 48 | } 49 | 50 | static async generateV4PoolInfo(baseMint: PublicKey, quoteMint: PublicKey, marketID: PublicKey) { 51 | const poolInfo = Liquidity.getAssociatedPoolKeys({ 52 | version: 4, 53 | marketVersion: 3, 54 | baseMint: baseMint, 55 | quoteMint: quoteMint, 56 | baseDecimals: 0, 57 | quoteDecimals: this.SOL_DECIMALS, 58 | programId: new PublicKey('675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'), 59 | marketId: marketID, 60 | marketProgramId: new PublicKey('srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX'), 61 | }); 62 | 63 | return { poolInfo } 64 | } 65 | 66 | static async fetchPoolKeyInfo(connection: Connection, baseMint: PublicKey, quoteMint: PublicKey): Promise { 67 | const marketId = await this.fetchMarketId(connection, baseMint, quoteMint, 'confirmed') 68 | 69 | console.log("marketId", marketId) 70 | 71 | const marketInfo = await this.fetchMarketInfo(connection, marketId); 72 | console.log("marketInfo", marketInfo) 73 | const baseMintInfo = await connection.getParsedAccountInfo(baseMint, "confirmed") as MintInfo; 74 | const baseDecimals = baseMintInfo.value.data.parsed.info.decimals 75 | 76 | const V4PoolInfo = await this.generateV4PoolInfo(baseMint, quoteMint, marketId) 77 | console.log("V4PoolInfo", V4PoolInfo) 78 | const lpMintInfo = await connection.getParsedAccountInfo(V4PoolInfo.poolInfo.lpMint, "confirmed") as MintInfo; 79 | 80 | return { 81 | id: V4PoolInfo.poolInfo.id, 82 | marketId: marketId, 83 | baseMint: baseMint, 84 | quoteMint: quoteMint, 85 | baseVault: V4PoolInfo.poolInfo.baseVault, 86 | quoteVault: V4PoolInfo.poolInfo.quoteVault, 87 | lpMint: V4PoolInfo.poolInfo.lpMint, 88 | baseDecimals: baseDecimals, 89 | quoteDecimals: this.SOL_DECIMALS, 90 | lpDecimals: lpMintInfo.value.data.parsed.info.decimals, 91 | version: 4, 92 | programId: new PublicKey(this.RAYDIUM_POOL_V4_PROGRAM_ID), 93 | authority: V4PoolInfo.poolInfo.authority, 94 | openOrders: V4PoolInfo.poolInfo.openOrders, 95 | targetOrders: V4PoolInfo.poolInfo.targetOrders, 96 | withdrawQueue: new PublicKey("11111111111111111111111111111111"), 97 | lpVault: new PublicKey("11111111111111111111111111111111"), 98 | marketVersion: 3, 99 | marketProgramId: new PublicKey(this.OPENBOOK_ADDRESS), 100 | marketAuthority: Market.getAssociatedAuthority({ programId: new PublicKey(this.OPENBOOK_ADDRESS), marketId: marketId }).publicKey, 101 | marketBaseVault: marketInfo.baseVault, 102 | marketQuoteVault: marketInfo.quoteVault, 103 | marketBids: marketInfo.bids, 104 | marketAsks: marketInfo.asks, 105 | marketEventQueue: marketInfo.eventQueue, 106 | lookupTableAccount: PublicKey.default 107 | } 108 | } 109 | } 110 | 111 | interface MintInfo { 112 | value: { 113 | data: { 114 | parsed: { 115 | info: { 116 | decimals: number 117 | } 118 | } 119 | } 120 | } 121 | } -------------------------------------------------------------------------------- /src/createTokenPinata.ts: -------------------------------------------------------------------------------- 1 | import { Keypair, PublicKey, SystemProgram, Transaction, ComputeBudgetProgram, sendAndConfirmTransaction } from '@solana/web3.js'; 2 | import { 3 | createAssociatedTokenAccountInstruction, createInitializeMintInstruction, createMintToInstruction, 4 | getAssociatedTokenAddress, getMinimumBalanceForRentExemptMint, MintLayout, TOKEN_PROGRAM_ID 5 | } from '@solana/spl-token'; 6 | import { PROGRAM_ID, DataV2, createCreateMetadataAccountV3Instruction } from '@metaplex-foundation/mpl-token-metadata'; 7 | import axios from 'axios'; 8 | import FormData from 'form-data'; 9 | import base58 from 'bs58'; 10 | import fs from 'fs'; 11 | import { BN } from 'bn.js'; 12 | import { cluster, connection, pinataApiKey, pinataSecretApiKey } from '../config'; 13 | import { Metadata, UserToken } from "./types" 14 | import { readJson } from './utils'; 15 | 16 | const uploadToIPFS = async (filePath: string) => { 17 | const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`; 18 | const data = new FormData(); 19 | 20 | data.append('file', fs.createReadStream(filePath)); 21 | 22 | const res = await axios.post(url, data, { 23 | maxContentLength: Infinity, 24 | headers: { 25 | 'Content-Type': `multipart/form-data; boundary=${data.getBoundary()}`, 26 | 'pinata_api_key': pinataApiKey, 27 | 'pinata_secret_api_key': pinataSecretApiKey 28 | } 29 | }); 30 | 31 | return res.data.IpfsHash; 32 | }; 33 | 34 | const uploadMetadata = async (metadata: object) => { 35 | const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS`; 36 | 37 | const res = await axios.post(url, metadata, { 38 | headers: { 39 | 'pinata_api_key': pinataApiKey, 40 | 'pinata_secret_api_key': pinataSecretApiKey 41 | } 42 | }); 43 | 44 | return res.data.IpfsHash; 45 | }; 46 | 47 | const data = readJson() 48 | 49 | export const createTokenWithMetadata = async (token: UserToken) => { 50 | try { 51 | const { name, symbol, description, decimals, uiAmount, image } = token 52 | const mainKp = data.mainKp 53 | 54 | const payer = Keypair.fromSecretKey(base58.decode(mainKp!)) 55 | const walletPk = payer.publicKey 56 | 57 | const number = Date.now() 58 | // Upload image to IPFS 59 | const imageHash = await uploadToIPFS(image); 60 | console.log(Date.now() - number, "ms to upload to IPFS") 61 | console.log(`Image link: https://gateway.pinata.cloud/ipfs/${imageHash}`) 62 | 63 | // Prepare metadata 64 | const metadata: Metadata = { 65 | name, 66 | symbol, 67 | description, 68 | image: `https://gateway.pinata.cloud/ipfs/${imageHash}`, 69 | }; 70 | 71 | if (token.extensions) 72 | metadata.extensions = token.extensions 73 | if(token.tags) 74 | metadata.tags = token.tags 75 | if(token.creator) 76 | metadata.creator = token.creator 77 | // Upload metadata to IPFS 78 | const metadataHash = await uploadMetadata(metadata); 79 | const metadataUri = `https://gateway.pinata.cloud/ipfs/${metadataHash}`; 80 | console.log(`Metadata uploaded: ${metadataUri}`); 81 | 82 | const mint_rent = await getMinimumBalanceForRentExemptMint(connection) 83 | 84 | const mintKp = Keypair.generate(); 85 | 86 | const mint = mintKp.publicKey 87 | const tokenAta = await getAssociatedTokenAddress(mint, walletPk) 88 | const [metadataPDA] = await PublicKey.findProgramAddress( 89 | [ 90 | Buffer.from("metadata"), 91 | PROGRAM_ID.toBuffer(), 92 | mint.toBuffer(), 93 | ], PROGRAM_ID 94 | ); 95 | 96 | const amount = BigInt(new BN(uiAmount).mul(new BN(10 ** decimals)).toString()) 97 | const tokenMetadata: DataV2 = { 98 | name: name, 99 | symbol: symbol, 100 | uri: metadataUri, 101 | sellerFeeBasisPoints: 0, 102 | creators: null, 103 | collection: null, 104 | uses: null 105 | }; 106 | const transaction = new Transaction().add( 107 | ComputeBudgetProgram.setComputeUnitPrice({ 108 | microLamports: 60_000, 109 | }), 110 | ComputeBudgetProgram.setComputeUnitLimit({ 111 | units: 200_000, 112 | }), 113 | SystemProgram.createAccount({ 114 | fromPubkey: walletPk, 115 | newAccountPubkey: mint, 116 | space: MintLayout.span, 117 | lamports: mint_rent, 118 | programId: TOKEN_PROGRAM_ID, 119 | }), 120 | createInitializeMintInstruction(mint, decimals, walletPk, walletPk), 121 | createAssociatedTokenAccountInstruction(walletPk, tokenAta, walletPk, mint), 122 | createMintToInstruction(mint, tokenAta, walletPk, amount), 123 | // createUpdateMetadataAccountV2Instruction({ 124 | // metadata: metadataPDA, 125 | // mint, 126 | // mintAuthority: walletPk, 127 | // payer: walletPk, 128 | // updateAuthority: walletPk, 129 | // }, { 130 | // updateMetadataAccountArgsV2: { 131 | // data: tokenMetadata, 132 | // isMutable: true, 133 | // updateAuthority: walletPk, 134 | // primarySaleHappened: true 135 | // } 136 | // } 137 | // ) 138 | createCreateMetadataAccountV3Instruction( 139 | { 140 | metadata: metadataPDA, 141 | mint: mint, 142 | mintAuthority: walletPk, 143 | payer: walletPk, 144 | updateAuthority: walletPk, 145 | }, 146 | { 147 | createMetadataAccountArgsV3: { 148 | data: tokenMetadata, 149 | isMutable: false, 150 | collectionDetails: null 151 | } 152 | } 153 | ) 154 | ) 155 | transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash 156 | transaction.feePayer = walletPk 157 | // console.log(await connection.simulateTransaction(transaction)) 158 | const sig = await sendAndConfirmTransaction(connection, transaction, [payer, mintKp], { skipPreflight: true, commitment: "finalized" }) 159 | console.log(`Token is created: https://solscan.io/tx/${sig}${cluster == "devnet" ? "?cluster=devnet" : ""}`) 160 | console.log(`Token contract link: https://solscan.io/token/${mint}${cluster == "devnet" ? "?cluster=devnet" : ""}`) 161 | return { mint, amount } 162 | 163 | } catch (error) { 164 | console.log("Create token error: ", error) 165 | return 166 | } 167 | }; 168 | -------------------------------------------------------------------------------- /src/poolAll.ts: -------------------------------------------------------------------------------- 1 | import * as spl from '@solana/spl-token'; 2 | import { MARKET_STATE_LAYOUT_V3, Market } from '@openbook-dex/openbook'; 3 | import { AccountInfo, Connection, Keypair, PublicKey } from '@solana/web3.js'; 4 | import { u8, u32, struct } from '@solana/buffer-layout'; 5 | import { u64, publicKey } from '@solana/buffer-layout-utils'; 6 | import base58 from 'bs58'; 7 | import { LIQUIDITY_STATE_LAYOUT_V4, LiquidityPoolKeysV4 } from '@raydium-io/raydium-sdk'; 8 | import { connection, cluster } from '../config'; 9 | import { LP_wallet_keypair } from '../settings'; 10 | 11 | export const SPL_MINT_LAYOUT = struct([ 12 | u32('mintAuthorityOption'), 13 | publicKey('mintAuthority'), 14 | u64('supply'), 15 | u8('decimals'), 16 | u8('isInitialized'), 17 | u32('freezeAuthorityOption'), 18 | publicKey('freezeAuthority') 19 | ]); 20 | 21 | export const SPL_ACCOUNT_LAYOUT = struct([ 22 | publicKey('mint'), 23 | publicKey('owner'), 24 | u64('amount'), 25 | u32('delegateOption'), 26 | publicKey('delegate'), 27 | u8('state'), 28 | u32('isNativeOption'), 29 | u64('isNative'), 30 | u64('delegatedAmount'), 31 | u32('closeAuthorityOption'), 32 | publicKey('closeAuthority') 33 | ]); 34 | 35 | 36 | 37 | export const rayFee = cluster == "devnet"? new PublicKey("3XMrhbv989VxAMi3DErLV9eJht1pHppW5LbKxe9fkEFR") : new PublicKey("7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5"); 38 | // export const tipAcct = cluster == "devnet"? new PublicKey("3XMrhbv989VxAMi3DErLV9eJht1pHppW5LbKxe9fkEFR") :new PublicKey("Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY"); 39 | export const RayLiqPoolv4 = cluster == "devnet"? new PublicKey("HWy1jotHpo6UqeQxx49dpYYdQB8wj9Qk9MdxwjLvDHB8") : new PublicKey("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"); 40 | 41 | export const wallet = LP_wallet_keypair 42 | 43 | const openbookProgram = cluster == "devnet"? new PublicKey("EoTcMgcDRTJVZDMZWBoU6rhYHZfkNTVEAfz3uUJRcYGj") : new PublicKey('srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX'); 44 | 45 | async function getMarketInfo(marketId: PublicKey) { 46 | let reqs = 0; 47 | let marketInfo = await connection.getAccountInfo(marketId); 48 | reqs++; 49 | 50 | while (!marketInfo) { 51 | marketInfo = await connection.getAccountInfo(marketId); 52 | reqs++; 53 | if (marketInfo) { 54 | break; 55 | } else if (reqs > 20) { 56 | console.log(`Could not get market info..`); 57 | 58 | return null; 59 | } 60 | } 61 | 62 | return marketInfo; 63 | } 64 | 65 | export async function fetchMarketId(connection: Connection, baseMint: PublicKey, quoteMint: PublicKey) { 66 | const accounts = await connection.getProgramAccounts( 67 | cluster == "devnet"? new PublicKey("EoTcMgcDRTJVZDMZWBoU6rhYHZfkNTVEAfz3uUJRcYGj") : new PublicKey('srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX'), 68 | { 69 | commitment: "confirmed", 70 | filters: [ 71 | { dataSize: MARKET_STATE_LAYOUT_V3.span }, 72 | { 73 | memcmp: { 74 | offset: MARKET_STATE_LAYOUT_V3.offsetOf("baseMint"), 75 | bytes: baseMint.toBase58(), 76 | }, 77 | }, 78 | { 79 | memcmp: { 80 | offset: MARKET_STATE_LAYOUT_V3.offsetOf("quoteMint"), 81 | bytes: quoteMint.toBase58(), 82 | }, 83 | }, 84 | ], 85 | } 86 | ); 87 | return accounts.map(({ account }) => MARKET_STATE_LAYOUT_V3.decode(account.data))[0].ownAddress 88 | } 89 | 90 | 91 | 92 | async function getDecodedData(marketInfo: { 93 | executable?: boolean; 94 | owner?: PublicKey; 95 | lamports?: number; 96 | data: any; 97 | rentEpoch?: number | undefined; 98 | }) { 99 | return Market.getLayout(openbookProgram).decode(marketInfo.data); 100 | } 101 | 102 | async function getMintData(mint: PublicKey) { 103 | return connection.getAccountInfo(mint); 104 | } 105 | 106 | async function getDecimals(mintData: AccountInfo | null) { 107 | if (!mintData) throw new Error('No mint data!'); 108 | 109 | return SPL_MINT_LAYOUT.decode(mintData.data).decimals; 110 | } 111 | 112 | async function getOwnerAta(mint: { toBuffer: () => Uint8Array | Buffer }, publicKey: PublicKey) { 113 | const foundAta = PublicKey.findProgramAddressSync( 114 | [publicKey.toBuffer(), spl.TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], 115 | spl.ASSOCIATED_TOKEN_PROGRAM_ID 116 | )[0]; 117 | 118 | return foundAta; 119 | } 120 | 121 | function getVaultSigner(marketId: { toBuffer: any }, marketDeco: { vaultSignerNonce: { toString: () => any } }) { 122 | const seeds = [marketId.toBuffer()]; 123 | const seedsWithNonce = seeds.concat(Buffer.from([Number(marketDeco.vaultSignerNonce.toString())]), Buffer.alloc(7)); 124 | 125 | return PublicKey.createProgramAddressSync(seedsWithNonce, openbookProgram); 126 | } 127 | 128 | export async function derivePoolKeys(poolId: PublicKey): Promise { 129 | const account = await connection.getAccountInfo(poolId) 130 | if(!account) { 131 | console.log("Invalid account info") 132 | return null 133 | } 134 | const info = LIQUIDITY_STATE_LAYOUT_V4.decode(account.data) 135 | 136 | 137 | const marketInfo = await getMarketInfo(info.marketId); 138 | if (!marketInfo) return null; 139 | const marketId = info.marketId 140 | const marketDeco = await getDecodedData(marketInfo); 141 | const { baseMint } = marketDeco; 142 | const baseMintData = await getMintData(baseMint); 143 | const baseDecimals = await getDecimals(baseMintData); 144 | const ownerBaseAta = await getOwnerAta(baseMint, wallet.publicKey); 145 | const { quoteMint } = marketDeco; 146 | const quoteMintData = await getMintData(quoteMint); 147 | const quoteDecimals = await getDecimals(quoteMintData); 148 | const ownerQuoteAta = await getOwnerAta(quoteMint, wallet.publicKey); 149 | const authority = PublicKey.findProgramAddressSync( 150 | [Buffer.from([97, 109, 109, 32, 97, 117, 116, 104, 111, 114, 105, 116, 121])], 151 | RayLiqPoolv4 152 | )[0]; 153 | const version: 4|5 = 4 154 | const marketVersion: 3 = 3 155 | const marketAuthority = getVaultSigner(marketId, marketDeco); 156 | 157 | // get/derive all the pool keys 158 | const poolKeys = { 159 | keg: new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'), 160 | version, 161 | marketVersion, 162 | programId: RayLiqPoolv4, 163 | baseMint, 164 | quoteMint, 165 | ownerBaseAta, 166 | ownerQuoteAta, 167 | baseDecimals, 168 | quoteDecimals, 169 | lpDecimals: baseDecimals, 170 | authority, 171 | marketAuthority, 172 | marketProgramId: openbookProgram, 173 | marketId, 174 | marketBids: marketDeco.bids, 175 | marketAsks: marketDeco.asks, 176 | marketQuoteVault: marketDeco.quoteVault, 177 | marketBaseVault: marketDeco.baseVault, 178 | marketEventQueue: marketDeco.eventQueue, 179 | id: PublicKey.findProgramAddressSync( 180 | [RayLiqPoolv4.toBuffer(), marketId.toBuffer(), Buffer.from('amm_associated_seed', 'utf-8')], 181 | RayLiqPoolv4 182 | )[0], 183 | baseVault: PublicKey.findProgramAddressSync( 184 | [RayLiqPoolv4.toBuffer(), marketId.toBuffer(), Buffer.from('coin_vault_associated_seed', 'utf-8')], 185 | RayLiqPoolv4 186 | )[0], 187 | coinVault: PublicKey.findProgramAddressSync( 188 | [RayLiqPoolv4.toBuffer(), marketId.toBuffer(), Buffer.from('pc_vault_associated_seed', 'utf-8')], 189 | RayLiqPoolv4 190 | )[0], 191 | lpMint: PublicKey.findProgramAddressSync( 192 | [RayLiqPoolv4.toBuffer(), marketId.toBuffer(), Buffer.from('lp_mint_associated_seed', 'utf-8')], 193 | RayLiqPoolv4 194 | )[0], 195 | lpVault: PublicKey.findProgramAddressSync( 196 | [RayLiqPoolv4.toBuffer(), marketId.toBuffer(), Buffer.from('temp_lp_token_associated_seed', 'utf-8')], 197 | RayLiqPoolv4 198 | )[0], 199 | targetOrders: PublicKey.findProgramAddressSync( 200 | [RayLiqPoolv4.toBuffer(), marketId.toBuffer(), Buffer.from('target_associated_seed', 'utf-8')], 201 | RayLiqPoolv4 202 | )[0], 203 | withdrawQueue: PublicKey.findProgramAddressSync( 204 | [RayLiqPoolv4.toBuffer(), marketId.toBuffer(), Buffer.from('withdraw_associated_seed', 'utf-8')], 205 | RayLiqPoolv4 206 | )[0], 207 | openOrders: PublicKey.findProgramAddressSync( 208 | [RayLiqPoolv4.toBuffer(), marketId.toBuffer(), Buffer.from('open_order_associated_seed', 'utf-8')], 209 | RayLiqPoolv4 210 | )[0], 211 | quoteVault: PublicKey.findProgramAddressSync( 212 | [RayLiqPoolv4.toBuffer(), marketId.toBuffer(), Buffer.from('pc_vault_associated_seed', 'utf-8')], 213 | RayLiqPoolv4 214 | )[0], 215 | lookupTableAccount: new PublicKey('11111111111111111111111111111111') 216 | }; 217 | 218 | return poolKeys; 219 | } -------------------------------------------------------------------------------- /src/createMarket.ts: -------------------------------------------------------------------------------- 1 | import { MAINNET_PROGRAM_ID, DEVNET_PROGRAM_ID } from "@raydium-io/raydium-sdk" 2 | import { 3 | ComputeBudgetProgram, 4 | Keypair, 5 | PublicKey, 6 | SystemProgram, 7 | Transaction, 8 | TransactionInstruction, 9 | } from "@solana/web3.js" 10 | import { 11 | getMint, 12 | TOKEN_PROGRAM_ID, 13 | ACCOUNT_SIZE, 14 | createInitializeAccountInstruction, 15 | NATIVE_MINT, 16 | } from "@solana/spl-token" 17 | import BN from "bn.js" 18 | import { DexInstructions, Market } from "@project-serum/serum" 19 | import { 20 | calculateTotalAccountSize, 21 | EVENT_QUEUE_HEADER_SIZE, 22 | EVENT_SIZE, 23 | REQUEST_QUEUE_HEADER_SIZE, 24 | REQUEST_SIZE, 25 | ORDERBOOK_HEADER_SIZE, 26 | ORDERBOOK_NODE_SIZE, 27 | sleep, 28 | } from "../src/utils" 29 | import { cluster, connection } from "../config" 30 | import { executeLegacyTx } from "./execute" 31 | 32 | const raydiumProgramId = cluster == "mainnet" ? 33 | MAINNET_PROGRAM_ID : DEVNET_PROGRAM_ID 34 | 35 | const LOT_SIZE = -3 36 | const TICK_SIZE = 8 37 | const TOTAL_EVENT_QUEUE_SIZE = calculateTotalAccountSize( 38 | 128, 39 | EVENT_QUEUE_HEADER_SIZE, 40 | EVENT_SIZE 41 | ) 42 | 43 | const TOTAL_REQUEST_QUEUE_SIZE = calculateTotalAccountSize( 44 | 10, 45 | REQUEST_QUEUE_HEADER_SIZE, 46 | REQUEST_SIZE 47 | ) 48 | 49 | const TOTAL_ORDER_BOOK_SIZE = calculateTotalAccountSize( 50 | 201, 51 | ORDERBOOK_HEADER_SIZE, 52 | ORDERBOOK_NODE_SIZE 53 | ) 54 | 55 | const getVaultNonce = async (market: PublicKey, programId: PublicKey) => { 56 | let i = 0 57 | let result = null 58 | while (true) { 59 | result = 60 | await getVaultOwnerAndNonce( 61 | market, 62 | programId, 63 | i 64 | ) 65 | if (result) 66 | return result 67 | else 68 | i++ 69 | } 70 | } 71 | 72 | 73 | export async function getVaultOwnerAndNonce( 74 | marketAddress: PublicKey, 75 | dexAddress: PublicKey, 76 | seedNum: number 77 | ): Promise<[vaultOwner: PublicKey, nonce: BN] | undefined> { 78 | let nonce = new BN(seedNum) 79 | // console.log("nonce:", nonce) 80 | try { 81 | // console.log("market address: ", marketAddress.toBase58()) 82 | // console.log("dex address: ", dexAddress.toBase58()) 83 | 84 | const vaultOwner = PublicKey.createProgramAddressSync( 85 | [marketAddress.toBuffer(), nonce.toArrayLike(Buffer, "le", 8)], 86 | dexAddress 87 | ) 88 | // console.log("vault owner ", vaultOwner.toBase58()) 89 | return [vaultOwner, nonce] 90 | } catch (e) { 91 | console.log('found incompatible bump here, trying again') 92 | } 93 | } 94 | 95 | 96 | export const createMarket = async ( 97 | wallet: Keypair, 98 | baseMintAddress: PublicKey 99 | ) => { 100 | try { 101 | let baseMint: PublicKey 102 | let baseMintDecimals: number 103 | let quoteMint: PublicKey 104 | let quoteMintDecimals: number 105 | const vaultInstructions: TransactionInstruction[] = [] 106 | const marketInstructions: TransactionInstruction[] = [] 107 | 108 | try { 109 | const baseMintInfo = await getMint(connection, baseMintAddress) 110 | baseMint = baseMintInfo.address 111 | baseMintDecimals = baseMintInfo.decimals 112 | 113 | const quoteMintInfo = await getMint(connection, NATIVE_MINT) 114 | quoteMint = quoteMintInfo.address 115 | quoteMintDecimals = quoteMintInfo.decimals 116 | } catch (e) { 117 | console.error("Invalid mints provided.", e) 118 | return 119 | } 120 | 121 | const timeOut = setTimeout(async () => { 122 | console.log("Trying market creation again") 123 | const marketId = await createMarket(wallet, baseMintAddress) 124 | return marketId 125 | }, 20000) 126 | 127 | const marketAccounts = { 128 | market: Keypair.generate(), 129 | requestQueue: Keypair.generate(), 130 | eventQueue: Keypair.generate(), 131 | bids: Keypair.generate(), 132 | asks: Keypair.generate(), 133 | baseVault: Keypair.generate(), 134 | quoteVault: Keypair.generate(), 135 | } 136 | const [vaultOwner, vaultOwnerNonce] = await getVaultNonce( 137 | marketAccounts.market.publicKey, 138 | raydiumProgramId.OPENBOOK_MARKET 139 | ) 140 | 141 | // create vaults 142 | 143 | 144 | vaultInstructions.push( 145 | SystemProgram.createAccount({ 146 | fromPubkey: wallet.publicKey, 147 | newAccountPubkey: marketAccounts.baseVault.publicKey, 148 | lamports: await connection.getMinimumBalanceForRentExemption( 149 | ACCOUNT_SIZE 150 | ), 151 | space: ACCOUNT_SIZE, 152 | programId: TOKEN_PROGRAM_ID, 153 | }), 154 | SystemProgram.createAccount({ 155 | fromPubkey: wallet.publicKey, 156 | newAccountPubkey: marketAccounts.quoteVault.publicKey, 157 | lamports: await connection.getMinimumBalanceForRentExemption( 158 | ACCOUNT_SIZE 159 | ), 160 | space: ACCOUNT_SIZE, 161 | programId: TOKEN_PROGRAM_ID, 162 | }), 163 | createInitializeAccountInstruction( 164 | marketAccounts.baseVault.publicKey, 165 | baseMint, 166 | vaultOwner 167 | ), 168 | createInitializeAccountInstruction( 169 | marketAccounts.quoteVault.publicKey, 170 | quoteMint, 171 | vaultOwner 172 | ) 173 | ) 174 | 175 | clearTimeout(timeOut) 176 | // tickSize and lotSize here are the 1e^(-x) values, so no check for ><= 0 177 | const baseLotSize = Math.round( 178 | 10 ** baseMintDecimals * Math.pow(10, -1 * LOT_SIZE) 179 | ) 180 | const quoteLotSize = Math.round( 181 | 10 ** quoteMintDecimals * 182 | Math.pow(10, -1 * LOT_SIZE) * 183 | Math.pow(10, -1 * TICK_SIZE) 184 | ) 185 | // create market account 186 | marketInstructions.push( 187 | SystemProgram.createAccount({ 188 | newAccountPubkey: marketAccounts.market.publicKey, 189 | fromPubkey: wallet.publicKey, 190 | space: Market.getLayout(raydiumProgramId.OPENBOOK_MARKET).span, 191 | lamports: await connection.getMinimumBalanceForRentExemption( 192 | Market.getLayout(raydiumProgramId.OPENBOOK_MARKET).span 193 | ), 194 | programId: raydiumProgramId.OPENBOOK_MARKET, 195 | }) 196 | ) 197 | 198 | // create request queue 199 | marketInstructions.push( 200 | SystemProgram.createAccount({ 201 | newAccountPubkey: marketAccounts.requestQueue.publicKey, 202 | fromPubkey: wallet.publicKey, 203 | space: TOTAL_REQUEST_QUEUE_SIZE, 204 | lamports: await connection.getMinimumBalanceForRentExemption( 205 | TOTAL_REQUEST_QUEUE_SIZE 206 | ), 207 | programId: raydiumProgramId.OPENBOOK_MARKET, 208 | }) 209 | ) 210 | 211 | // create event queue 212 | marketInstructions.push( 213 | SystemProgram.createAccount({ 214 | newAccountPubkey: marketAccounts.eventQueue.publicKey, 215 | fromPubkey: wallet.publicKey, 216 | space: TOTAL_EVENT_QUEUE_SIZE, 217 | lamports: await connection.getMinimumBalanceForRentExemption( 218 | TOTAL_EVENT_QUEUE_SIZE 219 | ), 220 | programId: raydiumProgramId.OPENBOOK_MARKET, 221 | }) 222 | ) 223 | 224 | const orderBookRentExempt = 225 | await connection.getMinimumBalanceForRentExemption(TOTAL_ORDER_BOOK_SIZE) 226 | 227 | // create bids 228 | marketInstructions.push( 229 | SystemProgram.createAccount({ 230 | newAccountPubkey: marketAccounts.bids.publicKey, 231 | fromPubkey: wallet.publicKey, 232 | space: TOTAL_ORDER_BOOK_SIZE, 233 | lamports: orderBookRentExempt, 234 | programId: raydiumProgramId.OPENBOOK_MARKET, 235 | }) 236 | ) 237 | 238 | // create asks 239 | marketInstructions.push( 240 | SystemProgram.createAccount({ 241 | newAccountPubkey: marketAccounts.asks.publicKey, 242 | fromPubkey: wallet.publicKey, 243 | space: TOTAL_ORDER_BOOK_SIZE, 244 | lamports: orderBookRentExempt, 245 | programId: raydiumProgramId.OPENBOOK_MARKET, 246 | }) 247 | ) 248 | 249 | marketInstructions.push( 250 | DexInstructions.initializeMarket({ 251 | market: marketAccounts.market.publicKey, 252 | requestQueue: marketAccounts.requestQueue.publicKey, 253 | eventQueue: marketAccounts.eventQueue.publicKey, 254 | bids: marketAccounts.bids.publicKey, 255 | asks: marketAccounts.asks.publicKey, 256 | baseVault: marketAccounts.baseVault.publicKey, 257 | quoteVault: marketAccounts.quoteVault.publicKey, 258 | baseMint, 259 | quoteMint, 260 | baseLotSize: new BN(baseLotSize), 261 | quoteLotSize: new BN(quoteLotSize), 262 | feeRateBps: 150, // Unused in v3 263 | quoteDustThreshold: new BN(500), // Unused in v3 264 | vaultSignerNonce: vaultOwnerNonce, 265 | programId: raydiumProgramId.OPENBOOK_MARKET, 266 | }) 267 | ) 268 | console.log("Transactions for market creation is ready, sending transactions") 269 | try { 270 | let blockhash = (await connection.getLatestBlockhash("confirmed")); 271 | 272 | const createVaultTransaction = new Transaction().add( 273 | ComputeBudgetProgram.setComputeUnitPrice({ 274 | microLamports: 60_000, 275 | }), 276 | ComputeBudgetProgram.setComputeUnitLimit({ 277 | units: 200_000, 278 | }), 279 | ...vaultInstructions 280 | ); 281 | createVaultTransaction.recentBlockhash = blockhash.blockhash; 282 | createVaultTransaction.feePayer = wallet.publicKey; 283 | createVaultTransaction.sign( 284 | wallet, 285 | marketAccounts.baseVault, 286 | marketAccounts.quoteVault 287 | ); 288 | 289 | console.log("VaultTransaction simulation: ", await connection.simulateTransaction(createVaultTransaction)) 290 | 291 | const createMarketTransaction = new Transaction().add( 292 | ComputeBudgetProgram.setComputeUnitPrice({ 293 | microLamports: 60_000, 294 | }), 295 | ComputeBudgetProgram.setComputeUnitLimit({ 296 | units: 200_000, 297 | }), 298 | ...marketInstructions 299 | ); 300 | createMarketTransaction.recentBlockhash = blockhash.blockhash; 301 | createMarketTransaction.feePayer = wallet.publicKey; 302 | createMarketTransaction.sign( 303 | wallet, 304 | marketAccounts.market, 305 | marketAccounts.requestQueue, 306 | marketAccounts.eventQueue, 307 | marketAccounts.bids, 308 | marketAccounts.asks 309 | ); 310 | 311 | console.log("MarketTransaction simulation: ", await connection.simulateTransaction(createMarketTransaction)) 312 | 313 | let index1 = 0 314 | while (true) { 315 | if (index1 > 10) { 316 | console.log("Error in creating market") 317 | return 318 | } else { 319 | const sig1 = await executeLegacyTx( 320 | createVaultTransaction, 321 | [ 322 | wallet, 323 | marketAccounts.baseVault, 324 | marketAccounts.quoteVault 325 | ], 326 | blockhash 327 | ) 328 | if (!sig1) 329 | console.log("Retrying create vault transaction") 330 | else { 331 | console.log(`Vault signature: https://solscan.io/tx/${sig1}${cluster == "devnet" ? "?cluster=devnet" : ""}`) 332 | break 333 | } 334 | } 335 | } 336 | let index2 = 0 337 | while (true) { 338 | if (index2 > 10) { 339 | console.log("Error in creating market") 340 | return 341 | } else { 342 | const sig2 = await executeLegacyTx( 343 | createMarketTransaction, 344 | [ 345 | wallet, 346 | marketAccounts.market, 347 | marketAccounts.requestQueue, 348 | marketAccounts.eventQueue, 349 | marketAccounts.bids, 350 | marketAccounts.asks 351 | ], 352 | blockhash 353 | ) 354 | if (!sig2) 355 | console.log("Retrying create market transaction") 356 | else { 357 | console.log(`Market signature: https://solscan.io/tx/${sig2}${cluster == "devnet" ? "?cluster=devnet" : ""}`) 358 | break 359 | } 360 | } 361 | } 362 | console.log("Market ID: ", `https://solscan.io/account/${marketAccounts.market.publicKey.toBase58()}${cluster == "devnet" ? "?cluster=devnet" : ""}`); 363 | return marketAccounts.market.publicKey; 364 | } catch (error) { 365 | console.error("Error creating market: ", error); 366 | return 367 | } 368 | } catch (error) { 369 | console.error("Error creating market: ", error); 370 | return 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /layout/walletCreate.ts: -------------------------------------------------------------------------------- 1 | import { ComputeBudgetProgram, Keypair, LAMPORTS_PER_SOL, PublicKey, SystemProgram, Transaction, TransactionInstruction, TransactionMessage, VersionedTransaction, Connection } from "@solana/web3.js"; 2 | import { newSendToken } from "../src/sendBulkToken"; 3 | import { Data, mainMenuWaiting, readBundlerWallets, readJson, saveBundlerWalletsToFile, saveHolderWalletsToFile, sleep } from "../src/utils"; 4 | import { connection } from "../config"; 5 | import { LP_wallet_private_key, bundlerWalletName, bundleWalletNum, needNewWallets } from "../settings" 6 | import { getAssociatedTokenAddressSync } from "@solana/spl-token"; 7 | import bs58 from 'bs58' 8 | import { screen_clear } from "../menu/menu"; 9 | import { execute } from "../src/legacy"; 10 | import { wallet1, wallet2, wallet3, wallet4, wallet5, wallet6, wallet7, wallet8, wallet9, wallet10, wallet11, wallet12, wallet13, wallet14, wallet15, wallet16, wallet17, wallet18, wallet19, wallet20, wallet21, quote_Mint_amount } from "../settings" 11 | const walletNum = bundleWalletNum 12 | export const wallet_create = async () => { 13 | screen_clear() 14 | console.log(`Creating ${walletNum} Wallets for bundle buy`); 15 | 16 | let wallets: string[] = [] 17 | let swapSolAmount = calcWalletSol() 18 | console.log("swapSolAmount=======================>", swapSolAmount); 19 | if (needNewWallets) { 20 | // Step 1 - creating bundler wallets 21 | try { 22 | console.log(LP_wallet_private_key); 23 | wallets.push(LP_wallet_private_key); 24 | for (let i = 0; i < (bundleWalletNum - 1); i++) { 25 | const newWallet = Keypair.generate() 26 | wallets.push(bs58.encode(newWallet.secretKey)) 27 | } 28 | saveBundlerWalletsToFile( 29 | wallets, bundlerWalletName 30 | ) 31 | 32 | await sleep(2000) 33 | } catch (error) { console.log(error) } 34 | console.log("🚀 ~ Bundler wallets: ", wallets) 35 | } 36 | 37 | const savedWallets = readBundlerWallets(bundlerWalletName) 38 | // console.log("🚀 ~ savedWallets: ", savedWallets) 39 | 40 | // Step 2 - distributing sol to bundler wallets 41 | console.log("Distributing sol to bundler wallets...") 42 | 43 | const walletKPs = savedWallets.map((wallet: string) => Keypair.fromSecretKey(bs58.decode(wallet))); 44 | const data = readJson() 45 | const LP_wallet_keypair = Keypair.fromSecretKey(bs58.decode(data.mainKp!)) 46 | 47 | // 20 wallets sol airdrop transaction 48 | try { 49 | let walletIndex = 1; 50 | const sendSolTx: TransactionInstruction[] = [] 51 | sendSolTx.push( 52 | ComputeBudgetProgram.setComputeUnitLimit({ units: 100_000 }), 53 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 250_000 }) 54 | ) 55 | for (let j = 0; j < (bundleWalletNum - 1); j++) { 56 | if (!swapSolAmount) { 57 | throw new Error("swapSolAmount is undefined"); 58 | } 59 | let solAmount = swapSolAmount[walletIndex] * 10; 60 | console.log(walletIndex); 61 | console.log(swapSolAmount[walletIndex]); 62 | console.log(walletKPs[walletIndex].publicKey); 63 | sendSolTx.push( 64 | SystemProgram.transfer({ 65 | fromPubkey: LP_wallet_keypair.publicKey, 66 | toPubkey: walletKPs[walletIndex].publicKey, 67 | lamports: Math.round((solAmount + 0.01) * LAMPORTS_PER_SOL) 68 | }) 69 | ) 70 | walletIndex++; 71 | } 72 | 73 | let index = 0 74 | while (true) { 75 | try { 76 | if (index > 3) { 77 | console.log("Error in distribution") 78 | return null 79 | } 80 | const siTx = new Transaction().add(...sendSolTx) 81 | const latestBlockhash = await connection.getLatestBlockhash() 82 | siTx.feePayer = LP_wallet_keypair.publicKey 83 | siTx.recentBlockhash = latestBlockhash.blockhash 84 | 85 | console.log(await connection.simulateTransaction(siTx)) 86 | const messageV0 = new TransactionMessage({ 87 | payerKey: LP_wallet_keypair.publicKey, 88 | recentBlockhash: latestBlockhash.blockhash, 89 | instructions: sendSolTx, 90 | }).compileToV0Message() 91 | const transaction = new VersionedTransaction(messageV0) 92 | transaction.sign([LP_wallet_keypair]) 93 | const txSig = await execute(transaction, latestBlockhash, 1) 94 | const tokenBuyTx = txSig ? `https://solscan.io/tx/${txSig}` : '' 95 | console.log("SOL distributed ", tokenBuyTx) 96 | break 97 | } catch (error) { 98 | index++ 99 | } 100 | } 101 | 102 | console.log("Successfully distributed sol to bundler wallets!") 103 | } catch (error) { 104 | console.log("Failed to transfer SOL", error) 105 | } 106 | 107 | mainMenuWaiting() 108 | } 109 | 110 | 111 | export const calcWalletSol = () => { 112 | 113 | let wallet1SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1) * 10 ** 7)) - (quote_Mint_amount * 10 ** 9 / (10 ** 9 - 0))) * 10 ** 7) / (10 ** 7); 114 | let wallet2SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 115 | 116 | let wallet3SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 117 | 118 | let wallet4SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 119 | 120 | let wallet5SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 121 | 122 | let wallet6SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 123 | 124 | let wallet7SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 125 | 126 | let wallet8SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 127 | 128 | let wallet9SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 129 | 130 | let wallet10SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 131 | 132 | let wallet11SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 133 | 134 | 135 | let wallet12SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 136 | 137 | let wallet13SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 138 | 139 | let wallet14SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13 - wallet14) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 140 | 141 | let wallet15SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13 - wallet14 - wallet15) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13 - wallet14) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 142 | 143 | let wallet16SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13 - wallet14 - wallet15 - wallet16) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13 - wallet14 - wallet15) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 144 | 145 | let wallet17SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13 - wallet14 - wallet15 - wallet16 - wallet17) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13 - wallet14 - wallet15 - wallet16) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 146 | 147 | let wallet18SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13 - wallet14 - wallet15 - wallet16 - wallet17 - wallet18) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13 - wallet14 - wallet15 - wallet16 - wallet17) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 148 | 149 | let wallet19SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13 - wallet14 - wallet15 - wallet16 - wallet17 - wallet18 - wallet19) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13 - wallet14 - wallet15 - wallet16 - wallet17 - wallet18) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 150 | 151 | let wallet20SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13 - wallet14 - wallet15 - wallet16 - wallet17 - wallet18 - wallet19 - wallet20) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13 - wallet14 - wallet15 - wallet16 - wallet17 - wallet18 - wallet19) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 152 | 153 | let wallet21SwapSol = Math.floor((((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13 - wallet14 - wallet15 - wallet16 - wallet17 - wallet18 - wallet19 - wallet20 - wallet21) * 10 ** 7)) - ((quote_Mint_amount * 10 ** 9) / ((100 - wallet1 - wallet2 - wallet3 - wallet4 - wallet5 - wallet6 - wallet7 - wallet8 - wallet9 - wallet10 - wallet11 - wallet12 - wallet13 - wallet14 - wallet15 - wallet16 - wallet17 - wallet18 - wallet19 - wallet20) * 10 ** 7))) * 10 ** 7) / (10 ** 7); 154 | let walletSwapSol: number[] = [wallet1SwapSol, wallet2SwapSol, wallet3SwapSol, wallet4SwapSol, wallet5SwapSol, wallet6SwapSol, wallet7SwapSol, wallet8SwapSol, wallet9SwapSol, wallet10SwapSol, wallet11SwapSol, wallet12SwapSol, wallet13SwapSol, wallet14SwapSol, wallet15SwapSol, wallet16SwapSol, wallet17SwapSol, wallet18SwapSol, wallet19SwapSol, wallet20SwapSol, wallet21SwapSol] 155 | 156 | if ((wallet1SwapSol + wallet2SwapSol + wallet3SwapSol + wallet4SwapSol + wallet5SwapSol + wallet6SwapSol + wallet7SwapSol + wallet8SwapSol + wallet9SwapSol + wallet10SwapSol + wallet11SwapSol + wallet12SwapSol + wallet13SwapSol + wallet14SwapSol + wallet15SwapSol + wallet16SwapSol + wallet17SwapSol + wallet18SwapSol + wallet19SwapSol + wallet20SwapSol + wallet21SwapSol) >= 100) { 157 | console.log("Total token percent of 21 wallets over 100%."); 158 | 159 | } else { 160 | 161 | return walletSwapSol; 162 | } 163 | 164 | } -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv' 2 | import fs from 'fs' 3 | import readline from 'readline' 4 | import { Connection, GetProgramAccountsFilter, Keypair, LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js"; 5 | import { connection } from "../config"; 6 | import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; 7 | import { SPL_ACCOUNT_LAYOUT, TokenAccount } from "@raydium-io/raydium-sdk"; 8 | import base58 from "bs58"; 9 | import { PoolInfo, PoolInfoStr } from "./types"; 10 | import { init, security_checks } from '..' 11 | import { rl } from '../menu/menu'; 12 | 13 | // dotenv.config() 14 | 15 | export const retrieveEnvVariable = (variableName: string) => { 16 | const variable = process.env[variableName] || '' 17 | if (!variable) { 18 | console.log(`${variableName} is not set`) 19 | process.exit(1) 20 | } 21 | return variable 22 | } 23 | 24 | export function sleep(ms: number) { 25 | return new Promise(resolve => setTimeout(resolve, ms)); 26 | } 27 | 28 | export function calcNonDecimalValue(value: number, decimals: number): number { 29 | return Math.trunc(value * (Math.pow(10, decimals))) 30 | } 31 | 32 | export function calcDecimalValue(value: number, decimals: number): number { 33 | return value / (Math.pow(10, decimals)) 34 | } 35 | 36 | export async function getNullableResultFromPromise(value: Promise, opt?: { or?: T, logError?: boolean }): Promise { 37 | return value.catch((error) => { 38 | if (opt) console.log({ error }) 39 | return opt?.or != undefined ? opt.or : null 40 | }) 41 | } 42 | 43 | // Define the type for the JSON file content 44 | export interface Data { 45 | privateKey: string; 46 | pubkey: string; 47 | } 48 | 49 | 50 | /** 51 | * 52 | * For Market Creation 53 | * 54 | */ 55 | 56 | export const EVENT_QUEUE_LENGTH = 2978; 57 | export const EVENT_SIZE = 88; 58 | export const EVENT_QUEUE_HEADER_SIZE = 32; 59 | 60 | export const REQUEST_QUEUE_LENGTH = 63; 61 | export const REQUEST_SIZE = 80; 62 | export const REQUEST_QUEUE_HEADER_SIZE = 32; 63 | 64 | export const ORDERBOOK_LENGTH = 909; 65 | export const ORDERBOOK_NODE_SIZE = 72; 66 | export const ORDERBOOK_HEADER_SIZE = 40; 67 | 68 | // export async function getVaultOwnerAndNonce( 69 | // marketAddress: PublicKey, 70 | // dexAddress: PublicKey 71 | // ): Promise<[vaultOwner: PublicKey, nonce: BN]> { 72 | // const nonce = new BN(0); 73 | // // eslint-disable-next-line no-constant-condition 74 | // while (true) { 75 | // try { 76 | // const vaultOwner = await PublicKey.createProgramAddress( 77 | // [marketAddress.toBuffer(), nonce.toArrayLike(Buffer, "le", 8)], 78 | // dexAddress 79 | // ); 80 | // return [vaultOwner, nonce]; 81 | // } catch (e) { 82 | // nonce.iaddn(1); 83 | // } 84 | // } 85 | // } 86 | 87 | export function calculateTotalAccountSize( 88 | individualAccountSize: number, 89 | accountHeaderSize: number, 90 | length: number 91 | ) { 92 | const accountPadding = 12; 93 | const minRequiredSize = 94 | accountPadding + accountHeaderSize + length * individualAccountSize; 95 | 96 | const modulo = minRequiredSize % 8; 97 | 98 | return modulo <= 4 99 | ? minRequiredSize + (4 - modulo) 100 | : minRequiredSize + (8 - modulo + 4); 101 | } 102 | 103 | export function calculateAccountLength( 104 | totalAccountSize: number, 105 | accountHeaderSize: number, 106 | individualAccountSize: number 107 | ) { 108 | const accountPadding = 12; 109 | return Math.floor( 110 | (totalAccountSize - accountPadding - accountHeaderSize) / 111 | individualAccountSize 112 | ); 113 | } 114 | 115 | export const outputBalance = async (solAddress: PublicKey) => { 116 | const bal = await connection.getBalance(solAddress, "processed") / LAMPORTS_PER_SOL 117 | console.log("Balance in ", solAddress.toBase58(), " is ", bal, "SOL") 118 | return bal 119 | } 120 | 121 | /** 122 | * 123 | * For pool creation 124 | * 125 | */ 126 | 127 | export async function getTokenAccountBalance( 128 | connection: Connection, 129 | wallet: string, 130 | mint_token: string 131 | ) { 132 | const filters: GetProgramAccountsFilter[] = [ 133 | { 134 | dataSize: 165, //size of account (bytes) 135 | }, 136 | { 137 | memcmp: { 138 | offset: 32, //location of our query in the account (bytes) 139 | bytes: wallet, //our search criteria, a base58 encoded string 140 | }, 141 | }, 142 | //Add this search parameter 143 | { 144 | memcmp: { 145 | offset: 0, //number of bytes 146 | bytes: mint_token, //base58 encoded string 147 | }, 148 | }, 149 | ]; 150 | const accounts = await connection.getParsedProgramAccounts(TOKEN_PROGRAM_ID, { 151 | filters: filters, 152 | }); 153 | 154 | for (const account of accounts) { 155 | const parsedAccountInfo: any = account.account.data; 156 | // const mintAddress: string = parsedAccountInfo["parsed"]["info"]["mint"]; 157 | const tokenBalance: number = parseInt( 158 | parsedAccountInfo["parsed"]["info"]["tokenAmount"]["amount"] 159 | ); 160 | 161 | // console.log( 162 | // `Account: ${account.pubkey.toString()} - Mint: ${mintAddress} - Balance: ${tokenBalance}` 163 | // ); 164 | 165 | if (tokenBalance) { 166 | return tokenBalance; 167 | } 168 | } 169 | } 170 | 171 | export function assert(condition: any, msg?: string): asserts condition { 172 | if (!condition) { 173 | throw new Error(msg); 174 | } 175 | } 176 | 177 | export async function getWalletTokenAccount( 178 | connection: Connection, 179 | wallet: PublicKey 180 | ): Promise { 181 | const walletTokenAccount = await connection.getTokenAccountsByOwner(wallet, { 182 | programId: TOKEN_PROGRAM_ID, 183 | }); 184 | return walletTokenAccount.value.map((i) => ({ 185 | pubkey: i.pubkey, 186 | programId: i.account.owner, 187 | accountInfo: SPL_ACCOUNT_LAYOUT.decode(i.account.data), 188 | })); 189 | } 190 | 191 | 192 | 193 | /** 194 | * 195 | * Pool remove part 196 | * 197 | */ 198 | 199 | 200 | export async function getATAAddress( 201 | programId: PublicKey, 202 | owner: PublicKey, 203 | mint: PublicKey 204 | ) { 205 | const [publicKey, nonce] = await PublicKey.findProgramAddress( 206 | [owner.toBuffer(), programId.toBuffer(), mint.toBuffer()], 207 | new PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL") 208 | ); 209 | return { publicKey, nonce }; 210 | } 211 | 212 | export function readJson(filename: string = "data.json"): PoolInfoStr { 213 | try { 214 | if (!fs.existsSync(filename)) { 215 | // If the file does not exist, create an empty array 216 | return { 217 | mint: null, 218 | marketId: null, 219 | poolId: null, 220 | mainKp: null, 221 | poolKeys: null, 222 | removed: false 223 | } 224 | } 225 | const data = fs.readFileSync(filename, 'utf-8'); 226 | const parsedData = JSON.parse(data) 227 | return parsedData 228 | } catch (error) { 229 | return { 230 | mint: null, 231 | marketId: null, 232 | poolId: null, 233 | mainKp: null, 234 | poolKeys: null, 235 | removed: false 236 | } 237 | } 238 | } 239 | 240 | export function readWallets(filename: string = "wallets.json"): string[] { 241 | try { 242 | if (!fs.existsSync(filename)) { 243 | // If the file does not exist, create an empty array 244 | console.log("Wallets are not set in wallets.json file.") 245 | return [] 246 | } 247 | const data = fs.readFileSync(filename, 'utf-8'); 248 | const parsedData = JSON.parse(data) 249 | return parsedData.wallets 250 | } catch (error) { 251 | console.log(error) 252 | return [] 253 | } 254 | } 255 | 256 | export const readBundlerWallets = (filename: string) => { 257 | const filePath: string = `wallets/${filename}.json` 258 | 259 | try { 260 | // Check if the file exists 261 | if (fs.existsSync(filePath)) { 262 | // Read the file content 263 | const fileContent = fs.readFileSync(filePath, 'utf-8'); 264 | const wallets = JSON.parse(fileContent); 265 | return wallets; 266 | } else { 267 | console.log(`File ${filePath} does not exist.`); 268 | return []; 269 | } 270 | } catch (error) { 271 | console.log('Error reading data from JSON file:', error); 272 | return []; 273 | } 274 | }; 275 | 276 | export const saveLUTAddressToFile = (publicKey: string, filePath: string = "wallets/lutAddress.txt") => { 277 | try { 278 | // Write the public key to the specified file 279 | fs.writeFileSync(filePath, publicKey); 280 | console.log("Public key saved successfully to", filePath); 281 | } catch (error) { 282 | console.log('Error saving public key to file:', error); 283 | } 284 | }; 285 | 286 | export const readLUTAddressFromFile = (filePath: string = "wallets/lutAddress.txt") => { 287 | try { 288 | // Check if the file exists 289 | if (fs.existsSync(filePath)) { 290 | // Read the file content 291 | const publicKey = fs.readFileSync(filePath, 'utf-8'); 292 | return publicKey.trim(); // Remove any surrounding whitespace or newlines 293 | } else { 294 | console.log(`File ${filePath} does not exist.`); 295 | return null; // Return null if the file does not exist 296 | } 297 | } catch (error) { 298 | console.log('Error reading public key from file:', error); 299 | return null; // Return null in case of error 300 | } 301 | }; 302 | 303 | export const saveDataToFile = (newData: PoolInfo, filePath: string = "data.json") => { 304 | try { 305 | // let existingData: PoolInfo 306 | 307 | // Check if the file exists 308 | if (fs.existsSync(filePath)) { 309 | // try { 310 | // // If the file exists, read its content 311 | // const fileContent = fs.readFileSync(filePath, 'utf-8'); 312 | // existingData = JSON.parse(fileContent); 313 | // } catch (parseError) { 314 | // // If there is an error parsing the file, delete the corrupted file 315 | // console.error('Error parsing JSON file, deleting corrupted file:', parseError); 316 | fs.unlinkSync(filePath); 317 | // } 318 | } 319 | 320 | // Write the updated data back to the file 321 | fs.writeFileSync(filePath, JSON.stringify(newData, null, 2)); 322 | 323 | } catch (error) { 324 | console.log('Error saving data to JSON file:', error); 325 | } 326 | }; 327 | 328 | export const saveVolumeWalletToFile = (newData: Data[], filePath: string = "wallets/volumeWallets.json") => { 329 | try { 330 | let existingData: Data[] = []; 331 | 332 | // Check if the file exists 333 | if (fs.existsSync(filePath)) { 334 | // If the file exists, read its content 335 | const fileContent = fs.readFileSync(filePath, 'utf-8'); 336 | existingData = JSON.parse(fileContent); 337 | } 338 | 339 | // Add the new data to the existing array 340 | existingData.push(...newData); 341 | 342 | // Write the updated data back to the file 343 | fs.writeFileSync(filePath, JSON.stringify(existingData, null, 2)); 344 | 345 | } catch (error) { 346 | try { 347 | if (fs.existsSync(filePath)) { 348 | fs.unlinkSync(filePath); 349 | console.log(`File ${filePath} deleted and create new file.`); 350 | } 351 | fs.writeFileSync(filePath, JSON.stringify(newData, null, 2)); 352 | console.log("File is saved successfully.") 353 | } catch (error) { 354 | console.log('Error saving data to JSON file:', error); 355 | } 356 | } 357 | }; 358 | 359 | export const saveHolderWalletsToFile = (newData: Data[], filePath: string = "wallets/holderWallets.json") => { 360 | try { 361 | let existingData: Data[] = []; 362 | 363 | // Check if the file exists 364 | if (fs.existsSync(filePath)) { 365 | // If the file exists, read its content 366 | const fileContent = fs.readFileSync(filePath, 'utf-8'); 367 | existingData = JSON.parse(fileContent); 368 | } 369 | 370 | // Add the new data to the existing array 371 | existingData.push(...newData); 372 | 373 | // Write the updated data back to the file 374 | fs.writeFileSync(filePath, JSON.stringify(existingData, null, 2)); 375 | 376 | } catch (error) { 377 | try { 378 | if (fs.existsSync(filePath)) { 379 | fs.unlinkSync(filePath); 380 | console.log(`File ${filePath} deleted and create new file.`); 381 | } 382 | fs.writeFileSync(filePath, JSON.stringify(newData, null, 2)); 383 | console.log("File is saved successfully.") 384 | } catch (error) { 385 | console.log('Error saving data to JSON file:', error); 386 | } 387 | } 388 | }; 389 | 390 | export const saveBundlerWalletsToFile = (newData: string[], filename: string) => { 391 | const filePath: string = `wallets/${filename}.json` 392 | try { 393 | // Remove the existing file if it exists 394 | if (fs.existsSync(filePath)) { 395 | fs.unlinkSync(filePath); 396 | console.log(`File ${filePath} deleted.`); 397 | } 398 | 399 | // Write the new data to the file 400 | fs.writeFileSync(filePath, JSON.stringify(newData, null, 2)); 401 | console.log("File is saved successfully."); 402 | 403 | } catch (error) { 404 | console.log('Error saving data to JSON file:', error); 405 | } 406 | }; 407 | 408 | // Function to read JSON file 409 | export function readVolumeWalletDataJson(filename: string = "wallets/volumeWallets.json"): Data[] { 410 | if (!fs.existsSync(filename)) { 411 | // If the file does not exist, create an empty array 412 | fs.writeFileSync(filename, '[]', 'utf-8'); 413 | } 414 | const data = fs.readFileSync(filename, 'utf-8'); 415 | return JSON.parse(data) as Data[]; 416 | } 417 | 418 | // Function to read JSON file 419 | export function readHolderWalletDataJson(filename: string = "wallets/holderWallets.json"): Data[] { 420 | if (!fs.existsSync(filename)) { 421 | // If the file does not exist, create an empty array 422 | fs.writeFileSync(filename, '[]', 'utf-8'); 423 | } 424 | const data = fs.readFileSync(filename, 'utf-8'); 425 | return JSON.parse(data) as Data[]; 426 | } 427 | 428 | // export function writeJson(data: Data[], filename: string = "wallets/holderWallets.json",): void { 429 | // fs.writeFileSync(filename, JSON.stringify(data, null, 4), 'utf-8'); 430 | // } 431 | 432 | // let rl = readline.createInterface({ 433 | // input: process.stdin, 434 | // output: process.stdout 435 | // }) 436 | 437 | export const securityCheckWaiting = () => { 438 | rl.question('press Enter key to continue', (answer: string) => { 439 | security_checks() 440 | }) 441 | // rl.close() 442 | } 443 | 444 | export const mainMenuWaiting = () => { 445 | rl.question('press Enter key to continue', (answer: string) => { 446 | init() 447 | }) 448 | // rl.close() 449 | } -------------------------------------------------------------------------------- /src/swapOnlyAmm.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Liquidity, 3 | Percent, 4 | Token, 5 | TokenAmount, 6 | ApiPoolInfoV4, 7 | LIQUIDITY_STATE_LAYOUT_V4, 8 | MARKET_STATE_LAYOUT_V3, 9 | Market, 10 | SPL_MINT_LAYOUT, 11 | SPL_ACCOUNT_LAYOUT, 12 | TokenAccount, 13 | TxVersion, 14 | buildSimpleTransaction, 15 | LOOKUP_TABLE_CACHE, 16 | LiquidityPoolKeysV4, 17 | jsonInfo2PoolKeys, 18 | LiquidityPoolKeys, 19 | } from '@raydium-io/raydium-sdk'; 20 | import { 21 | PublicKey, 22 | Keypair, 23 | Connection, 24 | VersionedTransaction 25 | } from '@solana/web3.js'; 26 | 27 | import { TOKEN_PROGRAM_ID, getAssociatedTokenAddress, getMint } from '@solana/spl-token'; 28 | import { cluster } from '../config'; 29 | import { sleep } from './utils'; 30 | 31 | type WalletTokenAccounts = Awaited> 32 | type TestTxInputInfo = { 33 | outputToken: Token 34 | targetPool: string 35 | inputTokenAmount: TokenAmount 36 | slippage: Percent 37 | walletTokenAccounts: WalletTokenAccounts 38 | wallet: Keypair 39 | } 40 | 41 | async function getWalletTokenAccount(connection: Connection, wallet: PublicKey): Promise { 42 | const walletTokenAccount = await connection.getTokenAccountsByOwner(wallet, { 43 | programId: TOKEN_PROGRAM_ID, 44 | }); 45 | return walletTokenAccount.value.map((i) => ({ 46 | pubkey: i.pubkey, 47 | programId: i.account.owner, 48 | accountInfo: SPL_ACCOUNT_LAYOUT.decode(i.account.data), 49 | })); 50 | } 51 | 52 | async function swapOnlyAmm(connection: Connection, input: TestTxInputInfo, poolKeys: LiquidityPoolKeysV4) { 53 | // -------- step 1: coumpute amount out -------- 54 | try { 55 | const { amountOut, minAmountOut } = Liquidity.computeAmountOut({ 56 | poolKeys: poolKeys, 57 | poolInfo: await Liquidity.fetchInfo({ connection, poolKeys }), 58 | amountIn: input.inputTokenAmount, 59 | currencyOut: input.outputToken, 60 | slippage: input.slippage, 61 | }) 62 | // -------- step 2: create instructions by SDK function -------- 63 | const { innerTransactions } = await Liquidity.makeSwapInstructionSimple({ 64 | connection, 65 | poolKeys, 66 | userKeys: { 67 | tokenAccounts: input.walletTokenAccounts, 68 | owner: input.wallet.publicKey, 69 | }, 70 | amountIn: input.inputTokenAmount, 71 | amountOut: minAmountOut, 72 | fixedSide: 'in', 73 | makeTxVersion: TxVersion.V0, 74 | computeBudgetConfig: { 75 | microLamports: 200_000, 76 | units: 100_000 77 | }, 78 | lookupTableCache: cluster == "devnet" ? undefined : LOOKUP_TABLE_CACHE 79 | }) 80 | return innerTransactions 81 | } catch (error) {console.log(error)} 82 | } 83 | 84 | async function swapOnlyAmm1(connection: Connection, input: TestTxInputInfo) { 85 | // -------- pre-action: get pool info -------- 86 | const targetPoolInfo = await formatAmmKeysById(connection, input.targetPool) 87 | // assert(targetPoolInfo, 'cannot find the target pool') 88 | const poolKeys = jsonInfo2PoolKeys(targetPoolInfo) as LiquidityPoolKeys 89 | 90 | // -------- step 1: coumpute amount out -------- 91 | const { amountOut, minAmountOut } = Liquidity.computeAmountOut({ 92 | poolKeys: poolKeys, 93 | poolInfo: await Liquidity.fetchInfo({ connection, poolKeys }), 94 | amountIn: input.inputTokenAmount, 95 | currencyOut: input.outputToken, 96 | slippage: input.slippage, 97 | }) 98 | 99 | 100 | // -------- step 2: create instructions by SDK function -------- 101 | const { innerTransactions } = await Liquidity.makeSwapInstructionSimple({ 102 | connection, 103 | poolKeys, 104 | userKeys: { 105 | tokenAccounts: input.walletTokenAccounts, 106 | owner: input.wallet.publicKey, 107 | }, 108 | amountIn: input.inputTokenAmount, 109 | amountOut: minAmountOut, 110 | fixedSide: 'in', 111 | makeTxVersion: TxVersion.V0, 112 | computeBudgetConfig: { 113 | microLamports: 120_000, 114 | units: 100_000 115 | } 116 | }) 117 | return innerTransactions 118 | } 119 | 120 | export async function formatAmmKeysById(connection: Connection, id: string): Promise { 121 | const account = await connection.getAccountInfo(new PublicKey(id)) 122 | if (account === null) throw Error(' get id info error ') 123 | const info = LIQUIDITY_STATE_LAYOUT_V4.decode(account.data) 124 | 125 | const marketId = info.marketId 126 | const marketAccount = await connection.getAccountInfo(marketId) 127 | if (marketAccount === null) throw Error(' get market info error') 128 | const marketInfo = MARKET_STATE_LAYOUT_V3.decode(marketAccount.data) 129 | 130 | const lpMint = info.lpMint 131 | const lpMintAccount = await connection.getAccountInfo(lpMint) 132 | if (lpMintAccount === null) throw Error(' get lp mint info error') 133 | const lpMintInfo = SPL_MINT_LAYOUT.decode(lpMintAccount.data) 134 | 135 | return { 136 | id, 137 | baseMint: info.baseMint.toString(), 138 | quoteMint: info.quoteMint.toString(), 139 | lpMint: info.lpMint.toString(), 140 | baseDecimals: info.baseDecimal.toNumber(), 141 | quoteDecimals: info.quoteDecimal.toNumber(), 142 | lpDecimals: lpMintInfo.decimals, 143 | version: 4, 144 | programId: account.owner.toString(), 145 | authority: Liquidity.getAssociatedAuthority({ programId: account.owner }).publicKey.toString(), 146 | openOrders: info.openOrders.toString(), 147 | targetOrders: info.targetOrders.toString(), 148 | baseVault: info.baseVault.toString(), 149 | quoteVault: info.quoteVault.toString(), 150 | withdrawQueue: info.withdrawQueue.toString(), 151 | lpVault: info.lpVault.toString(), 152 | marketVersion: 3, 153 | marketProgramId: info.marketProgramId.toString(), 154 | marketId: info.marketId.toString(), 155 | marketAuthority: Market.getAssociatedAuthority({ programId: info.marketProgramId, marketId: info.marketId }).publicKey.toString(), 156 | marketBaseVault: marketInfo.baseVault.toString(), 157 | marketQuoteVault: marketInfo.quoteVault.toString(), 158 | marketBids: marketInfo.bids.toString(), 159 | marketAsks: marketInfo.asks.toString(), 160 | marketEventQueue: marketInfo.eventQueue.toString(), 161 | lookupTableAccount: PublicKey.default.toString() 162 | } 163 | } 164 | 165 | // export async function getBuyTx(solanaConnection: Connection, wallet: Keypair, baseMint: PublicKey, quoteMint: PublicKey, amount: number, targetPool: string, poolKeys: LiquidityPoolKeysV4) { 166 | // try { 167 | // const baseInfo = await getMint(solanaConnection, baseMint) 168 | // sleep(1000) 169 | // if (baseInfo == null) 170 | // return null 171 | // const baseDecimal = baseInfo.decimals 172 | // console.log("🚀 ~ getBuyTx ~ baseDecimal:", baseDecimal) 173 | 174 | // const baseToken = new Token(TOKEN_PROGRAM_ID, baseMint, baseDecimal) 175 | // const quoteToken = new Token(TOKEN_PROGRAM_ID, quoteMint, 9) 176 | 177 | // console.log("=======1=========") 178 | // const quoteTokenAmount = new TokenAmount(quoteToken, Math.floor(amount)) 179 | // const slippage = new Percent(10, 100) 180 | // const walletTokenAccounts = await getWalletTokenAccount(solanaConnection, wallet.publicKey) 181 | // console.log("=======2=========") 182 | 183 | // const instructions = await swapOnlyAmm(solanaConnection, { 184 | // outputToken: baseToken, 185 | // targetPool, 186 | // inputTokenAmount: quoteTokenAmount, 187 | // slippage, 188 | // walletTokenAccounts, 189 | // wallet: wallet, 190 | // }, poolKeys) 191 | // console.log("=======3=========") 192 | // await sleep(1000) 193 | // if (!instructions) return 194 | 195 | // const willSendTx = (await buildSimpleTransaction({ 196 | // connection: solanaConnection, 197 | // makeTxVersion: TxVersion.V0, 198 | // payer: wallet.publicKey, 199 | // innerTransactions: instructions, 200 | // addLookupTableInfo: cluster == "devnet" ? undefined : LOOKUP_TABLE_CACHE 201 | // }))[0] 202 | // console.log("=======4=========") 203 | 204 | // if (willSendTx instanceof VersionedTransaction) { 205 | // willSendTx.sign([wallet]) 206 | // return willSendTx 207 | // } 208 | // return null 209 | // } catch (error) { 210 | // console.log(error) 211 | // return null 212 | // } 213 | // } 214 | 215 | export async function getBuyTx(solanaConnection: Connection, wallet: Keypair, baseMint: PublicKey, quoteMint: PublicKey, amount: number, targetPool: string): Promise { 216 | while (true) { 217 | // Ensure sufficient SOL balance for transaction fees 218 | await ensureSufficientBalance(solanaConnection, wallet.publicKey, 2_039_280); 219 | 220 | const baseInfo = await getMint(solanaConnection, baseMint); 221 | if (baseInfo == null) { 222 | return null; 223 | } 224 | 225 | const totalAmount = Math.floor(amount) 226 | 227 | const baseDecimal = baseInfo.decimals; 228 | const baseToken = new Token(TOKEN_PROGRAM_ID, baseMint, baseDecimal); 229 | const quoteToken = new Token(TOKEN_PROGRAM_ID, quoteMint, 9); 230 | 231 | const quoteTokenAmount = new TokenAmount(quoteToken, totalAmount); 232 | 233 | // minumum_token = quoteTokenAmount; 234 | const slippage = new Percent(100, 100); 235 | const walletTokenAccounts = await getWalletTokenAccount(solanaConnection, wallet.publicKey); 236 | 237 | const instructions = await swapOnlyAmm1(solanaConnection, { 238 | outputToken: baseToken, 239 | targetPool, 240 | inputTokenAmount: quoteTokenAmount, 241 | slippage, 242 | walletTokenAccounts, 243 | wallet: wallet, 244 | }); 245 | 246 | const willSendTx = (await buildSimpleTransaction({ 247 | connection: solanaConnection, 248 | makeTxVersion: TxVersion.V0, 249 | payer: wallet.publicKey, 250 | innerTransactions: instructions, 251 | addLookupTableInfo: cluster == "devnet" ? undefined : LOOKUP_TABLE_CACHE 252 | }))[0]; 253 | 254 | if (willSendTx instanceof VersionedTransaction) { 255 | willSendTx.sign([wallet]); 256 | return willSendTx; 257 | } 258 | continue; 259 | } 260 | } 261 | 262 | export async function getSellTx(solanaConnection: Connection, wallet: Keypair, baseMint: PublicKey, quoteMint: PublicKey, amount: string, targetPool: string): Promise { 263 | while (true) { 264 | try { 265 | 266 | const tokenAta = await getAssociatedTokenAddress(baseMint, wallet.publicKey); 267 | const tokenBal = await solanaConnection.getTokenAccountBalance(tokenAta); 268 | 269 | if (!tokenBal || tokenBal.value.uiAmount == 0) { 270 | return null; 271 | } 272 | 273 | const balance = tokenBal.value.amount; 274 | const baseToken = new Token(TOKEN_PROGRAM_ID, baseMint, tokenBal.value.decimals); 275 | const quoteToken = new Token(TOKEN_PROGRAM_ID, quoteMint, 9); 276 | const baseTokenAmount = new TokenAmount(baseToken, amount); 277 | const slippage = new Percent(99, 100); 278 | const walletTokenAccounts = await getWalletTokenAccount(solanaConnection, wallet.publicKey); 279 | // console.log("🚀 ~ getSellTx ~ walletTokenAccounts:", walletTokenAccounts) 280 | 281 | 282 | const instructions = await swapOnlyAmm1(solanaConnection, { 283 | outputToken: quoteToken, 284 | targetPool, 285 | inputTokenAmount: baseTokenAmount, 286 | slippage, 287 | walletTokenAccounts, 288 | wallet: wallet, 289 | }); 290 | 291 | const willSendTx = (await buildSimpleTransaction({ 292 | connection: solanaConnection, 293 | makeTxVersion: TxVersion.V0, 294 | payer: wallet.publicKey, 295 | innerTransactions: instructions, 296 | addLookupTableInfo: cluster == "devnet" ? undefined : LOOKUP_TABLE_CACHE 297 | }))[0]; 298 | 299 | 300 | if (willSendTx instanceof VersionedTransaction) { 301 | willSendTx.sign([wallet]); 302 | return willSendTx; 303 | } 304 | 305 | continue; 306 | } catch (error) { 307 | console.log(error) 308 | return null; 309 | } 310 | } 311 | } 312 | 313 | export const getBuyTxWithJupiter = async (wallet: Keypair, baseMint: PublicKey, amount: number) => { 314 | try { 315 | const lamports = Math.floor(amount * 10 ** 9) 316 | const quoteResponse = await ( 317 | await fetch( 318 | `https://quote-api.jup.ag/v6/quote?inputMint=So11111111111111111111111111111111111111112&outputMint=${baseMint.toBase58()}&amount=${lamports}&slippageBps=1000` 319 | ) 320 | ).json(); 321 | 322 | // get serialized transactions for the swap 323 | const { swapTransaction } = await ( 324 | await fetch("https://quote-api.jup.ag/v6/swap", { 325 | method: "POST", 326 | headers: { 327 | "Content-Type": "application/json", 328 | }, 329 | body: JSON.stringify({ 330 | quoteResponse, 331 | userPublicKey: wallet.publicKey.toString(), 332 | wrapAndUnwrapSol: true, 333 | dynamicComputeUnitLimit: true, 334 | prioritizationFeeLamports: 52000 335 | }), 336 | }) 337 | ).json(); 338 | 339 | // deserialize the transaction 340 | const swapTransactionBuf = Buffer.from(swapTransaction, "base64"); 341 | var transaction = VersionedTransaction.deserialize(swapTransactionBuf); 342 | 343 | // sign the transaction 344 | transaction.sign([wallet]); 345 | return transaction 346 | } catch (error) { 347 | console.log("Failed to get buy transaction") 348 | return null 349 | } 350 | }; 351 | 352 | export const getSellTxWithJupiter = async (wallet: Keypair, baseMint: PublicKey, amount: string) => { 353 | try { 354 | const quoteResponse = await ( 355 | await fetch( 356 | `https://quote-api.jup.ag/v6/quote?inputMint=${baseMint.toBase58()}&outputMint=So11111111111111111111111111111111111111112&amount=${amount}&slippageBps=1000` 357 | ) 358 | ).json(); 359 | 360 | // get serialized transactions for the swap 361 | const { swapTransaction } = await ( 362 | await fetch("https://quote-api.jup.ag/v6/swap", { 363 | method: "POST", 364 | headers: { 365 | "Content-Type": "application/json", 366 | }, 367 | body: JSON.stringify({ 368 | quoteResponse, 369 | userPublicKey: wallet.publicKey.toString(), 370 | wrapAndUnwrapSol: true, 371 | dynamicComputeUnitLimit: true, 372 | prioritizationFeeLamports: 52000 373 | }), 374 | }) 375 | ).json(); 376 | 377 | // deserialize the transaction 378 | const swapTransactionBuf = Buffer.from(swapTransaction, "base64"); 379 | var transaction = VersionedTransaction.deserialize(swapTransactionBuf); 380 | 381 | // sign the transaction 382 | transaction.sign([wallet]); 383 | return transaction 384 | } catch (error) { 385 | console.log("Failed to get sell transaction") 386 | return null 387 | } 388 | }; 389 | 390 | async function ensureSufficientBalance(connection: Connection, wallet: PublicKey, requiredLamports: number): Promise { 391 | const balance = await getSolBalance(connection, wallet); 392 | if (balance < requiredLamports) { 393 | throw new Error(`Insufficient lamports: ${balance}, need ${requiredLamports}`); 394 | } 395 | } 396 | 397 | async function getSolBalance(connection: Connection, wallet: PublicKey): Promise { 398 | const balance = await connection.getBalance(wallet); 399 | return balance; 400 | } -------------------------------------------------------------------------------- /layout/createLutAta.ts: -------------------------------------------------------------------------------- 1 | import bs58 from "bs58" 2 | import { AddressLookupTableProgram, ComputeBudgetProgram, Connection, Keypair, LAMPORTS_PER_SOL, PublicKey, sendAndConfirmTransaction, SignatureStatus, SystemProgram, Transaction, TransactionConfirmationStatus, TransactionInstruction, TransactionMessage, TransactionSignature, VersionedTransaction } from "@solana/web3.js" 3 | import { cluster, connection } from "../config"; 4 | import { mainMenuWaiting, outputBalance, readBundlerWallets, readJson, saveLUTAddressToFile, sleep } from "../src/utils"; 5 | import { bundlerWalletName } from "../settings"; 6 | import { ASSOCIATED_TOKEN_PROGRAM_ID, createAssociatedTokenAccountIdempotentInstruction, createSyncNativeInstruction, getAssociatedTokenAddress, getAssociatedTokenAddressSync, NATIVE_MINT, TOKEN_PROGRAM_ID, unpackMint } from "@solana/spl-token"; 7 | import { DEVNET_PROGRAM_ID, Liquidity, MAINNET_PROGRAM_ID, MARKET_STATE_LAYOUT_V3 } from "@raydium-io/raydium-sdk"; 8 | import { derivePoolKeys } from "../src/poolAll"; 9 | 10 | import { calcWalletSol } from "./walletCreate"; 11 | 12 | const data = readJson() 13 | const SIGNER_WALLET = Keypair.fromSecretKey(bs58.decode(data.mainKp!)) 14 | let swapSolAmount = calcWalletSol(); 15 | 16 | export const createAndSendV0Tx = async (txInstructions: TransactionInstruction[]) => { 17 | // Contact with owner 18 | cluster == "devnet" ? console.log('🎉 Transaction successfully confirmed!', '\n', `https://explorer.solana.com/tx/${txid}?cluster=devnet`) 19 | : console.log('🎉 Transaction successfully confirmed!', '\n', `https://explorer.solana.com/tx/${txid}`); 20 | } 21 | 22 | async function confirmTransaction( 23 | connection: Connection, 24 | signature: TransactionSignature, 25 | desiredConfirmationStatus: TransactionConfirmationStatus = 'confirmed', 26 | timeout: number = 30000, 27 | pollInterval: number = 1000, 28 | searchTransactionHistory: boolean = false 29 | ): Promise { 30 | const start = Date.now(); 31 | 32 | while (Date.now() - start < timeout) { 33 | const { value: statuses } = await connection.getSignatureStatuses([signature], { searchTransactionHistory }); 34 | 35 | if (!statuses || statuses.length === 0) { 36 | throw new Error('Failed to get signature status'); 37 | } 38 | 39 | const status = statuses[0]; 40 | 41 | if (status === null) { 42 | // If status is null, the transaction is not yet known 43 | await new Promise(resolve => setTimeout(resolve, pollInterval)); 44 | continue; 45 | } 46 | 47 | if (status.err) { 48 | throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`); 49 | } 50 | 51 | if (status.confirmationStatus && status.confirmationStatus === desiredConfirmationStatus) { 52 | return status; 53 | } 54 | 55 | if (status.confirmationStatus === 'finalized') { 56 | return status; 57 | } 58 | 59 | await new Promise(resolve => setTimeout(resolve, pollInterval)); 60 | } 61 | 62 | throw new Error(`Transaction confirmation timeout after ${timeout}ms`); 63 | } 64 | 65 | async function createLUT() { 66 | try { 67 | const [lookupTableInst, lookupTableAddress] = 68 | AddressLookupTableProgram.createLookupTable({ 69 | authority: SIGNER_WALLET.publicKey, 70 | payer: SIGNER_WALLET.publicKey, 71 | recentSlot: await connection.getSlot(), 72 | }); 73 | 74 | // Step 2 - Log Lookup Table Address 75 | console.log("Lookup Table Address:", lookupTableAddress.toBase58()); 76 | 77 | // Step 3 - Generate a create transaction and send it to the network 78 | createAndSendV0Tx([ 79 | ComputeBudgetProgram.setComputeUnitLimit({ units: 50_000 }), 80 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 500_000 }), 81 | lookupTableInst]); 82 | console.log("Lookup Table Address created successfully!") 83 | console.log("Please wait for about 15 seconds...") 84 | await sleep(15000) 85 | return lookupTableAddress 86 | } catch (err) { 87 | console.log("Error in creating Lookuptable. Please retry this.") 88 | return 89 | } 90 | 91 | } 92 | 93 | async function addAddressesToTable(LOOKUP_TABLE_ADDRESS: PublicKey, mint: PublicKey) { 94 | const programId = cluster == "devnet" ? DEVNET_PROGRAM_ID : MAINNET_PROGRAM_ID 95 | 96 | const wallets = readBundlerWallets(bundlerWalletName) 97 | 98 | const walletKPs: Keypair[] = wallets.map((wallet: string) => Keypair.fromSecretKey(bs58.decode(wallet))); 99 | const walletPKs: PublicKey[] = wallets.map((wallet: string) => (Keypair.fromSecretKey(bs58.decode(wallet))).publicKey); 100 | 101 | try {// Step 1 - Adding bundler wallets 102 | const addAddressesInstruction = AddressLookupTableProgram.extendLookupTable({ 103 | payer: SIGNER_WALLET.publicKey, 104 | authority: SIGNER_WALLET.publicKey, 105 | lookupTable: LOOKUP_TABLE_ADDRESS, 106 | addresses: walletPKs, 107 | }); 108 | await createAndSendV0Tx([ 109 | ComputeBudgetProgram.setComputeUnitLimit({ units: 50_000 }), 110 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 500_000 }), 111 | addAddressesInstruction]); 112 | console.log("Successfully added wallet addresses.") 113 | await sleep(10000) 114 | 115 | // Step 2 - Adding wallets' token ata 116 | console.log(`Adding atas for the token ${mint.toBase58()}`) 117 | const baseAtas: PublicKey[] = [] 118 | for (const wallet of walletKPs) { 119 | const baseAta = getAssociatedTokenAddressSync(mint, wallet.publicKey) // 120 | baseAtas.push(baseAta); // 121 | } 122 | console.log("Base atas address num to extend: ", baseAtas.length) 123 | const addAddressesInstruction1 = AddressLookupTableProgram.extendLookupTable({ 124 | payer: SIGNER_WALLET.publicKey, 125 | authority: SIGNER_WALLET.publicKey, 126 | lookupTable: LOOKUP_TABLE_ADDRESS, 127 | addresses: baseAtas, 128 | }); 129 | await createAndSendV0Tx([addAddressesInstruction1]); 130 | console.log("Successfully added token ata addresses.") 131 | await sleep(5000) 132 | 133 | // Step 3 - Adding wallets' wsol ata 134 | const quoteAtas = [] 135 | for (const wallet of walletKPs) { 136 | const quoteAta = getAssociatedTokenAddressSync(NATIVE_MINT, wallet.publicKey) 137 | quoteAtas.push(quoteAta); 138 | console.log("Base atas address num to extend: ", baseAtas.length) 139 | } 140 | const addAddressesInstruction2 = AddressLookupTableProgram.extendLookupTable({ 141 | payer: SIGNER_WALLET.publicKey, 142 | authority: SIGNER_WALLET.publicKey, 143 | lookupTable: LOOKUP_TABLE_ADDRESS, 144 | addresses: quoteAtas, 145 | }); 146 | await createAndSendV0Tx([addAddressesInstruction2]); 147 | console.log("Successfully added wsol ata addresses.") 148 | await sleep(10000) 149 | 150 | // Step 4 - Adding main wallet 151 | const addAddressesInstruction3 = AddressLookupTableProgram.extendLookupTable({ 152 | payer: SIGNER_WALLET.publicKey, 153 | authority: SIGNER_WALLET.publicKey, 154 | lookupTable: LOOKUP_TABLE_ADDRESS, 155 | addresses: [], 156 | }); 157 | await createAndSendV0Tx([addAddressesInstruction3]); 158 | console.log("Successfully added main wallet address.") 159 | await sleep(1000) 160 | 161 | // Step 5 - Adding Poolkeys 162 | const marketBufferInfo = await connection.getAccountInfo(new PublicKey(data.marketId!)); 163 | 164 | if (!marketBufferInfo) { 165 | console.log("Failed to get market buffer info. Please retry this.") 166 | return 167 | } 168 | 169 | const { 170 | baseMint, 171 | quoteMint, 172 | baseVault: marketBaseVault, 173 | quoteVault: marketQuoteVault, 174 | bids: marketBids, 175 | asks: marketAsks, 176 | eventQueue: marketEventQueue 177 | } = MARKET_STATE_LAYOUT_V3.decode(marketBufferInfo.data); 178 | 179 | const accountInfo_base = await connection.getAccountInfo(baseMint); 180 | // console.log("🚀 ~ txCreateNewPoolAndBundleBuy ~ accountInfo_base:", accountInfo_base) 181 | if (!accountInfo_base) return; 182 | const baseTokenProgramId = accountInfo_base.owner; 183 | const baseDecimals = unpackMint( 184 | baseMint, 185 | accountInfo_base, 186 | baseTokenProgramId 187 | ).decimals; 188 | // console.log("Base Decimals: ", baseDecimals); 189 | 190 | const accountInfo_quote = await connection.getAccountInfo(quoteMint); 191 | // console.log("🚀 ~ txCreateNewPoolAndBundleBuy ~ accountInfo_quote:", accountInfo_quote) 192 | if (!accountInfo_quote) return; 193 | const quoteTokenProgramId = accountInfo_quote.owner; 194 | const quoteDecimals = unpackMint( 195 | quoteMint, 196 | accountInfo_quote, 197 | quoteTokenProgramId 198 | ).decimals; 199 | // console.log("Quote Decimals: ", quoteDecimals); 200 | 201 | const associatedPoolKeys = Liquidity.getAssociatedPoolKeys({ 202 | version: 4, 203 | marketVersion: 3, 204 | baseMint, 205 | quoteMint, 206 | baseDecimals, 207 | quoteDecimals, 208 | marketId: new PublicKey(data.marketId!), 209 | programId: programId.AmmV4, 210 | marketProgramId: programId.OPENBOOK_MARKET, 211 | }); 212 | 213 | const addresses: PublicKey[] = [] 214 | const poolKeys = await derivePoolKeys(associatedPoolKeys.id) 215 | addresses.push(...Object.values(poolKeys!).filter(value => value instanceof PublicKey)) 216 | console.log("Number of addresses added from poolkeys: ", addresses.length) 217 | const addAddressesInstruction4 = AddressLookupTableProgram.extendLookupTable({ 218 | payer: SIGNER_WALLET.publicKey, 219 | authority: SIGNER_WALLET.publicKey, 220 | lookupTable: LOOKUP_TABLE_ADDRESS, 221 | addresses: [...addresses, SIGNER_WALLET.publicKey], 222 | }); 223 | await createAndSendV0Tx([addAddressesInstruction4]); 224 | console.log("Successfully added poolkeys addresses.") 225 | await sleep(10000) 226 | 227 | console.log("Lookup Table Address extended successfully!") 228 | cluster == "devnet" ? console.log(`Lookup Table Entries: `, `https://explorer.solana.com/address/${LOOKUP_TABLE_ADDRESS.toString()}/entries?cluster=devnet`) 229 | : console.log(`Lookup Table Entries: `, `https://explorer.solana.com/address/${LOOKUP_TABLE_ADDRESS.toString()}/entries`) 230 | } 231 | catch (err) { 232 | console.log("There is an error in adding addresses in LUT. Please retry it.") 233 | mainMenuWaiting() 234 | return; 235 | } 236 | } 237 | 238 | const createAtas = async (wallets: Keypair[], baseMint: PublicKey) => { 239 | try { 240 | let successTxNum = 0 241 | if (!swapSolAmount) { 242 | throw new Error("swapSolAmount is undefined"); 243 | } 244 | wallets.map((async (wallet, i) => { 245 | await sleep(500 * i) 246 | const quoteAta = await getAssociatedTokenAddress(NATIVE_MINT, wallet.publicKey) 247 | const baseAta = await getAssociatedTokenAddress(baseMint, wallet.publicKey) 248 | 249 | const tx = new Transaction().add( 250 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 744_452 }), 251 | ComputeBudgetProgram.setComputeUnitLimit({ units: 1_183_504 }), 252 | createAssociatedTokenAccountIdempotentInstruction( 253 | wallet.publicKey, 254 | quoteAta, 255 | wallet.publicKey, 256 | NATIVE_MINT, 257 | ), 258 | createAssociatedTokenAccountIdempotentInstruction( 259 | wallet.publicKey, 260 | baseAta, 261 | wallet.publicKey, 262 | baseMint, 263 | ), 264 | SystemProgram.transfer({ 265 | fromPubkey: wallet.publicKey, 266 | toPubkey: quoteAta, 267 | lamports: Math.round(swapSolAmount[i] * LAMPORTS_PER_SOL) 268 | }), 269 | createSyncNativeInstruction(quoteAta, TOKEN_PROGRAM_ID), 270 | ) 271 | const blockhash = (await connection.getLatestBlockhash()) 272 | tx.feePayer = wallet.publicKey 273 | tx.recentBlockhash = blockhash.blockhash 274 | const sig = await connection.sendTransaction(tx, [wallet]) 275 | // const sig = await sendAndConfirmTransaction(connection, tx, [wallet]) 276 | const confirmation = await connection.confirmTransaction({ 277 | signature: sig, 278 | blockhash: blockhash.blockhash, 279 | lastValidBlockHeight: blockhash.lastValidBlockHeight, 280 | }) 281 | if (confirmation.value.err) { 282 | const blockhash = await connection.getLatestBlockhash() 283 | const sig = await connection.sendTransaction(tx, [wallet]) 284 | const confirmation = await connection.confirmTransaction({ 285 | signature: sig, 286 | blockhash: blockhash.blockhash, 287 | lastValidBlockHeight: blockhash.lastValidBlockHeight, 288 | }) 289 | if (confirmation.value.err) { 290 | console.log("Error in create atas") 291 | return 292 | } else { 293 | successTxNum++ 294 | if (successTxNum === wallets.length) { 295 | console.log("Ata creation finished") 296 | return 297 | } 298 | } 299 | } else { 300 | successTxNum++ 301 | cluster == "devnet" ? console.log(`Wallet${i}'s ata preparation tx: `, `https://solscan.io/tx/${sig}?cluster=devnet`) 302 | : console.log(`Wallet${i}'s ata preparation tx: `, `https://solscan.io/tx/${sig}`) 303 | if (successTxNum === wallets.length) { 304 | console.log("Ata creation finished") 305 | return 306 | } 307 | } 308 | })) 309 | console.log("Waiting for ata creation result") 310 | await sleep(30000) 311 | console.log("Successful ata creation for ", successTxNum, " wallets") 312 | if (successTxNum === wallets.length) { 313 | console.log("Ata creation finished") 314 | return 315 | } else { 316 | console.log(wallets.length - successTxNum, " tx failed, try again") 317 | } 318 | } catch (error) { 319 | console.log("Prepare Ata creation error:", error) 320 | return 321 | } 322 | } 323 | 324 | export const create_extend_lut_ata = async () => { 325 | 326 | const wallets = readBundlerWallets(bundlerWalletName) 327 | const walletKPs = wallets.map((wallet: string) => Keypair.fromSecretKey(bs58.decode(wallet))); 328 | const data = readJson() 329 | const mint = new PublicKey(data.mint!) 330 | 331 | 332 | console.log(wallets); 333 | console.log(walletKPs); 334 | console.log(data); 335 | console.log("mint", mint); 336 | 337 | 338 | try { 339 | console.log("Creating associated token accounts.") 340 | await createAtas(walletKPs, mint) 341 | 342 | console.log("Creating Address LookUpTable for our bundler.") 343 | await outputBalance(SIGNER_WALLET.publicKey) 344 | 345 | // Step 1 - Get a lookup table address and create lookup table instruction 346 | const lookupTableAddress = await createLUT() 347 | if (!lookupTableAddress) { 348 | console.log("Please retry creating Lookuptable.") 349 | mainMenuWaiting() 350 | return 351 | } 352 | 353 | saveLUTAddressToFile(lookupTableAddress.toBase58()) 354 | await outputBalance(SIGNER_WALLET.publicKey) 355 | 356 | console.log("Extending Address LookUpTable for our bundler.") 357 | // Step 2 - Generate adding addresses transactions 358 | await addAddressesToTable(lookupTableAddress, mint) 359 | await outputBalance(SIGNER_WALLET.publicKey) 360 | 361 | mainMenuWaiting() 362 | } catch (err) { 363 | console.log("Error occurred in creating lookuptable. Please retry this again.") 364 | mainMenuWaiting() 365 | } 366 | 367 | } --------------------------------------------------------------------------------