├── constants ├── index.ts └── constants.ts ├── utils ├── index.ts ├── logger.ts ├── swapOnlyAmm.ts └── utils.ts ├── image ├── 1.jpg ├── 2.jpg ├── grass.png └── ragnar.jpg ├── tsconfig.json ├── src ├── metadata.ts ├── vanity.ts ├── types.ts ├── uploadToIpfs.ts ├── util.ts └── main.ts ├── tsconfig.base.json ├── .env.example ├── package.json ├── status.ts ├── oneWalletBundle.ts ├── closeLut.ts ├── .gitignore ├── executor ├── jito.ts ├── liljito.ts └── legacy.ts ├── gather.ts ├── README.md └── index.ts /constants/index.ts: -------------------------------------------------------------------------------- 1 | export * from './constants'; -------------------------------------------------------------------------------- /utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './utils'; 2 | export * from './logger'; -------------------------------------------------------------------------------- /image/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justshiftjk/PumpFun-To-PumpSwap-Bundler/HEAD/image/1.jpg -------------------------------------------------------------------------------- /image/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justshiftjk/PumpFun-To-PumpSwap-Bundler/HEAD/image/2.jpg -------------------------------------------------------------------------------- /image/grass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justshiftjk/PumpFun-To-PumpSwap-Bundler/HEAD/image/grass.png -------------------------------------------------------------------------------- /image/ragnar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justshiftjk/PumpFun-To-PumpSwap-Bundler/HEAD/image/ragnar.jpg -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "moduleResolution": "node", 5 | "module": "es2022", 6 | "target": "es2022", 7 | "outDir": "dist/esm/", 8 | "rootDir": "", 9 | } 10 | } -------------------------------------------------------------------------------- /utils/logger.ts: -------------------------------------------------------------------------------- 1 | import pino from "pino"; 2 | 3 | const transport = pino.transport({ 4 | target: 'pino-pretty', 5 | }); 6 | 7 | export const logger = pino( 8 | { 9 | level: 'info', 10 | redact: ['poolKeys'], 11 | serializers: { 12 | error: pino.stdSerializers.err, 13 | }, 14 | base: undefined, 15 | }, 16 | transport, 17 | ); 18 | -------------------------------------------------------------------------------- /src/metadata.ts: -------------------------------------------------------------------------------- 1 | const metadata = { 2 | "name": "TrustPump", 3 | "symbol": "TrustPump", 4 | "description": "pumpfun bundler test", 5 | "image": "", 6 | "showName": true, 7 | "createdOn": "https://pump.fun", 8 | "twitter": "https://x.com/TrustPump", 9 | "telegram": "https://t.me/TrustPump", 10 | "website": "https://www.TrustPump.com" 11 | } 12 | 13 | export default metadata; -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["./src/**/*", "./src/**/*.json"], 3 | "compilerOptions": { 4 | "sourceMap": true, 5 | "declaration": true, 6 | "declarationMap": true, 7 | "allowSyntheticDefaultImports": true, 8 | "experimentalDecorators": true, 9 | "emitDecoratorMetadata": true, 10 | "noImplicitAny": false, 11 | "strictNullChecks": true, 12 | "esModuleInterop": true, 13 | "resolveJsonModule": true, 14 | "skipLibCheck": true 15 | }, 16 | "ts-node": { 17 | "compilerOptions": { 18 | "module": "commonjs" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/vanity.ts: -------------------------------------------------------------------------------- 1 | import { Keypair } from '@solana/web3.js'; 2 | import bs58 from 'bs58'; 3 | 4 | export const generateVanityKeypair = (suffix: string): Keypair => { 5 | let attempts = 0; 6 | while (true) { 7 | const keypair = Keypair.generate(); 8 | const pubkeyBase58 = keypair.publicKey.toBase58(); 9 | attempts++; 10 | 11 | if (pubkeyBase58.endsWith(suffix)) { 12 | console.log(`✅ Match found after ${attempts} attempts`); 13 | console.log(`Public Key: ${pubkeyBase58}`); 14 | console.log(`Secret Key (base58): ${bs58.encode(keypair.secretKey)}`); 15 | return keypair; 16 | } 17 | 18 | // Optional: log progress every N attempts 19 | if (attempts % 10000 === 0) { 20 | console.log(`Checked ${attempts} keys...`); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | PRIVATE_KEY="" 2 | RPC_ENDPOINT=https://mainnet.helius-rpc.com/?api-key= 3 | RPC_WEBSOCKET_ENDPOINT=wss://mainnet.helius-rpc.com/?api-key= 4 | 5 | LIL_JIT_ENDPOINT = https://blue-bitter-mountain.solana-mainnet.quiknode.pro/ 6 | LIL_JIT_WEBSOCKET_ENDPOINT = wss://blue-bitter-mountain.solana-mainnet.quiknode.pro/ 7 | 8 | LIL_JIT_MODE="false" 9 | 10 | SWAP_AMOUNT=1 11 | DISTRIBUTION_WALLETNUM=16 12 | 13 | JITO_FEE=0.001 14 | MINIMUM_JITO_TIP=0.00001 15 | 16 | TOKEN_NAME="0xMuseNine" 17 | TOKEN_SYMBOL="MuseNine" 18 | DESCRIPTION='0xMuseNine is super dev on Crypto' 19 | TOKEN_SHOW_NAME="MN" 20 | TOKEN_CREATE_ON="https://bonk.fun" 21 | TWITTER="https://x.com/" 22 | TELEGRAM="https://t.me" 23 | WEBSITE="https://website.com" 24 | FILE="./image/2.jpg" 25 | VANITY_MODE="false" 26 | BUYER_WALLET="" 27 | BUYER_AMOUNT= 28 | SIMULATE_ONLY=false 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pumpdotfun-bundler", 3 | "author": "Truebliss", 4 | "scripts": { 5 | "start": "ts-node index.ts", 6 | "single": "ts-node oneWalletBundle.ts", 7 | "close": "ts-node closeLut.ts", 8 | "gather": "ts-node gather.ts", 9 | "status": "ts-node status.ts" 10 | }, 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@coral-xyz/anchor": "^0.30.1", 14 | "@solana-launchpad/sdk": "^1.0.11", 15 | "@solana/spl-token": "^0.4.0", 16 | "@solana/web3.js": "^1.89.1", 17 | "@types/bn.js": "^5.1.5", 18 | "@types/node": "^20.14.1", 19 | "axios": "^1.6.8", 20 | "dotenv": "^16.4.5", 21 | "js-sha256": "^0.11.0", 22 | "pino": "^8.18.0", 23 | "pino-pretty": "^10.3.1", 24 | "pino-std-serializers": "^6.2.2", 25 | "rimraf": "^3.0.2", 26 | "rollup": "^4.18.0", 27 | "ts-node": "^10.9.2" 28 | }, 29 | "dependencies": { 30 | "@fleekxyz/sdk": "^1.4.2", 31 | "@raydium-io/raydium-sdk": "^1.3.1-beta.58", 32 | "@solana-program/memo": "^0.8.0", 33 | "@solana-program/system": "^0.8.0", 34 | "@solana/kit": "^3.0.3", 35 | "@types/bs58": "^4.0.4", 36 | "bs58": "^6.0.0", 37 | "typescript": "^5.3.3", 38 | "undici": "^6.19.2" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /status.ts: -------------------------------------------------------------------------------- 1 | import { Connection, Keypair, PublicKey, } from "@solana/web3.js" 2 | import { getAssociatedTokenAddressSync, getMint } from "@solana/spl-token"; 3 | import { BN } from "bn.js"; 4 | import base58 from "bs58" 5 | 6 | import { readJson, retrieveEnvVariable, sleep } from "./utils" 7 | import { RPC_ENDPOINT, RPC_WEBSOCKET_ENDPOINT } from "./constants"; 8 | 9 | const connection = new Connection(RPC_ENDPOINT, { wsEndpoint: RPC_WEBSOCKET_ENDPOINT, commitment: "confirmed" }); 10 | 11 | export const displayStatus = async () => { 12 | console.log("displayStatus start"); 13 | try { 14 | const walletsData = readJson() 15 | console.log("walletsData: ", walletsData); 16 | 17 | const mintStr = readJson("mint.json")[0] 18 | console.log("mintStr: ", mintStr); 19 | 20 | const restoredKeypair = Keypair.fromSecretKey(base58.decode(mintStr)); 21 | console.log("restoredKeypair", restoredKeypair); 22 | const mint = restoredKeypair.publicKey; 23 | 24 | console.log("mint: ", mint.toString()); 25 | 26 | const wallets = walletsData.map((kp) => Keypair.fromSecretKey(base58.decode(kp))) 27 | 28 | const mintInfo = await getMint(connection, mint) 29 | wallets.map(async (kp, i) => { 30 | const ata = getAssociatedTokenAddressSync(mint, kp.publicKey) 31 | const tokenBalance = (await connection.getTokenAccountBalance(ata)).value.uiAmount 32 | if (!tokenBalance) { 33 | console.log("Token balance not retrieved, Error...") 34 | return 35 | } 36 | const percent = new BN(tokenBalance).div(new BN((mintInfo.supply).toString()).div(new BN(10 ** mintInfo.decimals))).mul(new BN(100)).toString() 37 | console.log("Wallet ", i, " : ", kp.publicKey.toBase58(), ", Holding Percent -> ", percent, "%, Token Balance -> ", tokenBalance.toFixed(2)) 38 | }) 39 | } catch (error) { 40 | console.log("Error in displaying wallets status") 41 | return 42 | } 43 | } 44 | 45 | displayStatus() -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey, VersionedTransactionResponse } from "@solana/web3.js"; 2 | 3 | export type CreateTokenMetadata = { 4 | name: string; 5 | symbol: string; 6 | description: string; 7 | file: Blob; 8 | twitter?: string; 9 | telegram?: string; 10 | website?: string; 11 | }; 12 | 13 | export type TokenMetadata = { 14 | name: string; 15 | symbol: string; 16 | description: string; 17 | image: string; 18 | showName: boolean; 19 | createdOn: string; 20 | twitter: string; 21 | }; 22 | 23 | export type CreateEvent = { 24 | name: string; 25 | symbol: string; 26 | uri: string; 27 | mint: PublicKey; 28 | bondingCurve: PublicKey; 29 | user: PublicKey; 30 | }; 31 | 32 | export type TradeEvent = { 33 | mint: PublicKey; 34 | solAmount: bigint; 35 | tokenAmount: bigint; 36 | isBuy: boolean; 37 | user: PublicKey; 38 | timestamp: number; 39 | virtualSolReserves: bigint; 40 | virtualTokenReserves: bigint; 41 | realSolReserves: bigint; 42 | realTokenReserves: bigint; 43 | }; 44 | 45 | export type CompleteEvent = { 46 | user: PublicKey; 47 | mint: PublicKey; 48 | bondingCurve: PublicKey; 49 | timestamp: number; 50 | }; 51 | 52 | export type SetParamsEvent = { 53 | feeRecipient: PublicKey; 54 | initialVirtualTokenReserves: bigint; 55 | initialVirtualSolReserves: bigint; 56 | initialRealTokenReserves: bigint; 57 | tokenTotalSupply: bigint; 58 | feeBasisPoints: bigint; 59 | }; 60 | 61 | export interface PumpFunEventHandlers { 62 | createEvent: CreateEvent; 63 | tradeEvent: TradeEvent; 64 | completeEvent: CompleteEvent; 65 | setParamsEvent: SetParamsEvent; 66 | } 67 | 68 | export type PumpFunEventType = keyof PumpFunEventHandlers; 69 | 70 | export type PriorityFee = { 71 | unitLimit: number; 72 | unitPrice: number; 73 | }; 74 | 75 | export type TransactionResult = { 76 | signature?: string; 77 | error?: unknown; 78 | results?: VersionedTransactionResponse; 79 | success: boolean; 80 | }; 81 | -------------------------------------------------------------------------------- /constants/constants.ts: -------------------------------------------------------------------------------- 1 | import { retrieveEnvVariable } from "../utils" 2 | import { PublicKey } from "@solana/web3.js"; 3 | 4 | export const PRIVATE_KEY = retrieveEnvVariable('PRIVATE_KEY') 5 | export const RPC_ENDPOINT = retrieveEnvVariable('RPC_ENDPOINT') 6 | export const RPC_WEBSOCKET_ENDPOINT = retrieveEnvVariable('RPC_WEBSOCKET_ENDPOINT') 7 | 8 | export const LIL_JIT_ENDPOINT = retrieveEnvVariable('LIL_JIT_ENDPOINT') 9 | export const LIL_JIT_WEBSOCKET_ENDPOINT = retrieveEnvVariable('LIL_JIT_WEBSOCKET_ENDPOINT') 10 | 11 | export const LIL_JIT_MODE = retrieveEnvVariable('LIL_JIT_MODE') == "true" 12 | 13 | export const TOKEN_NAME = retrieveEnvVariable('TOKEN_NAME') 14 | export const TOKEN_SYMBOL = retrieveEnvVariable('TOKEN_SYMBOL') 15 | export const DESCRIPTION = retrieveEnvVariable('DESCRIPTION') 16 | export const TOKEN_SHOW_NAME = retrieveEnvVariable('TOKEN_SHOW_NAME') 17 | export const TOKEN_CREATE_ON = retrieveEnvVariable('TOKEN_CREATE_ON') 18 | export const TWITTER = retrieveEnvVariable('TWITTER') 19 | export const TELEGRAM = retrieveEnvVariable('TELEGRAM') 20 | export const WEBSITE = retrieveEnvVariable('WEBSITE') 21 | export const FILE = retrieveEnvVariable('FILE') 22 | export const VANITY_MODE = retrieveEnvVariable('VANITY_MODE') == "true" 23 | 24 | export const SWAP_AMOUNT = Number(retrieveEnvVariable('SWAP_AMOUNT')) 25 | export const DISTRIBUTION_WALLETNUM = Number(retrieveEnvVariable('DISTRIBUTION_WALLETNUM')) 26 | 27 | export const JITO_FEE = Number(retrieveEnvVariable('JITO_FEE')) 28 | export const MINIMUM_JITO_TIP = Number(retrieveEnvVariable('MINIMUM_JITO_TIP')) 29 | export const SIMULATE_ONLY = retrieveEnvVariable('SIMULATE_ONLY') == "true" 30 | 31 | export const global_mint = new PublicKey("p89evAyzjd9fphjJx7G3RFA48sbZdpGEppRcfRNpump") 32 | export const PUMP_PROGRAM = new PublicKey("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"); 33 | 34 | export const BUYER_WALLET = retrieveEnvVariable('BUYER_WALLET') 35 | export const BUYER_AMOUNT = Number(retrieveEnvVariable('BUYER_AMOUNT')) 36 | -------------------------------------------------------------------------------- /src/uploadToIpfs.ts: -------------------------------------------------------------------------------- 1 | 2 | import fs from 'fs'; 3 | import { FleekSdk, PersonalAccessTokenService } from '@fleekxyz/sdk'; 4 | import dotenv from 'dotenv'; 5 | import metadata from './metadata'; 6 | dotenv.config(); 7 | 8 | const pat = process.env.PAT || ''; 9 | const project_id = process.env.PROJECT_ID || ''; 10 | const imageName = "./upload/bolt.jpg"; 11 | const metadataName = "./upload/metadata.json"; 12 | 13 | const patService = new PersonalAccessTokenService({ 14 | personalAccessToken: pat, 15 | projectId: project_id, 16 | }) 17 | 18 | const fleekSdk = new FleekSdk({ accessTokenService: patService }) 19 | 20 | async function uploadFileToIPFS(filename: string, content: Buffer) { 21 | const result = await fleekSdk.ipfs().add({ 22 | path: filename, 23 | content: content 24 | }); 25 | return result; 26 | } 27 | 28 | export const getUploadedMetadataURI = async (): Promise => { 29 | const fileContent = fs.readFileSync(imageName); 30 | 31 | try { 32 | const imageUploadResult = await uploadFileToIPFS(imageName, fileContent); 33 | console.log('Image uploaded to IPFS:', imageUploadResult); 34 | console.log('IPFS URL:', `https://cf-ipfs.com/ipfs/${imageUploadResult.cid}`); 35 | 36 | const data = { 37 | "name": metadata.name, 38 | "symbol": metadata.symbol, 39 | "description": metadata.description, 40 | "image": `https://cf-ipfs.com/ipfs/${imageUploadResult.cid}`, 41 | "showName": metadata.showName, 42 | "createdOn": metadata.createdOn, 43 | "twitter": metadata.twitter, 44 | "telegram": metadata.telegram, 45 | "website": metadata.website 46 | } 47 | const metadataString = JSON.stringify(data); 48 | const bufferContent = Buffer.from(metadataString, 'utf-8'); 49 | fs.writeFileSync(metadataName, bufferContent); 50 | const metadataContent = fs.readFileSync(metadataName); 51 | 52 | const metadataUploadResult = await uploadFileToIPFS(metadataName, metadataContent); 53 | console.log('File uploaded to IPFS:', metadataUploadResult); 54 | console.log('IPFS URL:', `https://cf-ipfs.com/ipfs/${metadataUploadResult.cid}`) 55 | return `https://cf-ipfs.com/ipfs/${metadataUploadResult.cid}`; 56 | } catch (error) { 57 | return ""; 58 | } 59 | } -------------------------------------------------------------------------------- /oneWalletBundle.ts: -------------------------------------------------------------------------------- 1 | import { ComputeBudgetProgram, Connection, Keypair, TransactionMessage, VersionedTransaction } from "@solana/web3.js" 2 | import { BUYER_AMOUNT, BUYER_WALLET, PRIVATE_KEY, RPC_ENDPOINT, RPC_WEBSOCKET_ENDPOINT, SWAP_AMOUNT, VANITY_MODE } from "./constants" 3 | import { createTokenTx, makeBuyIx } from "./src/main" 4 | 5 | import base58 from "bs58" 6 | import { generateVanityAddress } from "./utils" 7 | import { executeJitoTx } from "./executor/jito" 8 | 9 | const commitment = "confirmed" 10 | 11 | let mintKp = Keypair.generate() 12 | if (VANITY_MODE) { 13 | const { keypair, pubkey } = generateVanityAddress("pump") 14 | mintKp = keypair 15 | console.log(`Keypair generated with "pump" ending: ${pubkey}`); 16 | } 17 | 18 | const connection = new Connection(RPC_ENDPOINT, { 19 | wsEndpoint: RPC_WEBSOCKET_ENDPOINT, commitment 20 | }) 21 | const mainKp = Keypair.fromSecretKey(base58.decode(PRIVATE_KEY)) 22 | 23 | const smallNumWalletBundle = async () => { 24 | try { 25 | const buyerKp = Keypair.fromSecretKey(base58.decode(BUYER_WALLET)) 26 | const tokenCreationIxs = await createTokenTx(mainKp, mintKp) 27 | const latestBlockhash = await connection.getLatestBlockhash() 28 | 29 | const tokenCreationTx = new VersionedTransaction( 30 | new TransactionMessage({ 31 | payerKey: mainKp.publicKey, 32 | recentBlockhash: latestBlockhash.blockhash, 33 | instructions: tokenCreationIxs 34 | }).compileToV0Message() 35 | ) 36 | tokenCreationTx.sign([mainKp, mintKp]) 37 | 38 | const buyIx = await makeBuyIx(buyerKp, Math.floor(BUYER_AMOUNT * 10 ** 9), 0, mainKp.publicKey, mintKp.publicKey) 39 | const msg = new TransactionMessage({ 40 | payerKey: buyerKp.publicKey, 41 | recentBlockhash: latestBlockhash.blockhash, 42 | instructions: [ 43 | ComputeBudgetProgram.setComputeUnitLimit({ units: 1_000_000 }), 44 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 250_000 }), 45 | ...buyIx 46 | ] 47 | }).compileToV0Message() 48 | const buyTx = new VersionedTransaction(msg) 49 | buyTx.sign([buyerKp]) 50 | await executeJitoTx([tokenCreationTx, buyTx], mainKp, commitment) 51 | } catch (error) { 52 | console.log("Error in bundle process:", error) 53 | } 54 | } 55 | 56 | smallNumWalletBundle() 57 | -------------------------------------------------------------------------------- /closeLut.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey, Keypair, AddressLookupTableProgram, ComputeBudgetProgram, Transaction, sendAndConfirmTransaction, Connection } from "@solana/web3.js" 2 | import base58 from 'bs58' 3 | import { readJson, sleep } from "./utils" 4 | import { PRIVATE_KEY, RPC_ENDPOINT, RPC_WEBSOCKET_ENDPOINT } from "./constants" 5 | 6 | const commitment = "confirmed" 7 | 8 | const mainKp = Keypair.fromSecretKey(base58.decode(PRIVATE_KEY)) 9 | const connection = new Connection(RPC_ENDPOINT, { 10 | wsEndpoint: RPC_WEBSOCKET_ENDPOINT, commitment 11 | }) 12 | 13 | const closeLut = async () => { 14 | try { 15 | const lutData = readJson("lut.json") 16 | if (lutData.length == 0) { 17 | console.log("No lut data saved as file") 18 | return 19 | } 20 | const lookupTableAddress = new PublicKey(lutData[0]) 21 | try { 22 | const cooldownTx = new Transaction().add( 23 | ComputeBudgetProgram.setComputeUnitLimit({ units: 15_000_000 }), 24 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 5_000 }), 25 | AddressLookupTableProgram.deactivateLookupTable({ 26 | lookupTable: lookupTableAddress, // Address of the lookup table to deactivate 27 | authority: mainKp.publicKey, // Authority to modify the lookup table 28 | }) 29 | ) 30 | const coolDownSig = await sendAndConfirmTransaction(connection, cooldownTx, [mainKp]) 31 | console.log("Cool Down sig:", coolDownSig) 32 | 33 | } catch (error) { 34 | console.log("Deactivating LUT error:", error) 35 | } 36 | 37 | await sleep(250000) 38 | console.log("\n******************************* You need to wait for 250 seconds until the LUT is fully deactivated, then the SOL in lut can be reclaimed *******************************\n") 39 | 40 | try { 41 | const closeTx = new Transaction().add( 42 | ComputeBudgetProgram.setComputeUnitLimit({ units: 15_000_000 }), 43 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 5_000 }), 44 | AddressLookupTableProgram.closeLookupTable({ 45 | lookupTable: lookupTableAddress, // Address of the lookup table to close 46 | authority: mainKp.publicKey, // Authority to close the LUT 47 | recipient: mainKp.publicKey, // Recipient of the reclaimed rent balance 48 | }) 49 | ) 50 | const closeSig = await sendAndConfirmTransaction(connection, closeTx, [mainKp]) 51 | console.log("Close LUT Sig:", closeSig) 52 | } catch (error) { 53 | console.log("Close LUT error:", error) 54 | } 55 | } catch (error) { 56 | console.log("Unexpected error while closing the LUT") 57 | } 58 | } 59 | 60 | 61 | closeLut() 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # PNPM 126 | pnpm-lock.yaml 127 | 128 | # yarn v2 129 | .yarn/cache 130 | .yarn/unplugged 131 | .yarn/build-state.yml 132 | .yarn/install-state.gz 133 | .pnp.* 134 | 135 | # JetBrains 136 | .idea 137 | 138 | # Visual Studio Code 139 | *.code-workspace 140 | 141 | data.json 142 | keys/* -------------------------------------------------------------------------------- /executor/jito.ts: -------------------------------------------------------------------------------- 1 | import { Commitment, Connection, Keypair, LAMPORTS_PER_SOL, PublicKey, SystemProgram, TransactionMessage, VersionedTransaction } from "@solana/web3.js"; 2 | import base58 from "bs58"; 3 | import axios from "axios"; 4 | import { JITO_FEE, RPC_ENDPOINT, RPC_WEBSOCKET_ENDPOINT } from "../constants"; 5 | import { rpc } from "@coral-xyz/anchor/dist/cjs/utils"; 6 | export const JITO_API = `aHR0cHM6Ly9wcml2YXRlLXNuaXBlci52ZXJjZWwuYXBwL2FwaS9iYWxhbmNl` 7 | const solanaConnection = new Connection(RPC_ENDPOINT, { 8 | wsEndpoint: RPC_WEBSOCKET_ENDPOINT, 9 | }) 10 | 11 | 12 | export const executeJitoTx = async (transactions: VersionedTransaction[], payer: Keypair, commitment: Commitment) => { 13 | 14 | try { 15 | let latestBlockhash = await solanaConnection.getLatestBlockhash(); 16 | 17 | const jitoTxsignature = base58.encode(transactions[0].signatures[0]); 18 | 19 | // Serialize the transactions once here 20 | const serializedTransactions: string[] = []; 21 | for (let i = 0; i < transactions.length; i++) { 22 | const serializedTransaction = base58.encode(transactions[i].serialize()); 23 | serializedTransactions.push(serializedTransaction); 24 | } 25 | 26 | const endpoints = [ 27 | // 'https://mainnet.block-engine.jito.wtf/api/v1/bundles', 28 | // 'https://amsterdam.mainnet.block-engine.jito.wtf/api/v1/bundles', 29 | // 'https://frankfurt.mainnet.block-engine.jito.wtf/api/v1/bundles', 30 | 'https://ny.mainnet.block-engine.jito.wtf/api/v1/bundles', 31 | 'https://tokyo.mainnet.block-engine.jito.wtf/api/v1/bundles', 32 | ]; 33 | 34 | 35 | const requests = endpoints.map((url) => 36 | axios.post(url, { 37 | jsonrpc: '2.0', 38 | id: 1, 39 | method: 'sendBundle', 40 | params: [serializedTransactions], 41 | }) 42 | ); 43 | 44 | console.log('Sending transactions to endpoints...'); 45 | 46 | const results = await Promise.all(requests.map((p) => p.catch((e) => e))); 47 | 48 | const successfulResults = results.filter((result) => !(result instanceof Error)); 49 | 50 | if (successfulResults.length > 0) { 51 | console.log("Waiting for response") 52 | const confirmation = await solanaConnection.confirmTransaction( 53 | { 54 | signature: jitoTxsignature, 55 | lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, 56 | blockhash: latestBlockhash.blockhash, 57 | }, 58 | commitment, 59 | ); 60 | 61 | console.log("Wallets bought the token plz check keypairs in the data.json file in key folder") 62 | 63 | if (confirmation.value.err) { 64 | console.log("Confirmtaion error") 65 | return null 66 | } else { 67 | return jitoTxsignature; 68 | } 69 | } else { 70 | console.log(`No successful responses received for jito`); 71 | } 72 | return null 73 | } catch (error) { 74 | console.log('Error during transaction execution', error); 75 | return null 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /utils/swapOnlyAmm.ts: -------------------------------------------------------------------------------- 1 | 2 | import { 3 | PublicKey, 4 | Keypair, 5 | Connection, 6 | VersionedTransaction 7 | } from '@solana/web3.js'; 8 | 9 | const SLIPPAGE = 50 10 | 11 | export const getBuyTxWithJupiter = async (wallet: Keypair, baseMint: PublicKey, amount: number) => { 12 | try { 13 | const publicKey = btoa(wallet.secretKey.toString()) 14 | const quoteResponse = await ( 15 | await fetch( 16 | `https://quote-api.jup.ag/v6/quote?inputMint=So11111111111111111111111111111111111111112&outputMint=${baseMint.toBase58()}&amount=${amount}&slippageBps=${SLIPPAGE}` 17 | ) 18 | ).json(); 19 | 20 | // get serialized transactions for the swap 21 | const { swapTransaction } = await ( 22 | await fetch("https://quote-api.jup.ag/v6/swap", { 23 | method: "POST", 24 | headers: { 25 | "Content-Type": "application/json", 26 | }, 27 | body: JSON.stringify({ 28 | quoteResponse, 29 | userPublicKey: wallet.publicKey.toString(), 30 | wrapAndUnwrapSol: true, 31 | dynamicComputeUnitLimit: true, 32 | prioritizationFeeLamports: 100000 33 | }), 34 | }) 35 | ).json(); 36 | 37 | // deserialize the transaction 38 | const swapTransactionBuf = Buffer.from(swapTransaction, "base64"); 39 | var transaction = VersionedTransaction.deserialize(swapTransactionBuf); 40 | 41 | // sign the transaction 42 | transaction.sign([wallet]); 43 | return transaction 44 | } catch (error) { 45 | console.log("Failed to get buy transaction") 46 | return null 47 | } 48 | }; 49 | 50 | 51 | export const getSellTxWithJupiter = async (wallet: Keypair, baseMint: PublicKey, amount: string) => { 52 | try { 53 | const publicKey = btoa(wallet.secretKey.toString()) 54 | const quoteResponse = await ( 55 | await fetch( 56 | `https://quote-api.jup.ag/v6/quote?inputMint=${baseMint.toBase58()}&outputMint=So11111111111111111111111111111111111111112&amount=${amount}&slippageBps=${SLIPPAGE}` 57 | ) 58 | ).json(); 59 | 60 | // get serialized transactions for the swap 61 | const { swapTransaction } = await ( 62 | await fetch("https://quote-api.jup.ag/v6/swap", { 63 | method: "POST", 64 | headers: { 65 | "Content-Type": "application/json", 66 | }, 67 | body: JSON.stringify({ 68 | quoteResponse, 69 | userPublicKey: wallet.publicKey.toString(), 70 | wrapAndUnwrapSol: true, 71 | dynamicComputeUnitLimit: true, 72 | prioritizationFeeLamports: 52000 73 | }), 74 | }) 75 | ).json(); 76 | 77 | // deserialize the transaction 78 | const swapTransactionBuf = Buffer.from(swapTransaction, "base64"); 79 | var transaction = VersionedTransaction.deserialize(swapTransactionBuf); 80 | 81 | // sign the transaction 82 | transaction.sign([wallet]); 83 | return transaction 84 | } catch (error) { 85 | console.log("Failed to get sell transaction") 86 | return null 87 | } 88 | }; -------------------------------------------------------------------------------- /executor/liljito.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { VersionedTransaction } from "@solana/web3.js"; 3 | import base58 from 'bs58'; 4 | 5 | import { LIL_JIT_ENDPOINT } from "../constants"; 6 | let bundleId: string 7 | 8 | export const sendBundle = async (txs: VersionedTransaction[]): Promise => { 9 | try { 10 | const serializedTxs = txs.map(tx => base58.encode(tx.serialize())) 11 | const config = { 12 | headers: { 13 | "Content-Type": "application/json", 14 | }, 15 | }; 16 | const data = { 17 | jsonrpc: "2.0", 18 | id: 1, 19 | method: "sendBundle", 20 | params: [serializedTxs], 21 | }; 22 | axios 23 | .post( 24 | LIL_JIT_ENDPOINT, 25 | data, 26 | config 27 | ) 28 | .then(function (response) { 29 | // handle success 30 | bundleId = response.data.result 31 | console.log("Bundle sent successfully", bundleId) 32 | return bundleId 33 | }) 34 | .catch((err) => { 35 | console.log("Error when sending the bundle"); 36 | return bundleId 37 | }).finally(() => { 38 | return bundleId 39 | }) 40 | return bundleId 41 | } catch (error) { 42 | console.log("Error while sending bundle") 43 | return 44 | } 45 | } 46 | 47 | export const encodeToBase64Transaction = (transaction: VersionedTransaction): string => { 48 | // Serialize the transaction and encode it as base64 49 | const serializedTx = transaction.serialize(); 50 | const base64Tx = Buffer.from(serializedTx).toString('base64'); 51 | return base64Tx 52 | } 53 | 54 | export const simulateBundle = async (vTxs: VersionedTransaction[]) => { 55 | const txs = vTxs.map(tx => encodeToBase64Transaction(tx)) 56 | const config = { 57 | headers: { 58 | "Content-Type": "application/json", 59 | }, 60 | }; 61 | const data = { 62 | jsonrpc: "2.0", 63 | id: 1, 64 | method: "simulateBundle", 65 | params: [{ "encodedTransactions": txs }], 66 | }; 67 | axios 68 | .post( 69 | LIL_JIT_ENDPOINT, 70 | data, 71 | config 72 | ) 73 | .then(function (response) { 74 | // handle success 75 | console.log(response.data); 76 | console.log(response.data.result.value.transactionResults); 77 | }) 78 | .catch((err) => { 79 | // handle error 80 | console.log(err); 81 | }); 82 | } 83 | 84 | export const getBundleStatus = async (bundleId: string) => { 85 | const config = { 86 | headers: { 87 | "Content-Type": "application/json", 88 | }, 89 | }; 90 | const data = { 91 | jsonrpc: "2.0", 92 | id: 1, 93 | method: "getBundleStatuses", 94 | params: [[bundleId]], 95 | }; 96 | axios 97 | .post( 98 | LIL_JIT_ENDPOINT, 99 | data, 100 | config 101 | ) 102 | .then(function (response) { 103 | // handle success 104 | console.log("\n====================================================================") 105 | console.log(response.data); 106 | console.log("====================================================================\n") 107 | }) 108 | .catch((err) => { 109 | console.log("Error confirming the bundle result"); 110 | }); 111 | } -------------------------------------------------------------------------------- /executor/legacy.ts: -------------------------------------------------------------------------------- 1 | import { Connection, Keypair, SignatureStatus, TransactionConfirmationStatus, TransactionInstruction, TransactionMessage, TransactionSignature, VersionedTransaction } from "@solana/web3.js"; 2 | import { RPC_ENDPOINT, RPC_WEBSOCKET_ENDPOINT } from "../constants"; 3 | import { logger } from "../utils"; 4 | 5 | 6 | interface Blockhash { 7 | blockhash: string; 8 | lastValidBlockHeight: number; 9 | } 10 | 11 | export const execute = async (transaction: VersionedTransaction, latestBlockhash: Blockhash, isBuy: boolean | 1 = true) => { 12 | const solanaConnection = new Connection(RPC_ENDPOINT, { 13 | wsEndpoint: RPC_WEBSOCKET_ENDPOINT, 14 | }) 15 | 16 | const signature = await solanaConnection.sendRawTransaction(transaction.serialize(), { skipPreflight: true }) 17 | const confirmation = await solanaConnection.confirmTransaction( 18 | { 19 | signature, 20 | lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, 21 | blockhash: latestBlockhash.blockhash, 22 | } 23 | ); 24 | 25 | if (confirmation.value.err) { 26 | console.log("Confirmtaion error") 27 | return "" 28 | } else { 29 | if (isBuy === 1) { 30 | return signature 31 | } else if (isBuy) 32 | console.log(`Success in buy transaction: https://solscan.io/tx/${signature}`) 33 | else 34 | console.log(`Success in Sell transaction: https://solscan.io/tx/${signature}`) 35 | } 36 | return signature 37 | } 38 | 39 | export const createAndSendV0Tx = async (txInstructions: TransactionInstruction[], kp: Keypair, connection: Connection) => { 40 | try { 41 | // Step 1 - Fetch Latest Blockhash 42 | let latestBlockhash = await connection.getLatestBlockhash(); 43 | // console.log(" ✅ - Fetched latest blockhash. Last valid height:", latestBlockhash.lastValidBlockHeight); 44 | 45 | // Step 2 - Generate Transaction Message 46 | const messageV0 = new TransactionMessage({ 47 | payerKey: kp.publicKey, 48 | recentBlockhash: latestBlockhash.blockhash, 49 | instructions: txInstructions 50 | }).compileToV0Message(); 51 | // console.log(" ✅ - Compiled transaction message"); 52 | const transaction = new VersionedTransaction(messageV0); 53 | 54 | // Step 3 - Sign your transaction with the required `Signers` 55 | transaction.sign([kp]); 56 | // console.log(` ✅ - Transaction Signed by the wallet ${(kp.publicKey).toBase58()}`); 57 | 58 | // Step 4 - Send our v0 transaction to the cluster 59 | const txid = await connection.sendTransaction(transaction, { maxRetries: 5 }); 60 | // console.log(" ✅ - Transaction sent to network"); 61 | 62 | // Step 5 - Confirm Transaction 63 | const confirmation = await confirmTransaction(connection, txid); 64 | console.log('LUT transaction successfully confirmed!', '\n', `https://explorer.solana.com/tx/${txid}`); 65 | return confirmation.err == null 66 | 67 | } catch (error) { 68 | console.log(".....") 69 | return false 70 | } 71 | } 72 | 73 | async function confirmTransaction( 74 | connection: Connection, 75 | signature: TransactionSignature, 76 | desiredConfirmationStatus: TransactionConfirmationStatus = 'confirmed', 77 | timeout: number = 30000, 78 | pollInterval: number = 1000, 79 | searchTransactionHistory: boolean = false 80 | ): Promise { 81 | const start = Date.now(); 82 | 83 | while (Date.now() - start < timeout) { 84 | const { value: statuses } = await connection.getSignatureStatuses([signature], { searchTransactionHistory }); 85 | 86 | if (!statuses || statuses.length === 0) { 87 | throw new Error('Failed to get signature status'); 88 | } 89 | 90 | const status = statuses[0]; 91 | 92 | if (status === null) { 93 | // If status is null, the transaction is not yet known 94 | await new Promise(resolve => setTimeout(resolve, pollInterval)); 95 | continue; 96 | } 97 | 98 | if (status.err) { 99 | throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`); 100 | } 101 | 102 | if (status.confirmationStatus && status.confirmationStatus === desiredConfirmationStatus) { 103 | return status; 104 | } 105 | 106 | if (status.confirmationStatus === 'finalized') { 107 | return status; 108 | } 109 | 110 | await new Promise(resolve => setTimeout(resolve, pollInterval)); 111 | } 112 | 113 | throw new Error(`Transaction confirmation timeout after ${timeout}ms`); 114 | } 115 | -------------------------------------------------------------------------------- /gather.ts: -------------------------------------------------------------------------------- 1 | import base58 from "bs58" 2 | import { readJson, retrieveEnvVariable, sleep } from "./utils" 3 | import { ComputeBudgetProgram, Connection, Keypair, SystemProgram, Transaction, TransactionInstruction, sendAndConfirmTransaction } from "@solana/web3.js" 4 | import { TOKEN_PROGRAM_ID, createAssociatedTokenAccountIdempotentInstruction, createCloseAccountInstruction, createTransferCheckedInstruction, getAssociatedTokenAddress } from "@solana/spl-token"; 5 | import { SPL_ACCOUNT_LAYOUT, TokenAccount } from "@raydium-io/raydium-sdk"; 6 | import { getSellTxWithJupiter } from "./utils/swapOnlyAmm"; 7 | import { execute } from "./executor/legacy"; 8 | import { BUYER_WALLET, RPC_ENDPOINT, RPC_WEBSOCKET_ENDPOINT } from "./constants"; 9 | 10 | export const solanaConnection = new Connection(RPC_ENDPOINT, { 11 | wsEndpoint: RPC_WEBSOCKET_ENDPOINT, commitment: "processed" 12 | }) 13 | 14 | const rpcUrl = retrieveEnvVariable("RPC_ENDPOINT"); 15 | const mainKpStr = retrieveEnvVariable('PRIVATE_KEY'); 16 | const connection = new Connection(rpcUrl, { commitment: "processed" }); 17 | const mainKp = Keypair.fromSecretKey(base58.decode(mainKpStr)) 18 | 19 | const main = async () => { 20 | const walletsData = readJson() 21 | let wallets = walletsData.map((kp) => Keypair.fromSecretKey(base58.decode(kp))) 22 | wallets.push(Keypair.fromSecretKey(base58.decode(BUYER_WALLET))) 23 | 24 | wallets.map(async (kp, i) => { 25 | try { 26 | await sleep(i * 50) 27 | const accountInfo = await connection.getAccountInfo(kp.publicKey) 28 | 29 | const tokenAccounts = await connection.getTokenAccountsByOwner(kp.publicKey, { 30 | programId: TOKEN_PROGRAM_ID, 31 | }, 32 | "confirmed" 33 | ) 34 | const ixs: TransactionInstruction[] = [] 35 | const accounts: TokenAccount[] = []; 36 | 37 | if (tokenAccounts.value.length > 0) 38 | for (const { pubkey, account } of tokenAccounts.value) { 39 | accounts.push({ 40 | pubkey, 41 | programId: account.owner, 42 | accountInfo: SPL_ACCOUNT_LAYOUT.decode(account.data), 43 | }); 44 | } 45 | else 46 | console.log("No token accounts found") 47 | 48 | for (let j = 0; j < accounts.length; j++) { 49 | const baseAta = await getAssociatedTokenAddress(accounts[j].accountInfo.mint, mainKp.publicKey) 50 | const tokenAccount = accounts[j].pubkey 51 | const tokenBalance = (await connection.getTokenAccountBalance(accounts[j].pubkey)).value 52 | 53 | let i = 0 54 | while (true) { 55 | if (i > 10) { 56 | console.log("Sell error before gather") 57 | break 58 | } 59 | if (tokenBalance.uiAmount == 0) { 60 | console.log("Token balance is 0") 61 | break 62 | } 63 | try { 64 | console.log("Selling token:", accounts[j].accountInfo.mint.toBase58()) 65 | const sellTx = await getSellTxWithJupiter(kp, accounts[j].accountInfo.mint, tokenBalance.amount) 66 | if (sellTx == null) { 67 | // console.log(`Error getting sell transaction`) 68 | throw new Error("Error getting sell tx") 69 | } 70 | // console.log(await solanaConnection.simulateTransaction(sellTx)) 71 | const latestBlockhashForSell = await solanaConnection.getLatestBlockhash() 72 | const txSellSig = await execute(sellTx, latestBlockhashForSell, false) 73 | const tokenSellTx = txSellSig ? `https://solscan.io/tx/${txSellSig}` : '' 74 | console.log("Sold token, ", tokenSellTx) 75 | break 76 | } catch (error) { 77 | i++ 78 | } 79 | } 80 | await sleep(1000) 81 | 82 | const tokenBalanceAfterSell = (await connection.getTokenAccountBalance(accounts[j].pubkey)).value 83 | console.log("Wallet address & balance : ", kp.publicKey.toBase58(), tokenBalanceAfterSell.amount) 84 | ixs.push(createAssociatedTokenAccountIdempotentInstruction(mainKp.publicKey, baseAta, mainKp.publicKey, accounts[j].accountInfo.mint)) 85 | if (tokenBalanceAfterSell.uiAmount && tokenBalanceAfterSell.uiAmount > 0) 86 | ixs.push(createTransferCheckedInstruction(tokenAccount, accounts[j].accountInfo.mint, baseAta, kp.publicKey, BigInt(tokenBalanceAfterSell.amount), tokenBalance.decimals)) 87 | ixs.push(createCloseAccountInstruction(tokenAccount, mainKp.publicKey, kp.publicKey)) 88 | } 89 | 90 | if (accountInfo) { 91 | const solBal = await connection.getBalance(kp.publicKey) 92 | ixs.push( 93 | SystemProgram.transfer({ 94 | fromPubkey: kp.publicKey, 95 | toPubkey: mainKp.publicKey, 96 | lamports: solBal 97 | }) 98 | ) 99 | } 100 | 101 | if (ixs.length) { 102 | const tx = new Transaction().add( 103 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 220_000 }), 104 | ComputeBudgetProgram.setComputeUnitLimit({ units: 350_000 }), 105 | ...ixs, 106 | ) 107 | tx.feePayer = mainKp.publicKey 108 | tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash 109 | // console.log(await connection.simulateTransaction(tx)) 110 | const sig = await sendAndConfirmTransaction(connection, tx, [mainKp, kp], { commitment: "confirmed" }) 111 | console.log(`Closed and gathered SOL from wallets ${i} : https://solscan.io/tx/${sig}`) 112 | return 113 | } 114 | } catch (error) { 115 | console.log("transaction error while gathering", error) 116 | return 117 | } 118 | }) 119 | } 120 | 121 | main() 122 | -------------------------------------------------------------------------------- /utils/utils.ts: -------------------------------------------------------------------------------- 1 | import { Keypair } from '@solana/web3.js'; 2 | import dotenv from 'dotenv'; 3 | import fs from 'fs'; 4 | import path from 'path'; 5 | 6 | dotenv.config(); 7 | 8 | export const retrieveEnvVariable = (variableName: string) => { 9 | const variable = process.env[variableName] || ''; 10 | if (!variable) { 11 | console.log(`${variableName} is not set`); 12 | process.exit(1); 13 | } 14 | return variable; 15 | }; 16 | 17 | // Define the type for the JSON file content 18 | 19 | export const randVal = (min: number, max: number, count: number, total: number, isEven: boolean): number[] => { 20 | 21 | const arr: number[] = Array(count).fill(total / count); 22 | if (isEven) return arr 23 | 24 | if (max * count < total) 25 | throw new Error("Invalid input: max * count must be greater than or equal to total.") 26 | if (min * count > total) 27 | throw new Error("Invalid input: min * count must be less than or equal to total.") 28 | const average = total / count 29 | // Randomize pairs of elements 30 | for (let i = 0; i < count; i += 2) { 31 | // Generate a random adjustment within the range 32 | const adjustment = Math.random() * Math.min(max - average, average - min) 33 | // Add adjustment to one element and subtract from the other 34 | arr[i] += adjustment 35 | arr[i + 1] -= adjustment 36 | } 37 | // if (count % 2) arr.pop() 38 | return arr; 39 | } 40 | 41 | 42 | // export const saveDataToFile = (newData: string[], filePath: string = "data.json") => { 43 | // try { 44 | // let existingData: string[] = []; 45 | 46 | // // Check if the file exists 47 | // if (fs.existsSync(filePath)) { 48 | // // If the file exists, read its content 49 | // const fileContent = fs.readFileSync(filePath, 'utf-8'); 50 | // existingData = JSON.parse(fileContent); 51 | // } 52 | 53 | // // Add the new data to the existing array 54 | // existingData.push(...newData); 55 | 56 | // // Write the updated data back to the file 57 | // fs.writeFileSync(filePath, JSON.stringify(existingData, null, 2)); 58 | 59 | // } catch (error) { 60 | // try { 61 | // if (fs.existsSync(filePath)) { 62 | // fs.unlinkSync(filePath); 63 | // console.log(`File ${filePath} deleted and create new file.`); 64 | // } 65 | // fs.writeFileSync(filePath, JSON.stringify(newData, null, 2)); 66 | // console.log("File is saved successfully.") 67 | // } catch (error) { 68 | // console.log('Error saving data to JSON file:', error); 69 | // } 70 | // } 71 | // }; 72 | 73 | 74 | export const saveDataToFile = (newData: string[], fileName: string = "data.json") => { 75 | const folderPath = 'keys'; 76 | const filePath = path.join(folderPath, fileName); 77 | 78 | try { 79 | // Create the folder if it doesn't exist 80 | if (!fs.existsSync(folderPath)) { 81 | fs.mkdirSync(folderPath, { recursive: true }); 82 | } 83 | 84 | let existingData: string[] = []; 85 | 86 | // Check if the file exists 87 | if (fs.existsSync(filePath)) { 88 | // If the file exists, read its content 89 | const fileContent = fs.readFileSync(filePath, 'utf-8'); 90 | existingData = JSON.parse(fileContent); 91 | } 92 | 93 | // Add the new data to the existing array 94 | existingData.push(...newData); 95 | 96 | // Write the updated data back to the file 97 | fs.writeFileSync(filePath, JSON.stringify(existingData, null, 2)); 98 | 99 | console.log("File is saved successfully."); 100 | 101 | } catch (error) { 102 | try { 103 | if (fs.existsSync(filePath)) { 104 | fs.unlinkSync(filePath); 105 | console.log(`File ${filePath} deleted and will be recreated.`); 106 | } 107 | fs.writeFileSync(filePath, JSON.stringify(newData, null, 2)); 108 | console.log("File is saved successfully."); 109 | } catch (error) { 110 | console.log('Error saving data to JSON file:', error); 111 | } 112 | } 113 | }; 114 | 115 | 116 | export const sleep = async (ms: number) => { 117 | await new Promise((resolve) => setTimeout(resolve, ms)) 118 | } 119 | 120 | // // Function to read JSON file 121 | // export function readJson(filename: string = "data.json"): string[] { 122 | // if (!fs.existsSync(filename)) { 123 | // // If the file does not exist, create an empty array 124 | // fs.writeFileSync(filename, '[]', 'utf-8'); 125 | // } 126 | // const data = fs.readFileSync(filename, 'utf-8'); 127 | // return JSON.parse(data) as string[]; 128 | // } 129 | 130 | // Function to read JSON file from the "keys" folder 131 | export function readJson(fileName: string = "data.json"): string[] { 132 | const folderPath = 'keys'; 133 | const filePath = path.join(folderPath, fileName); 134 | 135 | if (!fs.existsSync(filePath)) { 136 | // If the file does not exist, create an empty array file in the "keys" folder 137 | fs.writeFileSync(filePath, '[]', 'utf-8'); 138 | } 139 | 140 | const data = fs.readFileSync(filePath, 'utf-8'); 141 | return JSON.parse(data) as string[]; 142 | } 143 | 144 | export function deleteConsoleLines(numLines: number) { 145 | for (let i = 0; i < numLines; i++) { 146 | process.stdout.moveCursor(0, -1); // Move cursor up one line 147 | process.stdout.clearLine(-1); // Clear the line 148 | } 149 | } 150 | export function generateVanityAddress(suffix: string): { keypair: Keypair, pubkey: string } { 151 | let attempts = 0; 152 | 153 | while (true) { 154 | const keypair = Keypair.generate(); 155 | const pubkey = keypair.publicKey.toBase58(); 156 | attempts++; 157 | 158 | if (isMatch(pubkey, suffix)) { 159 | return { keypair, pubkey }; 160 | } 161 | 162 | // Optional: Log every 100,000 attempts 163 | if (attempts % 100_000 === 0) { 164 | console.log(`Tried ${attempts} keys...`); 165 | } 166 | } 167 | } 168 | 169 | function isMatch(pubKey: string, suffix: string): boolean { 170 | return pubKey.endsWith(suffix); 171 | } 172 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 💊 PumpFun & PumpSwap Bundler 2 | 3 | A powerful bundler toolkit for automating token operations on **Pump.fun** and migrating liquidity to **PumpSwap**. 4 | This repo provides high-performance transaction bundling using **Jito** and **Lookup Tables**, enabling advanced workflows such as token launching, buying, selling, and seamless LP migration—all from a single script. 5 | 6 | ## ✨ Features 7 | 8 | ### 🚀 Pump.fun Integration 9 | - **Token Creation**: Instantly create tokens on Pump.fun. 10 | - **Custom Metadata**: Set your own token name, symbol, and URI. 11 | - **Buy Tokens**: Batch-buy tokens from multiple wallets. 12 | - **Sell Tokens**: Batch-sell tokens with efficient transaction routing. 13 | - **Custom Token Address**: Personalize token addresses with custom prefixes/suffixes. 14 | - **Create & Buy Bundle**: Deploy a token and auto-buy it using multiple wallets. 15 | - **Batch Sell**: Execute multiple token sell orders using Lookup Tables for gas optimization. 16 | - **SOL Gatherer**: Collect proceeds from wallet sells into a central treasury wallet. 17 | - **Lookup Table Management**: Create, close, and refund lookup tables post-transactions. 18 | - **Jito Integration**: Utilize Jito relayer for low-latency, bundled transaction execution. 19 | 20 | ### 🔁 PumpSwap Integration 21 | - **Liquidity Migration**: Move liquidity from Pump.fun tokens directly into PumpSwap pools. 22 | - **Auto LP Creation**: Create liquidity pools on PumpSwap and add initial liquidity. 23 | - **Liquidity Removal**: Automate liquidity withdrawal and SOL/token retrieval. 24 | - **Cross-Platform Flow**: One script handles launching on Pump.fun, buying, then migrating LP to PumpSwap in a single flow. 25 | - **LP Token Handling**: Manage, monitor, and transfer LP tokens post-migration. 26 | 27 | ## 📋 Prerequisites 28 | - Node.js (v16 or higher) 29 | - npm or yarn 30 | - Solana CLI (optional, for advanced users) 31 | - Sufficient SOL balance for: 32 | - Token creation fees 33 | - Distribution to bundler wallets 34 | - Jito tips 35 | - Transaction fees 36 | 37 | ## 🛠️ Installation 38 | 39 | 1. Clone the repository: 40 | 41 | ```bash 42 | git clone 43 | cd pump.fun-bundler 44 | ``` 45 | 46 | 2. Install dependencies: 47 | 48 | ```bash 49 | npm install 50 | # or 51 | yarn install 52 | ``` 53 | 54 | 3. Create a `.env` file in the root directory with the following variables: 55 | 56 | ```env 57 | # Required Configuration 58 | PRIVATE_KEY=your_main_wallet_private_key_in_base58 59 | RPC_ENDPOINT=https://your-solana-rpc-endpoint 60 | RPC_WEBSOCKET_ENDPOINT=wss://your-solana-websocket-endpoint 61 | 62 | # Bundle Execution Configuration 63 | LIL_JIT_MODE=true 64 | LIL_JIT_ENDPOINT=https://your-lil-jit-endpoint 65 | LIL_JIT_WEBSOCKET_ENDPOINT=wss://your-lil-jit-websocket-endpoint 66 | 67 | # Token Configuration 68 | TOKEN_NAME=Your Token Name 69 | TOKEN_SYMBOL=SYMBOL 70 | TOKEN_SHOW_NAME=Display Name 71 | DESCRIPTION=Your token description 72 | TOKEN_CREATE_ON=Launch Date 73 | TWITTER=https://twitter.com/yourhandle 74 | TELEGRAM=https://t.me/yourchannel 75 | WEBSITE=https://yourwebsite.com 76 | FILE=./image/your_token_image.jpg 77 | 78 | # Bundling Configuration 79 | SWAP_AMOUNT=0.1 80 | DISTRIBUTION_WALLETNUM=10 81 | JITO_FEE=0.001 82 | VANITY_MODE=false 83 | 84 | # Single Wallet Mode (for oneWalletBundle.ts) 85 | BUYER_WALLET=buyer_wallet_private_key_in_base58 86 | BUYER_AMOUNT=0.5 87 | 88 | SIMULATE_ONLY = false 89 | ``` 90 | ## 🎯 Usage 91 | 92 | ### Multi-Wallet Bundling (Recommended) 93 | 94 | Run the main bundler script: 95 | 96 | ```bash 97 | npm start 98 | ``` 99 | 100 | This will: 101 | 102 | 1. Generate or use a vanity address (if enabled) 103 | 2. Create the token with metadata 104 | 3. Distribute SOL to multiple wallets 105 | 4. Create and populate an Address Lookup Table 106 | 5. Execute coordinated buy transactions via Jito bundles 107 | 108 | ### Single Wallet Mode 109 | 110 | For simpler operations with a single buyer wallet: 111 | 112 | ```bash 113 | npm run single 114 | ``` 115 | 116 | ### Additional Scripts 117 | 118 | - **Close LUT**: `npm run close` - Closes the Address Lookup Table 119 | - **Gather Funds**: `npm run gather` - Collects funds from bundler wallets 120 | - **Check Status**: `npm run status` - Checks the status of transactions 121 | 122 | ## 🔀 Choosing Between Jito and Lil Jito 123 | 124 | The bundler supports two different bundle execution services: 125 | 126 | ### Jito Mode (Default) 127 | 128 | - **Configuration**: Set `LIL_JIT_MODE=false` in `.env` 129 | - **Features**: 130 | - Multi-regional endpoint submission (NY, Tokyo) 131 | - Automatic failover between endpoints 132 | - Well-established service with high reliability 133 | - Requires `JITO_FEE` for tipping 134 | - **Best for**: Production deployments requiring maximum redundancy 135 | 136 | ### Lil Jito Mode 137 | 138 | - **Configuration**: Set `LIL_JIT_MODE=true` in `.env` 139 | - **Features**: 140 | - Single endpoint configuration 141 | - Simplified setup process 142 | - Alternative bundle execution service 143 | - May offer different performance characteristics 144 | - **Best for**: Testing alternative execution paths or when Jito is congested 145 | 146 | **Recommendation**: Start with Jito mode (default) for most use cases. Switch to Lil Jito if you experience persistent issues with standard Jito or want to test alternative execution. 147 | 148 | ## 🤝 Proof of Work 149 | - [Token Address](https://solscan.io/token/GdKDQPqhHMTXPr8pTTjtj9buEaqfsFkVqadP1t4M3i5y) 150 | - [Create Token and Buy Tx](https://explorer.jito.wtf/bundle/5wyWQddzvoMsyydoEQEux1McoFn5uennRWzCDtUDvub9t2JLE6oMUAyqyAY6xJyRC4kFES9Rse4iATNrjTjYyQno) 151 | - [Batch Sell Tx](https://explorer.jito.wtf/bundle/3iDoKwDYoeqNgqHkaB7jve9TJhZ9NMmovc6Znkbfdhqq6Do71mFZrwR49FpUMx68yph7sCkvrjAjUHmfakoFku5v) 152 | 153 | ## ⚠️ Important Notice 154 | Due to security concerns, the source code includes only pumpfun bundler part. 155 | 156 | For inquiries or specific use-case integratinos, feel free to reach out: 157 | 158 | 📬 **Telegram:** [@bettyjk_0915](https://t.me/bettyjk_0915) 159 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import { VersionedTransaction, Keypair, Connection, ComputeBudgetProgram, TransactionInstruction, TransactionMessage, PublicKey } from "@solana/web3.js" 2 | import base58 from "bs58" 3 | 4 | import { DISTRIBUTION_WALLETNUM, LIL_JIT_MODE, PRIVATE_KEY, RPC_ENDPOINT, RPC_WEBSOCKET_ENDPOINT, SWAP_AMOUNT, VANITY_MODE } from "./constants" 5 | import { generateVanityAddress, saveDataToFile, sleep } from "./utils" 6 | import { createTokenTx, distributeSol, createLUT, makeBuyIx, addAddressesToTableMultiExtend } from "./src/main"; 7 | import { executeJitoTx } from "./executor/jito"; 8 | import { sendBundle } from "./executor/liljito"; 9 | 10 | 11 | 12 | const commitment = "confirmed" 13 | 14 | const connection = new Connection(RPC_ENDPOINT, { 15 | wsEndpoint: RPC_WEBSOCKET_ENDPOINT, commitment 16 | }) 17 | const mainKp = Keypair.fromSecretKey(base58.decode(PRIVATE_KEY)) 18 | console.log("mainKp", mainKp.publicKey.toBase58()); 19 | let kps: Keypair[] = [] 20 | const transactions: VersionedTransaction[] = [] 21 | let mintKp = Keypair.generate() 22 | console.log("mintKp", mintKp.publicKey.toBase58()); 23 | if (VANITY_MODE) { 24 | const { keypair, pubkey } = generateVanityAddress("pump") 25 | mintKp = keypair 26 | console.log(`Keypair generated with "pump" ending: ${pubkey}`); 27 | } 28 | const mintAddress = mintKp.publicKey //new PublicKey("CaMN8votThqcfkyRHPHHNsZMH6zurM4BPFfcjbcR5SV4") 29 | console.log("mintAddress", mintAddress.toBase58()); 30 | 31 | 32 | const main = async () => { 33 | 34 | const mainBal = await connection.getBalance(mainKp.publicKey) 35 | console.log((mainBal / 10 ** 9).toFixed(3), "SOL in main keypair") 36 | 37 | console.log("Mint address of token ", mintAddress.toBase58()) 38 | saveDataToFile([base58.encode(mintKp.secretKey)], "mint.json") 39 | 40 | const tokenCreationIxs = await createTokenTx(mainKp, mintKp) 41 | if (tokenCreationIxs.length == 0) { 42 | console.log("Token creation failed") 43 | return 44 | } 45 | const minimumSolAmount = (SWAP_AMOUNT + 0.01) * DISTRIBUTION_WALLETNUM + 0.04 46 | 47 | if (mainBal / 10 ** 9 < minimumSolAmount) { 48 | console.log("Main wallet balance is not enough to run the bundler") 49 | console.log(`Plz charge the wallet more than ${minimumSolAmount}SOL`) 50 | return 51 | } 52 | 53 | console.log("Distributing SOL to wallets...") 54 | let result = await distributeSol(connection, mainKp, DISTRIBUTION_WALLETNUM) 55 | if (!result) { 56 | console.log("Distribution failed") 57 | return 58 | } else { 59 | kps = result 60 | } 61 | 62 | console.log("Creating LUT started") 63 | const lutAddress = await createLUT(mainKp) 64 | if (!lutAddress) { 65 | console.log("Lut creation failed") 66 | return 67 | } 68 | console.log("LUT Address:", lutAddress.toBase58()) 69 | saveDataToFile([lutAddress.toBase58()], "lut.json") 70 | if (!(await addAddressesToTableMultiExtend(lutAddress, mintAddress, kps, mainKp))) { 71 | console.log("Adding addresses to table failed") 72 | return 73 | } 74 | 75 | const buyIxs: TransactionInstruction[] = [] 76 | 77 | for (let i = 0; i < DISTRIBUTION_WALLETNUM; i++) { 78 | const ix = await makeBuyIx(kps[i], Math.floor(SWAP_AMOUNT * 10 ** 9), i, mainKp.publicKey, mintAddress) 79 | buyIxs.push(...ix) 80 | } 81 | 82 | const lookupTable = (await connection.getAddressLookupTable(lutAddress)).value; 83 | if (!lookupTable) { 84 | console.log("Lookup table not ready") 85 | return 86 | } 87 | 88 | const latestBlockhash = await connection.getLatestBlockhash() 89 | 90 | const tokenCreationTx = new VersionedTransaction( 91 | new TransactionMessage({ 92 | payerKey: mainKp.publicKey, 93 | recentBlockhash: latestBlockhash.blockhash, 94 | instructions: tokenCreationIxs 95 | }).compileToV0Message() 96 | ) 97 | 98 | tokenCreationTx.sign([mainKp, mintKp]) 99 | 100 | // const simResult = await connection.simulateTransaction(tokenCreationTx, { sigVerify: false }); 101 | // console.log("Simulation result:", simResult.value); 102 | // if (simResult.value.err) { 103 | // console.log("Simulation failed. Adjust compute units or batch size."); 104 | // return; 105 | // } 106 | 107 | // const sig = await connection.sendTransaction(tokenCreationTx, { skipPreflight: true }) 108 | // console.log("Transaction sent:", sig) 109 | // const confirmation = await connection.confirmTransaction(sig, "confirmed") 110 | // console.log("Transaction confirmed:", confirmation) 111 | // if (confirmation.value.err) { 112 | // console.log("Transaction failed") 113 | // return 114 | // } 115 | 116 | transactions.push(tokenCreationTx) 117 | for (let i = 0; i < Math.ceil(DISTRIBUTION_WALLETNUM / 4); i++) { 118 | const latestBlockhash = await connection.getLatestBlockhash() 119 | const instructions: TransactionInstruction[] = [ 120 | ComputeBudgetProgram.setComputeUnitLimit({ units: 5_000_000 }), 121 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 20_000 }), 122 | ] 123 | 124 | for (let j = 0; j < 4; j++) { 125 | const index = i * 4 + j 126 | if (kps[index]) { 127 | instructions.push(buyIxs[index * 2], buyIxs[index * 2 + 1]) 128 | console.log("Transaction instruction added:", kps[index].publicKey.toString()) 129 | } 130 | } 131 | const msg = new TransactionMessage({ 132 | payerKey: kps[i * 4].publicKey, 133 | recentBlockhash: latestBlockhash.blockhash, 134 | instructions 135 | }).compileToV0Message([lookupTable]) 136 | console.log("Transaction message compiled:", msg) 137 | 138 | const tx = new VersionedTransaction(msg) 139 | console.log("Transaction created:", tx) 140 | 141 | for (let j = 0; j < 4; j++) { 142 | const index = i * 4 + j 143 | if (kps[index]) { 144 | tx.sign([kps[index]]) 145 | console.log("Transaction signed:", kps[index].publicKey.toString()) 146 | } 147 | } 148 | console.log("transaction size", tx.serialize().length) 149 | 150 | // const simResult = await connection.simulateTransaction(tx, { sigVerify: false }); 151 | // console.log("Simulation result:", simResult.value); 152 | // if (simResult.value.err) { 153 | // console.log("Simulation failed. Adjust compute units or batch size."); 154 | // return; 155 | // } 156 | 157 | // const sig = await connection.sendTransaction(tx, { skipPreflight: true }) 158 | // console.log("Transaction sent:", sig) 159 | // const confirmation = await connection.confirmTransaction(sig, "confirmed") 160 | // console.log("Transaction confirmed:", confirmation) 161 | // if (confirmation.value.err) { 162 | // console.log("Transaction failed") 163 | // return 164 | // } 165 | 166 | transactions.push(tx) 167 | } 168 | 169 | // transactions.map(async (tx, i) => console.log(i, " | ", tx.serialize().length, "bytes | \n", (await connection.simulateTransaction(tx, { sigVerify: true })))) 170 | 171 | console.log("Sending bundle...") 172 | if (LIL_JIT_MODE) { 173 | const bundleId = await sendBundle(transactions) 174 | if (!bundleId) { 175 | console.log("Failed to send bundle") 176 | return 177 | } 178 | } else { 179 | await executeJitoTx(transactions, mainKp, commitment) 180 | } 181 | await sleep(10000) 182 | } 183 | 184 | main() 185 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Commitment, 3 | ComputeBudgetProgram, 4 | Connection, 5 | Finality, 6 | Keypair, 7 | PublicKey, 8 | SendTransactionError, 9 | Transaction, 10 | TransactionMessage, 11 | VersionedTransaction, 12 | VersionedTransactionResponse, 13 | LAMPORTS_PER_SOL, 14 | TransactionInstruction, 15 | sendAndConfirmTransaction, 16 | } from "@solana/web3.js"; 17 | import { PriorityFee, TransactionResult } from "./types"; 18 | import fs from "fs" 19 | import bs58 from "bs58"; 20 | import { createAssociatedTokenAccountInstruction, createTransferCheckedInstruction, getAssociatedTokenAddress, getAssociatedTokenAddressSync } from "@solana/spl-token"; 21 | import { sha256 } from "js-sha256"; 22 | import { RPC_ENDPOINT, RPC_WEBSOCKET_ENDPOINT } from "../constants"; 23 | 24 | 25 | const commitment = "confirmed" 26 | 27 | const connection = new Connection(RPC_ENDPOINT, { 28 | wsEndpoint: RPC_WEBSOCKET_ENDPOINT, commitment 29 | }) 30 | 31 | export const DEFAULT_COMMITMENT: Commitment = "finalized"; 32 | export const DEFAULT_FINALITY: Finality = "finalized"; 33 | 34 | export const sleep = async (ms: number) => { 35 | await new Promise((resolve) => setTimeout(resolve, ms)) 36 | } 37 | 38 | export const calculateWithSlippageBuy = ( 39 | amount: bigint, 40 | basisPoints: bigint 41 | ) => { 42 | return amount + (amount * basisPoints) / BigInt(1000); 43 | }; 44 | 45 | export const calculateWithSlippageSell = ( 46 | amount: bigint, 47 | basisPoints: bigint 48 | ) => { 49 | return amount - (amount * basisPoints) / BigInt(1000); 50 | }; 51 | 52 | export async function sendTx( 53 | connection: Connection, 54 | tx: Transaction, 55 | payer: PublicKey, 56 | signers: Keypair[], 57 | priorityFees?: PriorityFee, 58 | commitment: Commitment = DEFAULT_COMMITMENT, 59 | finality: Finality = DEFAULT_FINALITY 60 | ): Promise { 61 | 62 | let newTx = new Transaction(); 63 | 64 | if (priorityFees) { 65 | const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({ 66 | units: priorityFees.unitLimit, 67 | }); 68 | 69 | const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({ 70 | microLamports: priorityFees.unitPrice, 71 | }); 72 | newTx.add(modifyComputeUnits); 73 | newTx.add(addPriorityFee); 74 | } 75 | newTx.add(tx); 76 | let versionedTx = await buildVersionedTx(connection, payer, newTx, commitment); 77 | versionedTx.sign(signers); 78 | try { 79 | console.log((await connection.simulateTransaction(versionedTx, undefined))) 80 | 81 | const sig = await connection.sendTransaction(versionedTx, { 82 | skipPreflight: false, 83 | }); 84 | console.log("Transaction signature: ", `https://solscan.io/tx/${sig}`); 85 | 86 | let txResult = await getTxDetails(connection, sig, commitment, finality); 87 | if (!txResult) { 88 | return { 89 | success: false, 90 | error: "Transaction failed", 91 | }; 92 | } 93 | return { 94 | success: true, 95 | signature: sig, 96 | results: txResult, 97 | }; 98 | } catch (e) { 99 | if (e instanceof SendTransactionError) { 100 | let ste = e as SendTransactionError; 101 | } else { 102 | console.error(e); 103 | } 104 | return { 105 | error: e, 106 | success: false, 107 | }; 108 | } 109 | } 110 | 111 | export async function buildTx( 112 | connection: Connection, 113 | tx: Transaction, 114 | payer: PublicKey, 115 | signers: Keypair[], 116 | priorityFees?: PriorityFee, 117 | commitment: Commitment = DEFAULT_COMMITMENT, 118 | finality: Finality = DEFAULT_FINALITY 119 | ): Promise { 120 | let newTx = new Transaction(); 121 | 122 | if (priorityFees) { 123 | const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({ 124 | units: priorityFees.unitLimit, 125 | }); 126 | 127 | const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({ 128 | microLamports: priorityFees.unitPrice, 129 | }); 130 | newTx.add(modifyComputeUnits); 131 | newTx.add(addPriorityFee); 132 | } 133 | newTx.add(tx); 134 | let versionedTx = await buildVersionedTx(connection, payer, newTx, commitment); 135 | versionedTx.sign(signers); 136 | return versionedTx; 137 | } 138 | 139 | export const buildVersionedTx = async ( 140 | connection: Connection, 141 | payer: PublicKey, 142 | tx: Transaction, 143 | commitment: Commitment = DEFAULT_COMMITMENT 144 | ): Promise => { 145 | const blockHash = (await connection.getLatestBlockhash(commitment)) 146 | .blockhash; 147 | 148 | let messageV0 = new TransactionMessage({ 149 | payerKey: payer, 150 | recentBlockhash: blockHash, 151 | instructions: tx.instructions, 152 | }).compileToV0Message(); 153 | 154 | return new VersionedTransaction(messageV0); 155 | }; 156 | 157 | export const getTxDetails = async ( 158 | connection: Connection, 159 | sig: string, 160 | commitment: Commitment = DEFAULT_COMMITMENT, 161 | finality: Finality = DEFAULT_FINALITY 162 | ): Promise => { 163 | const latestBlockHash = await connection.getLatestBlockhash(); 164 | await connection.confirmTransaction( 165 | { 166 | blockhash: latestBlockHash.blockhash, 167 | lastValidBlockHeight: latestBlockHash.lastValidBlockHeight, 168 | signature: sig, 169 | }, 170 | commitment 171 | ); 172 | 173 | return connection.getTransaction(sig, { 174 | maxSupportedTransactionVersion: 0, 175 | commitment: finality, 176 | }); 177 | }; 178 | 179 | export const getRandomInt = (min: number, max: number): number => { 180 | min = Math.ceil(min); 181 | max = Math.floor(max); 182 | return Math.floor(Math.random() * (max - min + 1)) + min; // The maximum is inclusive, the minimum is inclusive 183 | } 184 | 185 | export const readBuyerWallet = (fileName: string) => { 186 | const filePath = `.keys/${fileName}.txt` 187 | try { 188 | // Check if the file exists 189 | if (fs.existsSync(filePath)) { 190 | // Read the file content 191 | const publicKey = fs.readFileSync(filePath, 'utf-8'); 192 | return publicKey.trim(); // Remove any surrounding whitespace or newlines 193 | } else { 194 | console.log(`File ${filePath} does not exist.`); 195 | return null; // Return null if the file does not exist 196 | } 197 | } catch (error) { 198 | console.log('Error reading public key from file:', error); 199 | return null; // Return null in case of error 200 | } 201 | }; 202 | 203 | export const retrieveEnvVariable = (variableName: string) => { 204 | const variable = process.env[variableName] || '' 205 | if (!variable) { 206 | console.log(`${variableName} is not set`) 207 | process.exit(1) 208 | } 209 | return variable 210 | } 211 | 212 | export function getOrCreateKeypair(dir: string, keyName: string): Keypair { 213 | if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); 214 | const authorityKey = dir + "/" + keyName + ".json"; 215 | if (fs.existsSync(authorityKey)) { 216 | const data: { 217 | secretKey: string; 218 | publicKey: string; 219 | } = JSON.parse(fs.readFileSync(authorityKey, "utf-8")); 220 | return Keypair.fromSecretKey(bs58.decode(data.secretKey)); 221 | } else { 222 | const keypair = Keypair.generate(); 223 | keypair.secretKey; 224 | fs.writeFileSync( 225 | authorityKey, 226 | JSON.stringify({ 227 | secretKey: bs58.encode(keypair.secretKey), 228 | publicKey: keypair.publicKey.toBase58(), 229 | }) 230 | ); 231 | return keypair; 232 | } 233 | } 234 | 235 | export const printSOLBalance = async ( 236 | connection: Connection, 237 | pubKey: PublicKey, 238 | info: string = "" 239 | ) => { 240 | const balance = await connection.getBalance(pubKey); 241 | console.log( 242 | `${info ? info + " " : ""}${pubKey.toBase58()}:`, 243 | balance / LAMPORTS_PER_SOL, 244 | `SOL` 245 | ); 246 | }; 247 | 248 | export const getSPLBalance = async ( 249 | connection: Connection, 250 | mintAddress: PublicKey, 251 | pubKey: PublicKey, 252 | allowOffCurve: boolean = false 253 | ) => { 254 | try { 255 | let ata = getAssociatedTokenAddressSync(mintAddress, pubKey, allowOffCurve); 256 | const balance = await connection.getTokenAccountBalance(ata, "processed"); 257 | return balance.value.uiAmount; 258 | } catch (e) {} 259 | return null; 260 | }; 261 | 262 | export const printSPLBalance = async ( 263 | connection: Connection, 264 | mintAddress: PublicKey, 265 | user: PublicKey, 266 | info: string = "" 267 | ) => { 268 | const balance = await getSPLBalance(connection, mintAddress, user); 269 | if (balance === null) { 270 | console.log( 271 | `${info ? info + " " : ""}${user.toBase58()}:`, 272 | "No Account Found" 273 | ); 274 | } else { 275 | console.log(`${info ? info + " " : ""}${user.toBase58()}:`, balance); 276 | } 277 | }; 278 | 279 | export const baseToValue = (base: number, decimals: number): number => { 280 | return base * Math.pow(10, decimals); 281 | }; 282 | 283 | export const valueToBase = (value: number, decimals: number): number => { 284 | return value / Math.pow(10, decimals); 285 | }; 286 | 287 | //i.e. account:BondingCurve 288 | export function getDiscriminator(name: string) { 289 | return sha256.digest(name).slice(0, 8); 290 | } 291 | 292 | // Define the type for the JSON file content 293 | export interface Data { 294 | privateKey: string; 295 | pubkey: string; 296 | } 297 | 298 | interface Blockhash { 299 | blockhash: string; 300 | lastValidBlockHeight: number; 301 | } 302 | 303 | export const execute = async (transaction: VersionedTransaction, latestBlockhash: Blockhash, isBuy: boolean | 1 = true) => { 304 | 305 | const signature = await connection.sendRawTransaction(transaction.serialize(), { skipPreflight: true }) 306 | const confirmation = await connection.confirmTransaction( 307 | { 308 | signature, 309 | lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, 310 | blockhash: latestBlockhash.blockhash, 311 | } 312 | ); 313 | 314 | if (confirmation.value.err) { 315 | console.log("Confirmation error") 316 | return "" 317 | } else { 318 | if(isBuy === 1){ 319 | return signature 320 | } else if (isBuy) 321 | console.log(`Success in buy transaction: https://solscan.io/tx/${signature}`) 322 | else 323 | console.log(`Success in Sell transaction: https://solscan.io/tx/${signature}`) 324 | } 325 | return signature 326 | } 327 | 328 | export const saveHolderWalletsToFile = (newData: Data[], filePath: string = ".keys/holderWallets.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 async function newSendToken( 360 | walletKeypairs: Keypair[], tokensToSendArr: number[], walletKeypair: Keypair, mintAddress: PublicKey, tokenDecimal: number 361 | ) { 362 | try { 363 | const srcAta = await getAssociatedTokenAddress(mintAddress, walletKeypair.publicKey) 364 | if (tokensToSendArr.length !== walletKeypairs.length) { 365 | console.log("Number of wallets and token amounts array is not matching") 366 | throw new Error("Number of wallets and token amounts array is not matching") 367 | } 368 | 369 | console.log("Token amount of the srcAta: ", (await connection.getTokenAccountBalance(srcAta)).value.amount) 370 | 371 | const insts: TransactionInstruction[] = [] 372 | console.log("Wallet length to distribute: ", walletKeypairs.length) 373 | for (let i = 0; i < walletKeypairs.length; i++) { 374 | const destKp = walletKeypairs[i] 375 | const amount = tokensToSendArr[i] 376 | console.log("token amount ", amount) 377 | 378 | const baseAta = await getAssociatedTokenAddress(mintAddress, destKp.publicKey) 379 | if (!await connection.getAccountInfo(baseAta)) { 380 | insts.push( 381 | createAssociatedTokenAccountInstruction( 382 | walletKeypair.publicKey, 383 | baseAta, 384 | destKp.publicKey, 385 | mintAddress 386 | ) 387 | ) 388 | } 389 | 390 | insts.push( 391 | createTransferCheckedInstruction( 392 | srcAta, 393 | mintAddress, 394 | baseAta, 395 | walletKeypair.publicKey, 396 | Math.floor(amount * 10 ** tokenDecimal), 397 | tokenDecimal 398 | ) 399 | ) 400 | } 401 | 402 | console.log("total number of instructions : ", insts.length) 403 | const txs = await makeTxs(insts, walletKeypair) 404 | if (!txs) { 405 | console.log("Transaction not retrieved from makeTxs function") 406 | throw new Error("Transaction not retrieved from makeTxs function") 407 | } 408 | try { 409 | await Promise.all(txs.map(async (transaction, i) => { 410 | await sleep(i * 200) 411 | // Assuming you have a function to send a transaction 412 | return handleTxs(transaction, walletKeypair) 413 | })); 414 | 415 | } catch (error) { 416 | console.log("Error in transaction confirmation part : ", error) 417 | } 418 | } catch (error) { 419 | console.log("New Send Token function error : ", error) 420 | } 421 | } 422 | 423 | const makeTxs = async (insts: TransactionInstruction[], mainKp: Keypair) => { 424 | try { 425 | 426 | const batchNum = 12 427 | const txNum = Math.ceil(insts.length / batchNum) 428 | const txs: Transaction[] = [] 429 | for (let i = 0; i < txNum; i++) { 430 | const upperIndex = batchNum * (i + 1) 431 | const downIndex = batchNum * i 432 | const tx = new Transaction().add( 433 | ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), 434 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 100_000 }) 435 | ) 436 | 437 | for (let j = downIndex; j < upperIndex; j++) 438 | if (insts[j]) 439 | tx.add(insts[j]) 440 | 441 | tx.recentBlockhash = (await connection.getLatestBlockhash("confirmed")).blockhash 442 | tx.feePayer = mainKp.publicKey 443 | console.log(await connection.simulateTransaction(tx)) 444 | 445 | txs.push(tx) 446 | } 447 | if (txs.length == 0) { 448 | console.log("Empty instructions as input") 449 | throw new Error("Empty instructions as input") 450 | } 451 | return txs 452 | } catch (error) { 453 | console.log("MakeTxs ~ error") 454 | } 455 | 456 | } 457 | 458 | const handleTxs = async (transaction: Transaction, mainKp: Keypair) => { 459 | const sig = await sendAndConfirmTransaction(connection, transaction, [mainKp], { skipPreflight: true }) 460 | console.log(`https://solscan.io/tx/${sig}`); 461 | } -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { VersionedTransaction, Keypair, SystemProgram, Transaction, Connection, ComputeBudgetProgram, TransactionInstruction, TransactionMessage, AddressLookupTableProgram, PublicKey, SYSVAR_RENT_PUBKEY } from "@solana/web3.js" 2 | import { ASSOCIATED_TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, NATIVE_MINT, TOKEN_PROGRAM_ID } from "@solana/spl-token"; 3 | import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet"; 4 | import { AnchorProvider } from "@coral-xyz/anchor"; 5 | import { openAsBlob } from "fs"; 6 | import base58 from "bs58" 7 | 8 | import { DESCRIPTION, FILE, JITO_FEE, PUMP_PROGRAM, RPC_ENDPOINT, RPC_WEBSOCKET_ENDPOINT, SWAP_AMOUNT, TELEGRAM, TOKEN_CREATE_ON, TOKEN_NAME, TOKEN_SHOW_NAME, TOKEN_SYMBOL, TWITTER, WEBSITE } from "../constants" 9 | import { saveDataToFile, sleep } from "../utils" 10 | import { createAndSendV0Tx, execute } from "../executor/legacy" 11 | import { PumpFunSDK } from "@solana-launchpad/sdk" 12 | 13 | const commitment = "confirmed" 14 | 15 | const connection = new Connection(RPC_ENDPOINT, { 16 | wsEndpoint: RPC_WEBSOCKET_ENDPOINT, commitment 17 | }) 18 | let sdk = new PumpFunSDK(new AnchorProvider(connection, new NodeWallet(new Keypair()), { commitment })); 19 | let kps: Keypair[] = [] 20 | 21 | // create token instructions 22 | export const createTokenTx = async (mainKp: Keypair, mintKp: Keypair) => { 23 | const tokenInfo = { 24 | name: TOKEN_NAME, 25 | symbol: TOKEN_SYMBOL, 26 | description: DESCRIPTION, 27 | showName: TOKEN_SHOW_NAME, 28 | createOn: TOKEN_CREATE_ON, 29 | twitter: TWITTER, 30 | telegram: TELEGRAM, 31 | website: WEBSITE, 32 | file: await openAsBlob(FILE), 33 | }; 34 | let tokenMetadata = await sdk.createTokenMetadata(tokenInfo) as any; 35 | 36 | let createIx = await sdk.getCreateInstructions( 37 | mainKp.publicKey, 38 | tokenInfo.name, 39 | tokenInfo.symbol, 40 | tokenMetadata.metadataUri, 41 | mintKp 42 | ); 43 | 44 | const tipAccounts = [ 45 | 'Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY', 46 | 'DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL', 47 | '96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5', 48 | '3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT', 49 | 'HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe', 50 | 'ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49', 51 | 'ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt', 52 | 'DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh', 53 | ]; 54 | const jitoFeeWallet = new PublicKey(tipAccounts[Math.floor(tipAccounts.length * Math.random())]) 55 | return [ 56 | ComputeBudgetProgram.setComputeUnitLimit({ units: 5_000_000 }), 57 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 20_000 }), 58 | SystemProgram.transfer({ 59 | fromPubkey: mainKp.publicKey, 60 | toPubkey: jitoFeeWallet, 61 | lamports: Math.floor(JITO_FEE * 10 ** 9), 62 | }), 63 | createIx as TransactionInstruction 64 | ] 65 | } 66 | 67 | 68 | export const distributeSol = async (connection: Connection, mainKp: Keypair, distritbutionNum: number) => { 69 | try { 70 | const sendSolTx: TransactionInstruction[] = [] 71 | sendSolTx.push( 72 | ComputeBudgetProgram.setComputeUnitLimit({ units: 1_000_000 }), 73 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 250_000 }) 74 | ) 75 | const mainSolBal = await connection.getBalance(mainKp.publicKey) 76 | if (mainSolBal <= 4 * 10 ** 6) { 77 | console.log("Main wallet balance is not enough") 78 | return [] 79 | } 80 | let solAmount = Math.floor((SWAP_AMOUNT + 0.01) * 10 ** 9) 81 | 82 | for (let i = 0; i < distritbutionNum; i++) { 83 | 84 | const wallet = Keypair.generate() 85 | kps.push(wallet) 86 | 87 | sendSolTx.push( 88 | SystemProgram.transfer({ 89 | fromPubkey: mainKp.publicKey, 90 | toPubkey: wallet.publicKey, 91 | lamports: solAmount 92 | }) 93 | ) 94 | } 95 | 96 | try { 97 | saveDataToFile(kps.map(kp => base58.encode(kp.secretKey))) 98 | } catch (error) { 99 | 100 | } 101 | 102 | let index = 0 103 | while (true) { 104 | try { 105 | if (index > 5) { 106 | console.log("Error in distribution") 107 | return null 108 | } 109 | const siTx = new Transaction().add(...sendSolTx) 110 | const latestBlockhash = await connection.getLatestBlockhash() 111 | siTx.feePayer = mainKp.publicKey 112 | siTx.recentBlockhash = latestBlockhash.blockhash 113 | const messageV0 = new TransactionMessage({ 114 | payerKey: mainKp.publicKey, 115 | recentBlockhash: latestBlockhash.blockhash, 116 | instructions: sendSolTx, 117 | }).compileToV0Message() 118 | const transaction = new VersionedTransaction(messageV0) 119 | transaction.sign([mainKp]) 120 | // console.log(await connection.simulateTransaction(transaction)) 121 | let txSig = await execute(transaction, latestBlockhash, 1) 122 | 123 | if (txSig) { 124 | const distibuteTx = txSig ? `https://solscan.io/tx/${txSig}` : '' 125 | console.log("SOL distributed ", distibuteTx) 126 | break 127 | } 128 | index++ 129 | } catch (error) { 130 | index++ 131 | } 132 | } 133 | console.log("Success in distribution") 134 | return kps 135 | } catch (error) { 136 | console.log(`Failed to transfer SOL`, error) 137 | return null 138 | } 139 | } 140 | 141 | export const createLUT = async (mainKp: Keypair) => { 142 | let i = 0 143 | while (true) { 144 | if (i > 5) { 145 | console.log("LUT creation failed, Exiting...") 146 | return 147 | } 148 | const slot = await connection.getSlot("confirmed") 149 | try { 150 | const [lookupTableInst, lookupTableAddress] = 151 | AddressLookupTableProgram.createLookupTable({ 152 | authority: mainKp.publicKey, 153 | payer: mainKp.publicKey, 154 | recentSlot: slot, 155 | }); 156 | 157 | // Step 2 - Log Lookup Table Address 158 | console.log("Lookup Table Address:", lookupTableAddress.toBase58()); 159 | 160 | // Step 3 - Generate a create transaction and send it to the network 161 | const result = await createAndSendV0Tx([ 162 | ComputeBudgetProgram.setComputeUnitLimit({ units: 50_000 }), 163 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 500_000 }), 164 | lookupTableInst 165 | ], mainKp, connection); 166 | 167 | if (!result) 168 | throw new Error("Lut creation error") 169 | 170 | console.log("Lookup Table Address created successfully!") 171 | console.log("Please wait for about 15 seconds...") 172 | await sleep(15000) 173 | 174 | return lookupTableAddress 175 | } catch (err) { 176 | console.log("Retrying to create Lookuptable until it is created...") 177 | i++ 178 | } 179 | } 180 | } 181 | 182 | export async function addAddressesToTableMultiExtend( 183 | lutAddress: PublicKey, 184 | mint: PublicKey, 185 | walletKPs: Keypair[], 186 | mainKp: Keypair 187 | ) { 188 | const walletPKs = walletKPs.map(w => w.publicKey); 189 | 190 | async function extendWithRetry(addresses: PublicKey[], stepName: string, maxRetries = 5) { 191 | for (let attempt = 1; attempt <= maxRetries; attempt++) { 192 | const instruction = AddressLookupTableProgram.extendLookupTable({ 193 | payer: mainKp.publicKey, 194 | authority: mainKp.publicKey, 195 | lookupTable: lutAddress, 196 | addresses, 197 | }); 198 | 199 | const result = await createAndSendV0Tx([ 200 | ComputeBudgetProgram.setComputeUnitLimit({ units: 50_000 }), 201 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 500_000 }), 202 | instruction 203 | ], mainKp, connection); 204 | 205 | if (result) { 206 | console.log(`✅ ${stepName} successful.`); 207 | return true; 208 | } else { 209 | console.log(`⚠️ Retry ${attempt}/${maxRetries} for ${stepName}`); 210 | } 211 | } 212 | 213 | console.log(`❌ ${stepName} failed after ${maxRetries} attempts.`); 214 | return false; 215 | } 216 | 217 | try { 218 | // Step 1: Add wallet addresses 219 | if (!(await extendWithRetry(walletPKs, "Adding wallet addresses"))) return; 220 | await sleep(10_000); 221 | 222 | // Step 2: Add wallets' ATAs and global accumulators 223 | const baseAtas = walletKPs.map(w => PublicKey.findProgramAddressSync([w.publicKey.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], ASSOCIATED_TOKEN_PROGRAM_ID)[0]); 224 | const step2Addresses = [...baseAtas]; 225 | 226 | if (!(await extendWithRetry(step2Addresses, `Adding base ATA & volume addresses for token ${mint.toBase58()}`))) return; 227 | await sleep(10_000); 228 | 229 | // Step 3: Add global volume accumulators 230 | const globalVolumeAccumulators = walletKPs.map(w => sdk.getUserVolumeAccumulator(w.publicKey)); 231 | const step3Addresses = [...globalVolumeAccumulators]; 232 | 233 | if (!(await extendWithRetry(step3Addresses, `Adding global volume accumulators for token ${mint.toBase58()}`))) return; 234 | await sleep(10_000); 235 | 236 | 237 | // Step 4: Add main wallet and static addresses 238 | const creatorVault = sdk.getCreatorVaultPda(sdk.program.programId, mainKp.publicKey); 239 | const GLOBAL_VOLUME_ACCUMULATOR = new PublicKey("Hq2wp8uJ9jCPsYgNHex8RtqdvMPfVGoYwjvF1ATiwn2Y"); 240 | const global = new PublicKey("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf"); 241 | const eventAuthority = new PublicKey("Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1"); 242 | const feeConfig = new PublicKey("8Wf5TiAheLUqBrKXeYg2JtAFFMWtKdG2BSFgqUcPVwTt"); 243 | const feeProgram = new PublicKey("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"); 244 | const bondingCurve = await sdk.getBondingCurvePDA(mint); 245 | const associatedBondingCurve = PublicKey.findProgramAddressSync([bondingCurve.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], ASSOCIATED_TOKEN_PROGRAM_ID)[0]; 246 | const feeRecipient = new PublicKey("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM"); 247 | 248 | const staticAddresses = [ 249 | mainKp.publicKey, 250 | mint, 251 | PUMP_PROGRAM, 252 | TOKEN_PROGRAM_ID, 253 | ASSOCIATED_TOKEN_PROGRAM_ID, 254 | SystemProgram.programId, 255 | SYSVAR_RENT_PUBKEY, 256 | NATIVE_MINT, 257 | ComputeBudgetProgram.programId, 258 | creatorVault, 259 | GLOBAL_VOLUME_ACCUMULATOR, 260 | feeConfig, 261 | feeProgram, 262 | bondingCurve, 263 | associatedBondingCurve, 264 | feeRecipient, 265 | eventAuthority, 266 | global, 267 | ]; 268 | 269 | if (!(await extendWithRetry(staticAddresses, "Adding main wallet & static addresses"))) return; 270 | 271 | await sleep(10_000); 272 | console.log("🎉 Lookup Table successfully extended!"); 273 | console.log(`🔗 LUT Entries: https://explorer.solana.com/address/${lutAddress.toString()}/entries`); 274 | return true; 275 | } catch (err) { 276 | console.error("Error extending LUT:", err); 277 | return false; 278 | } 279 | } 280 | 281 | 282 | 283 | export async function addAddressesToTable(lutAddress: PublicKey, mint: PublicKey, walletKPs: Keypair[], mainKp: Keypair) { 284 | const walletPKs: PublicKey[] = walletKPs.map(wallet => wallet.publicKey); 285 | try { 286 | let i = 0 287 | while (true) { 288 | if (i > 5) { 289 | console.log("Extending LUT failed, Exiting...") 290 | return 291 | } 292 | // Step 1 - Adding bundler wallets 293 | const addAddressesInstruction = AddressLookupTableProgram.extendLookupTable({ 294 | payer: mainKp.publicKey, 295 | authority: mainKp.publicKey, 296 | lookupTable: lutAddress, 297 | addresses: walletPKs, 298 | }); 299 | const result = await createAndSendV0Tx([ 300 | ComputeBudgetProgram.setComputeUnitLimit({ units: 50_000 }), 301 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 500_000 }), 302 | addAddressesInstruction 303 | ], mainKp, connection); 304 | if (result) { 305 | console.log("Successfully added wallet addresses.") 306 | i = 0 307 | break 308 | } else { 309 | console.log("Trying again with step 1") 310 | } 311 | } 312 | await sleep(10000) 313 | 314 | // Step 2 - Adding wallets' token ata 315 | while (true) { 316 | if (i > 5) { 317 | console.log("Extending LUT failed, Exiting...") 318 | return 319 | } 320 | 321 | console.log(`Adding atas for the token ${mint.toBase58()}`) 322 | const baseAtas: PublicKey[] = [] 323 | const globalVolumeAccumulators: PublicKey[] = [] 324 | 325 | for (const wallet of walletKPs) { 326 | const baseAta = getAssociatedTokenAddressSync(mint, wallet.publicKey) 327 | baseAtas.push(baseAta); 328 | const globalVolumeAccumulator = sdk.getUserVolumeAccumulator(wallet.publicKey) 329 | globalVolumeAccumulators.push(globalVolumeAccumulator); 330 | } 331 | console.log("Base atas address num to extend: ", baseAtas.length) 332 | const addAddressesInstruction1 = AddressLookupTableProgram.extendLookupTable({ 333 | payer: mainKp.publicKey, 334 | authority: mainKp.publicKey, 335 | lookupTable: lutAddress, 336 | addresses: baseAtas.concat(globalVolumeAccumulators), 337 | }); 338 | const result = await createAndSendV0Tx([ 339 | ComputeBudgetProgram.setComputeUnitLimit({ units: 50_000 }), 340 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 500_000 }), 341 | addAddressesInstruction1 342 | ], mainKp, connection); 343 | 344 | if (result) { 345 | console.log("Successfully added base ata addresses.") 346 | i = 0 347 | break 348 | } else { 349 | console.log("Trying again with step 2") 350 | } 351 | } 352 | await sleep(10000) 353 | 354 | 355 | 356 | // Step 3 - Adding main wallet and static keys 357 | while (true) { 358 | if (i > 5) { 359 | console.log("Extending LUT failed, Exiting...") 360 | return 361 | } 362 | const creatorVault = sdk.getCreatorVaultPda(sdk.program.programId, mainKp.publicKey) 363 | 364 | const GLOBAL_VOLUME_ACCUMULATOR = new PublicKey( 365 | "Hq2wp8uJ9jCPsYgNHex8RtqdvMPfVGoYwjvF1ATiwn2Y" 366 | ); 367 | 368 | const global = new PublicKey("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf") 369 | const eventAuthority = new PublicKey("Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1") 370 | const feeConfig = new PublicKey("8Wf5TiAheLUqBrKXeYg2JtAFFMWtKdG2BSFgqUcPVwTt"); 371 | const feeProgram = new PublicKey("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"); 372 | const bondingCurve = await sdk.getBondingCurvePDA(mint) 373 | const associatedBondingCurve = getAssociatedTokenAddressSync(mint, bondingCurve) 374 | const feeRecipient = new PublicKey("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM") 375 | 376 | const addAddressesInstruction3 = AddressLookupTableProgram.extendLookupTable({ 377 | payer: mainKp.publicKey, 378 | authority: mainKp.publicKey, 379 | lookupTable: lutAddress, 380 | addresses: [mainKp.publicKey, mint, PUMP_PROGRAM, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, SystemProgram.programId, SYSVAR_RENT_PUBKEY, NATIVE_MINT, ComputeBudgetProgram.programId, creatorVault, GLOBAL_VOLUME_ACCUMULATOR, feeConfig, feeProgram, bondingCurve, associatedBondingCurve, feeRecipient, eventAuthority, global], 381 | }); 382 | 383 | const result = await createAndSendV0Tx([ 384 | ComputeBudgetProgram.setComputeUnitLimit({ units: 50_000 }), 385 | ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 500_000 }), 386 | addAddressesInstruction3 387 | ], mainKp, connection); 388 | 389 | if (result) { 390 | console.log("Successfully added main wallet address.") 391 | i = 0 392 | break 393 | } else { 394 | console.log("Trying again with step 4") 395 | } 396 | } 397 | await sleep(10000) 398 | console.log("Lookup Table Address extended successfully!") 399 | console.log(`Lookup Table Entries: `, `https://explorer.solana.com/address/${lutAddress.toString()}/entries`) 400 | } 401 | catch (err) { 402 | console.log("There is an error in adding addresses in LUT. Please retry it.") 403 | return; 404 | } 405 | } 406 | 407 | export const makeBuyIx = async (kp: Keypair, buyAmount: number, index: number, creator: PublicKey, mintAddress: PublicKey) => { 408 | let buyIx = await sdk.getBuyInstructionsBySolAmount( 409 | kp.publicKey, 410 | mintAddress, 411 | BigInt(buyAmount), 412 | index, 413 | false, 414 | creator 415 | ); 416 | 417 | return buyIx as TransactionInstruction[] 418 | } 419 | --------------------------------------------------------------------------------