1 Star 0 Fork 0

叮咚/solana-sniper-bot

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
solanaSniperBotWork.ts 35.11 KB
一键复制 编辑 原始数据 按行查看 历史
叮咚 提交于 10个月前 . test
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801
import bs58 from 'bs58';
import * as fs from 'fs';
// import { SearcherClient } from 'jito-ts/dist/sdk/block-engine/searcher.js';
import * as path from 'path';
import pino from 'pino';
import { Worker } from 'worker_threads';
import {
BigNumberish,
Liquidity,
LIQUIDITY_STATE_LAYOUT_V4,
LiquidityPoolKeys,
LiquidityStateV4,
MARKET_STATE_LAYOUT_V3,
MarketStateV3,
SPL_ACCOUNT_LAYOUT,
Token,
TokenAmount,
} from '@raydium-io/raydium-sdk';
import {
AccountLayout,
createAssociatedTokenAccountIdempotentInstruction,
createCloseAccountInstruction,
getAssociatedTokenAddressSync,
TOKEN_PROGRAM_ID,
} from '@solana/spl-token';
import {
ComputeBudgetProgram,
Connection,
KeyedAccountInfo,
Keypair,
LAMPORTS_PER_SOL,
PublicKey,
TransactionMessage,
VersionedTransaction,
} from '@solana/web3.js';
import {
COMMITMENT_LEVEL,
MAX_SELL_RETRIES,
MIN_POOL_SIZE,
PRIVATE_KEY,
QUOTE_AMOUNT,
QUOTE_MINT,
RPC_ENDPOINT,
RPC_WEBSOCKET_ENDPOINT,
SNIPE_LIST_REFRESH_INTERVAL,
USE_SNIPE_LIST,
} from './constants';
import {
createPoolKeys,
getTokenAccounts,
OPENBOOK_PROGRAM_ID,
RAYDIUM_LIQUIDITY_PROGRAM_ID_V4,
} from './liquidity';
import {
getMinimalMarketV3,
MinimalMarketLayoutV3,
} from './market';
import { MintLayout } from './types';
const chalk = require("chalk");
const BigNumber = require("bignumber.js");
const Decimal = require('decimal.js');
const BN = require('bn.js');
//待卖出代币map key:token
let pendingSellTokenMap = new Map();
const worker = new Worker('./solanaSniperBotWorkThread.js', {
workerData: {
path: './solanaSniperBotWorkThread.ts'
}
});
worker.on('message', (msg) => {
if ("sellToken" == msg.action) {
//全部卖出
let tokenAddress = msg.data;
let pendingSellToken = pendingSellTokenMap.get(tokenAddress);
if (pendingSellToken != undefined) {
sellToken2(pendingSellToken.tokenInfo.pair, pendingSellToken.tokenInfo.tokenAddress, pendingSellToken.tokenInfo.minAmountOut);
pendingSellTokenMap.delete(msg.data);
}
} else if ("excuteBuyToken" == msg.action) {
try {
pendingSellTokenMap.set(msg.data.tokenInfo.tokenAddress, msg.data);
let pendingSellTokenInfo = pendingSellTokenMap.get(msg.data.tokenInfo.tokenAddress);
if (pendingSellTokenInfo != undefined) {
console.log(">>>>>>(主线程操作)【四、接收子线程传入需要购买的新代币】: " + pendingSellTokenInfo.tokenInfo.tokenAddress + " openNowRiseRate:" + pendingSellTokenInfo.tokenInfo.openNowRiseRate);
buy(pendingSellTokenInfo.tokenInfo.pair, pendingSellTokenInfo.tokenInfo.poolState, pendingSellTokenInfo.tokenInfo.tokenAddress);//买入接口
// worker.postMessage({ "action": "updatePendingSellTokenInfo", "data": pendingSellTokenMap.get(tokenAddress) });
}
} catch (error) {
}
} else if ("removeToken" == msg.action) {
//移除未用的token
let pendingSellToken = pendingSellTokenMap.get(msg.data);
if (pendingSellToken != undefined) {
pendingSellTokenMap.delete(msg.data);
}
}
});
worker.on("error", error => {
console.log("子线程挂了", error);
});
worker.on("exit", exitCode => {
console.log(`子线程退出了 ${exitCode}`);
})
const transport = pino.transport(
{
targets: [
{
level: 'trace',
target: 'pino/file',
options: {
destination: 'buy.log',
},
},
],
}
);
export const logger = pino(
{
redact: ['poolkeys'],
serializers: {
error: pino.stdSerializers.err,
},
base: undefined,
timestamp: () => `,"time":"${new Date().toLocaleString('zh', { timeZone: 'Asia/Shanghai', hour12: false })}"`,
},
transport,
);
const solanaConnection = new Connection(RPC_ENDPOINT, { wsEndpoint: RPC_WEBSOCKET_ENDPOINT, });
export interface MinimalTokenAccountData { mint: PublicKey; address: PublicKey; poolKeys?: LiquidityPoolKeys; market?: MinimalMarketLayoutV3; }
const existingLiquidityPools: Set<string> = new Set<string>();
const existingOpenBookMarkets: Set<string> = new Set<string>();
const existingTokenAccounts: Map<string, MinimalTokenAccountData> = new Map<string, MinimalTokenAccountData>();
let wallet: Keypair;
let quoteToken: Token;
let quoteTokenAssociatedAddress: PublicKey;
let quoteAmount: TokenAmount;
let quoteMinPoolSizeAmount: TokenAmount;
let snipeList: string[] = [];
// let searcherClients: SearcherClient;
async function init(): Promise<void> {
//请注意killall -9 node 是强制关闭,不能被监听到,需要使用 killall -15 node
process.on('SIGTERM', function () {
console.log("强势币系统正在关闭......请稍等,执行相关保存任务中");
savePendingSellTokens();
console.log("执行完毕,正在退出");
process.exit(0);
});
// logger.level = LOG_LEVEL;
// 加载钱包
wallet = Keypair.fromSecretKey(bs58.decode(PRIVATE_KEY));
// searcherClients = jitoSearcherClient('frankfurt.mainnet.block-engine.jito.wtf', Keypair.fromSecretKey(bs58.decode('YVHkhmzQ8ikcoqkmRjP7z1AiSadKC391meAVsJMLQEUVKRPfM2yNVHVuL9k2Q3v4VBBRyDPhLKKQYQvZmmR7wmW')), { 'grpc.keepalive_timeout_ms': 4000, });
// console.log(chalk.red.bold.underline("******【初始化searcherClients】" + (await searcherClients.getTipAccounts())[0]));
// logger.info(`钱包地址: ${wallet.publicKey}`);
console.log(chalk.bgYellow.black(`我的钱包地址: ${wallet.publicKey}`));
// 获取最小的购买量
switch (QUOTE_MINT) {
case "WSOL": {
quoteToken = Token.WSOL;
quoteAmount = new TokenAmount(Token.WSOL, QUOTE_AMOUNT, false);
quoteMinPoolSizeAmount = new TokenAmount(
quoteToken,
MIN_POOL_SIZE,
false
);
break;
}
case "USDC": {
quoteToken = new Token(TOKEN_PROGRAM_ID, new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), 6, "USDC", "USDC");
quoteAmount = new TokenAmount(quoteToken, QUOTE_AMOUNT, false);
quoteMinPoolSizeAmount = new TokenAmount(
quoteToken,
MIN_POOL_SIZE,
false
);
break;
}
default: {
throw new Error(
`Unsupported quote mint "${QUOTE_MINT}". Supported values are USDC and WSOL`
);
}
}
// logger.info(`是否开启列表监听: ${USE_SNIPE_LIST}`);
// logger.info(`检查是否撤流: ${CHECK_IF_MINT_IS_RENOUNCED}`);
// logger.info(`池子最小标准: ${quoteMinPoolSizeAmount.isZero() ? "false" : quoteMinPoolSizeAmount.toFixed()} ${quoteToken.symbol}`);
// logger.info(`买入sol金额: ${formatAmount(quoteAmount.toFixed())} ${quoteToken.symbol}`);
// logger.info(`是否自动卖出: ${AUTO_SELL}`);
// logger.info(`自动卖出时间: ${AUTO_SELL_DELAY === 0 ? "false" : AUTO_SELL_DELAY}`);
// 检查要兑换的主流币(wsol)在钱包账户中是否有余额 start
const tokenAccounts = await getTokenAccounts(solanaConnection, wallet.publicKey, COMMITMENT_LEVEL);
for (const ta of tokenAccounts) {
existingTokenAccounts.set(ta.accountInfo.mint.toString(), <MinimalTokenAccountData>{
mint: ta.accountInfo.mint,
address: ta.pubkey,
});
}
const tokenAccount = tokenAccounts.find((acc) => acc.accountInfo.mint.toString() === quoteToken.mint.toString())!;
if (!tokenAccount) {
throw new Error(`该钱包地址: ${wallet.publicKey} 中没有 ${quoteToken.symbol},请先充值WSOL`);
}
quoteTokenAssociatedAddress = new PublicKey(tokenAccount.pubkey);
// 检查要兑换的主流币(wsol)在钱包账户中是否有余额 end
// 加载狙击列表
loadSnipeList();
}
function saveTokenAccount(mint: PublicKey, accountData: MinimalMarketLayoutV3) {
const ata = getAssociatedTokenAddressSync(new PublicKey(mint), wallet.publicKey);
const tokenAccount = <MinimalTokenAccountData>{
address: ata,
mint: mint,
market: <MinimalMarketLayoutV3>{ bids: accountData.bids, asks: accountData.asks, eventQueue: accountData.eventQueue, },
};
existingTokenAccounts.set(mint.toString(), tokenAccount);
return tokenAccount;
}
export async function processRaydiumPool(id: PublicKey, poolState: LiquidityStateV4, pair: string, solAmount: number, currentTokenPrice: number, updatedAccountInfo: KeyedAccountInfo) {
try {
if (!shouldBuy(poolState.baseMint.toString())) {
return;
}
let { data } = (await solanaConnection.getAccountInfo(poolState.baseMint)) || {};//获取该币raydium池的信息
if (!data) {
return;
}
//貔貅过滤
const deserialize = MintLayout.decode(data);
const mintAuthorityOption = deserialize.mintAuthorityOption;//0=弃权 1=未弃权
const tokennOwner = deserialize.mintAuthority.toString();//铸造代币的owner
if (mintAuthorityOption == 1) {
// console.log('******(主线程操作)拥有代币的Owner: ', tokennOwner, "mint: " + poolState.baseMint.toBase58(), "该代币尚未弃权!!!");
return;
}
if (mintAuthorityOption == 0) {
try {
const tokenAccounts = await solanaConnection.getTokenAccountsByOwner(new PublicKey(tokennOwner), { "programId": new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA") });
if (tokenAccounts.value.length > 10) {
return;
}
if (tokenAccounts.value.length > 0) {
const displayData = [];
for (const { pubkey, account } of tokenAccounts.value) {
{
displayData.push({
"owner": tokennOwner,
"tokenAccount": pubkey.toBase58(),
"mint": SPL_ACCOUNT_LAYOUT.decode(account.data).mint.toBase58(),
"amount": Number(SPL_ACCOUNT_LAYOUT.decode(account.data).amount)
});
}
}
console.table("\n");
console.log(chalk.red.bold.underline("******(主线程操作)【一、Owner拥有代币明细情况】****** 代币地址(mint Account): " + poolState.baseMint.toBase58() + " , Owner拥有者: " + tokennOwner));
console.table(displayData);
}
} catch (error) {
}
}
//打印日志===start
const poolOpenTime = parseInt(poolState.poolOpenTime.toString());
pendingSellTokenMap.set(poolState.baseMint.toString(), {
"tokenInfo": {
"pair": pair,
"poolState": poolState,
"updatedAccountInfo": updatedAccountInfo,
"tokenAddress": poolState.baseMint.toString(),
"owner": deserialize.mintAuthority.toString(),
"quoteVault": poolState.quoteVault.toString(),//token account(wsol加池用户,即获取池子wsol大小的balance用户)
"baseVault": poolState.baseVault.toString(),//token account(代币拥有者的账户,即代币balance的用户)
"marketId": poolState.marketId.toString(),
"poolOpenTime": new Date(poolOpenTime * 1000),
"initSol": solAmount,
"maxPrice": 0,
"openNowRiseRate": 0,
"openMaxRiseRate": 0,
"openTime": new Date(),
"nowPrice": 0,
"lastPrice": 0,
"startTime": 0,
"updatePriceTime": 0,
"maxPriceTime": 0,
"buyPrice": currentTokenPrice,
"buyTime": new Date(),
"sellPrincipalFalg": 0,
"sellPrincipalTime": new Date(),
"tokenDecimals": deserialize.decimals,
"ctime": new Date(),
"utime": new Date(),
"removeLiquidityTime": 0,
"minAmountOut": 0,
"timeMinDiff": 0,
"upCounts": 0,
"dowCounts": 0,
"lastRiseRate": 0,
"remarks": ''
}
});
worker.postMessage({ "action": "savePendingSellToken", "data": pendingSellTokenMap.get(poolState.baseMint.toString()) });
//打印日志===end
} catch (e) {
logger.error({ ...poolState, error: e }, "Failed to process pool");
}
}
export async function processOpenBookMarket(updatedAccountInfo: KeyedAccountInfo) {
let accountData: MarketStateV3 | undefined;
try {
accountData = MARKET_STATE_LAYOUT_V3.decode(updatedAccountInfo.accountInfo.data);
// 为了保证交易速度,这里需要在购买代币之前手机Openbook数据...
if (existingTokenAccounts.has(accountData.baseMint.toString())) {
return;
}
saveTokenAccount(new PublicKey(accountData.baseMint), accountData);
} catch (e) {
// logger.debug(e);
// logger.error({ mint: accountData?.baseMint }, `Failed to process market`);
logger.error({ ...accountData, error: e }, `Failed to process market`);
}
}
async function buy(accountId: PublicKey, accountData: LiquidityStateV4, tokenAddress: any): Promise<void> {
let pendingSellToken = pendingSellTokenMap.get(tokenAddress);
if (pendingSellToken == undefined) {
return;
}
try {
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>买入时获取Raydium分配的关联账户信息00000>>>>>>】 accountId:::" + accountId));
let tokenAccount = existingTokenAccounts.get(pendingSellToken.tokenInfo.tokenAddress);
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>买入时获取Raydium分配的关联账户信息11111>>>>>>】 tokenAccount:::" + tokenAccount));
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>买入时获取Raydium分配的关联账户信息22222>>>>>>】 !tokenAccount:::" + !tokenAccount));
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>买入时获取Raydium分配的关联账户信息33333>>>>>>】 accountData.marketId:::" + pendingSellToken.tokenInfo.marketId.toString()));
if (!tokenAccount) {
// 这里可能获取OpenBook数据时间不足
const market = await getMinimalMarketV3(solanaConnection, new PublicKey(pendingSellToken.tokenInfo.marketId.toString()), COMMITMENT_LEVEL);
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>买入时获取Raydium分配的关联账户信息55555>>>>>>】 market:::" + market));
tokenAccount = saveTokenAccount(new PublicKey(pendingSellToken.tokenInfo.tokenAddress), market);
}
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>买入时获取Raydium分配的关联账户信息44444>>>>>>】 tokenAccount:::" + tokenAccount));
const displayData0 = [
{ "关联账户TokenAccount": tokenAccount.address.toString(), "tokenAccountIn(兑换的主流币)": quoteTokenAssociatedAddress.toString(), "tokenAccountOut(兑换的代币)": tokenAccount.mint.toString(), "我的钱包地址": wallet.publicKey.toString() }
];
console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>买入时获取Raydium分配的关联账户信息>>>>>>】 accountId:::" + accountId));
console.table(displayData0);
const accountData = LIQUIDITY_STATE_LAYOUT_V4.decode(
pendingSellToken.tokenInfo.updatedAccountInfo.accountInfo.data
);
// console.log(chalk.black("=======accountData.baseMint==========: " + accountData.baseMint.toString()));
tokenAccount.poolKeys = createPoolKeys(new PublicKey(accountId), accountData, tokenAccount.market!);
const { innerTransaction } = Liquidity.makeSwapFixedInInstruction(
{
poolKeys: tokenAccount.poolKeys,
userKeys: {
tokenAccountIn: quoteTokenAssociatedAddress,
tokenAccountOut: tokenAccount.address,
owner: wallet.publicKey,
},
amountIn: quoteAmount.raw,
minAmountOut: 0,
},
tokenAccount.poolKeys.version
);
const displayData = [
// { "buy-id(pairAddress)": accountId.toString(), "marketId": tokenAccount?.poolKeys.marketId.toString(), "quoteVault": tokenAccount?.poolKeys.quoteVault.toString(), "baseVault": tokenAccount?.poolKeys.baseVault.toString() }
{ "buy-id(pairAddress)": accountId.toString(), "marketId": tokenAccount?.poolKeys.marketId.toString(), "quoteVault": tokenAccount?.poolKeys.quoteVault.toString() }
];
console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>正在执行购买操作>>>>>>】 代币地址: " + pendingSellToken.tokenInfo.tokenAddress.toString() + " Owner拥有者:" + pendingSellTokenMap.get(tokenAddress).tokenInfo.owner.toString()));
console.table(displayData);
const latestBlockhash = await solanaConnection.getLatestBlockhash({ commitment: COMMITMENT_LEVEL, });
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>买入时开始获取latestBlockhash>>>>>>】 代币地址: " + pendingSellToken.tokenInfo.tokenAddress.toString() + " latestBlockhash: " + latestBlockhash.blockhash));
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>买入时开始获取latestBlockhash>>>>>>】 wallet.publicKey: " + wallet.publicKey + " tokenAccount.address: " + tokenAccount.address + " accountData.baseMint: " + accountData.baseMint));
const messageV0 = new TransactionMessage({
payerKey: wallet.publicKey,
recentBlockhash: latestBlockhash.blockhash,
instructions: [
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 300000 }),//一定程度上影响交易的处理顺序和速度
ComputeBudgetProgram.setComputeUnitLimit({ units: 400000 }),//一定程度上影响交易的处理顺序和速度
createAssociatedTokenAccountIdempotentInstruction(wallet.publicKey, tokenAccount.address, wallet.publicKey, accountData.baseMint),
...innerTransaction.instructions,
],
}).compileToV0Message();
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>买入时开始获取messageV0>>>>>> 代币地址: " + pendingSellToken.tokenInfo.tokenAddress.toString() + " messageV0:" + messageV0));
const transaction = new VersionedTransaction(messageV0);
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>买入时开始获取transaction>>>>>> 代币地址: " + pendingSellToken.tokenInfo.tokenAddress.toString() + " transaction: " + transaction));
transaction.sign([wallet, ...innerTransaction.signers]);
//增加额外的签名 (signatures),要增加交易的费用,就需要在此交易中增加更多的签名。可以通过调用addSignature() 方法,添加一个新的签名。
const signature = await solanaConnection.sendRawTransaction(
transaction.serialize(),
{
maxRetries: 10,
preflightCommitment: COMMITMENT_LEVEL,
}
);
//jito start
// const _tipAcctoun: string = (await searcherClients.getTipAccounts())[0];
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>jito小费情况>>>>>>】: 代币地址: " + pendingSellToken.tokenInfo.tokenAddress.toString() + " _tipAcctoun: " + _tipAcctoun));
// const tipAcctou = new PublicKey(_tipAcctoun);
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>jito小费情况>>>>>>】: 代币地址: " + pendingSellToken.tokenInfo.tokenAddress.toString() + " tipAcctou: " + tipAcctou));
// let blockhash = await solanaConnection.getLatestBlockhash();
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>jito小费情况>>>>>>】: 代币地址: " + pendingSellToken.tokenInfo.tokenAddress.toString() + " blockhash: " + blockhash));
// const b: JitoBundle = new JitoBundle([], 5);
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>jito小费情况>>>>>>】: 代币地址: " + pendingSellToken.tokenInfo.tokenAddress.toString() + " b: " + b));
// b.addTransactions(transaction);
// b.addTipTx(wallet, 10000, tipAcctou, blockhash.blockhash);
// const resp = searcherClients.sendBundle(b);
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>jito小费情况>>>>>>】: 代币地址: " + pendingSellToken.tokenInfo.tokenAddress.toString() + " jitoHash: " + resp));
//jito end
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>买入时开始签名>>>>>>】: 代币地址: " + pendingSellToken.tokenInfo.tokenAddress.toString() + " " + signature + " latestBlockhash:" + latestBlockhash.blockhash));
// logger.info(
// {
// mint: pendingSellToken.tokenInfo.tokenAddress,
// url: `https://solscan.io/tx/${signature}?cluster=${NETWORK}`,
// },
// `Buy`,
// );
const confirmation = await solanaConnection.confirmTransaction(
{
signature,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
blockhash: latestBlockhash.blockhash,
},
COMMITMENT_LEVEL
);
if (!confirmation.value.err) {
// logger.info(
// {
// mint: pendingSellToken.tokenInfo.tokenAddress,
// signature,
// url: `https://solscan.io/tx/${signature}?cluster=${NETWORK}`,
// },
// `Confirmed buy tx`
// );
} else {
logger.debug(confirmation.value.err);
logger.info(
{ mint: pendingSellToken.tokenInfo.tokenAddress, signature },
`Error confirming buy tx`
);
}
//获取买入token的数量 start
// const tokenAccounts = await getTokenAccounts(solanaConnection, wallet.publicKey, COMMITMENT_LEVEL);
// for (const ta of tokenAccounts) {
// existingTokenAccounts.set(ta.accountInfo.mint.toString(), <MinimalTokenAccountData>{
// mint: ta.accountInfo.mint,
// address: ta.pubkey,
// });
// }
// const tokenAccountObj = tokenAccounts.find((acc) => acc.accountInfo.mint.toString() === accountId.toString())!;
// if (tokenAccountObj) {
// console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>获取买入token的数量>>>>>>】: 代币地址: " + accountData.baseMint.toString() + " minAmountOut:" + tokenAccountObj.accountInfo.amount));
// pendingSellTokenMap.get(tokenAddress).tokenInfo.minAmountOut = tokenAccountObj.accountInfo.amount;
// }
//获取买入token的数量 end
worker.postMessage({ "action": "updatePendingSellTokenInfo", "data": pendingSellTokenMap.get(tokenAddress) });
} catch (e) {
logger.error(e);
logger.error({ '代币地址(mint Account)': pendingSellToken.tokenInfo.tokenAddress }, `Failed to buy token`);
}
}
async function sellToken2(accountId: PublicKey, mint: PublicKey, amount: BigNumberish): Promise<void> {
let sold = false;
let retries = 0;
do {
try {
const tokenAccount = existingTokenAccounts.get(mint.toString());
if (!tokenAccount) {
return;
}
// if (!tokenAccount.poolKeys) {
// logger.warn({ mint }, "No pool keys found");
// return;
// }
if (amount === 0) {
//获取买入token的数量 start
const tokenAccounts = await getTokenAccounts(solanaConnection, wallet.publicKey, COMMITMENT_LEVEL);
for (const ta of tokenAccounts) {
existingTokenAccounts.set(ta.accountInfo.mint.toString(), <MinimalTokenAccountData>{
mint: ta.accountInfo.mint,
address: ta.pubkey,
});
}
const tokenAccountObj = tokenAccounts.find((acc) => acc.accountInfo.mint.toString() === mint.toString())!;
if (tokenAccountObj) {
amount = tokenAccountObj.accountInfo.amount;
} else {
logger.info({ mint: tokenAccount.mint, }, `******(主线程操作)代币余额不够,卖出交易失败!`);
return;
}
//获取买入token的数量 end
}
console.log(chalk.red.bold.underline("******(主线程操作)【>>>>>>全部卖出token的数量>>>>>>】: 代币地址: " + mint.toString() + " amount:" + amount));
const { innerTransaction } = Liquidity.makeSwapFixedInInstruction(
{
poolKeys: tokenAccount.poolKeys!,
userKeys: {
tokenAccountOut: quoteTokenAssociatedAddress,
tokenAccountIn: tokenAccount.address,
owner: wallet.publicKey,
},
amountIn: amount,
minAmountOut: 0,
},
tokenAccount.poolKeys!.version
);
const latestBlockhash = await solanaConnection.getLatestBlockhash({ commitment: COMMITMENT_LEVEL, });
const messageV0 = new TransactionMessage({
payerKey: wallet.publicKey,
recentBlockhash: latestBlockhash.blockhash,
instructions: [
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 421197 }),
ComputeBudgetProgram.setComputeUnitLimit({ units: 101337 }),
...innerTransaction.instructions,
createCloseAccountInstruction(
tokenAccount.address,
wallet.publicKey,
wallet.publicKey
),
],
}).compileToV0Message();
const transaction = new VersionedTransaction(messageV0);
transaction.sign([wallet, ...innerTransaction.signers]);
const signature = await solanaConnection.sendRawTransaction(
transaction.serialize(),
{
preflightCommitment: COMMITMENT_LEVEL,
}
);
// logger.info({ mint, signature }, `Sent sell tx`);
const confirmation = await solanaConnection.confirmTransaction(
{
signature,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
blockhash: latestBlockhash.blockhash,
},
COMMITMENT_LEVEL
);
if (confirmation.value.err) {
logger.debug(confirmation.value.err);
logger.info({ mint, signature }, `Error confirming sell tx`);
continue;
}
// logger.info(
// {
// dex: `https://dexscreener.com/solana/${mint}?maker=${wallet.publicKey}`,
// mint,
// signature,
// url: `https://solscan.io/tx/${signature}?cluster=${NETWORK}`,
// },
// `Confirmed sell tx`
// );
sold = true;
} catch (e: any) {
// wait for a bit before retrying
await new Promise((resolve) => setTimeout(resolve, 100));
retries++;
logger.debug(e);
logger.error(
{ mint },
`Failed to sell token, retry: ${retries}/${MAX_SELL_RETRIES}`
);
}
} while (!sold && retries < MAX_SELL_RETRIES);
}
function loadSnipeList() {
if (!USE_SNIPE_LIST) {
return;
}
const count = snipeList.length;
const data = fs.readFileSync(path.join(__dirname, "snipe-list.txt"), "utf-8");
snipeList = data.split("\n").map((a) => a.trim()).filter((a) => a);
if (snipeList.length != count) {
logger.info(`Loaded snipe list: ${snipeList.length}`);
}
}
function shouldBuy(key: string): boolean {
return USE_SNIPE_LIST ? snipeList.includes(key) : true;
}
//格式化金额(保留2位小数)
function formatAmount(arg1: string | number) {
const num = new BigNumber(arg1);
return num.toFixed(2);
}
//格式化金额(保留4位小数)
function formatAmount4(arg1: string | number) {
const num = new BigNumber(arg1);
return num.toFixed(4);
}
function formatBalance(arg1: number, arg2: number) {
if (!arg1 || !arg2) {
return 0;
}
const num = new BigNumber(arg1).div(new BigNumber(10 ** arg2));
return num.toFixed(arg2);
}
//初始化文本中的代卖出代币信息
const initPendingSellTokens = async () => {
//同步的方式读取文件
const data = fs.readFileSync('pendingSellToken.json', { encoding: 'utf8', flag: 'r' });
//还原
pendingSellTokenMap = new Map(JSON.parse(data));
if (pendingSellTokenMap.size > 0) {
//格式化里面的Date数据
for (var [key, value] of pendingSellTokenMap) {
value.tokenInfo.startTime = new Date();
value.tokenInfo.updatePriceTime = new Date();
value.tokenInfo.maxPriceTime = new Date();
value.tokenInfo.buyTime = new Date(value.tokenInfo.buyTime);
value.tokenInfo.sellPrincipalTime = new Date(value.tokenInfo.sellPrincipalTime);
}
//发给子线程保存
worker.postMessage({ "action": "initPendingSellTokens", "data": pendingSellTokenMap });
}
// logger.info("初始化待卖出代币信息成功");
console.log("初始化待卖出代币信息成功");
}
//保存待卖出代币信息到本地文本。
function savePendingSellTokens() {
try {
fs.writeFileSync('pendingSellToken.json', JSON.stringify([...pendingSellTokenMap]));
// logger.info('保存待卖出代币信息到本地已完成');
console.log('保存待卖出代币信息到本地已完成');
} catch (error) {
// logger.info('保存待卖出代币信息到本地失败');
console.log('保存待卖出代币信息到本地失败');
}
};
function sleep(ms: any) {
return new Promise(resolve => setTimeout(resolve, ms));
};
const runListener = async () => {
await initPendingSellTokens();
await init();
const runTimestamp = Math.floor(new Date().getTime() / 1000);
// //监听器1:raydium 的program_id(监控raydium的合约地址,一旦改地址产生变化则流动性发生变化,出现新的流动性)
const raydiumSubscriptionId = solanaConnection.onProgramAccountChange(
RAYDIUM_LIQUIDITY_PROGRAM_ID_V4,
async (updatedAccountInfo) => {
// console.log("代币地址>>> " + updatedAccountInfo.accountId + " executable: " + updatedAccountInfo.accountInfo.executable + "lamports: " + updatedAccountInfo.accountInfo.lamports + "owner: " + updatedAccountInfo.accountInfo.owner);
const pair = updatedAccountInfo.accountId.toString();//流动性的交易对地址
const poolState = LIQUIDITY_STATE_LAYOUT_V4.decode(
updatedAccountInfo.accountInfo.data
);
const poolOpenTime = parseInt(poolState.poolOpenTime.toString());
const existing = existingLiquidityPools.has(pair);
if (poolOpenTime > runTimestamp && !existing) {
existingLiquidityPools.add(pair);
const lay = LIQUIDITY_STATE_LAYOUT_V4.decode(
updatedAccountInfo.accountInfo.data
);
const qvault = await solanaConnection.getBalance(lay.quoteVault);//token account(wsol加池用户,即获取池子wsol大小的balance用户)
//流动池金额大于10SQL才会被狙击
if ((qvault >= 10000000000 && qvault < 1000000000000) && !existing) {
try {
const currentTokenBalance = await solanaConnection.getTokenAccountBalance(lay.baseVault);//token account(代币拥有者的账户,即代币balance的用户)
const currentLamportsBalance = await solanaConnection.getBalance(lay.quoteVault);
console.log("Current balance of 'payer' (池子 SOL 数量):", currentLamportsBalance / LAMPORTS_PER_SOL);
console.log("Current balance of 'payer' (池子 TOKEN 数量):", currentTokenBalance.value.uiAmount);
console.log("Current balance of 'payer' (池子 SOL USDC预估价值$):", (currentLamportsBalance / LAMPORTS_PER_SOL) * 190 * 7.30);
const currentTokenPrice = ((currentLamportsBalance / LAMPORTS_PER_SOL) * 190 * 7.30) / Number(currentTokenBalance.value.uiAmount);
// console.log("Current balance of 'payer' (池子 TOKEN 预估价格$):", currentTokenPrice);
const solAmount = currentLamportsBalance / LAMPORTS_PER_SOL;
processRaydiumPool(updatedAccountInfo.accountId, poolState, pair, solAmount, currentTokenPrice, updatedAccountInfo);
} catch (error) {
}
}
}
},
COMMITMENT_LEVEL,
[
{ dataSize: LIQUIDITY_STATE_LAYOUT_V4.span },
{
memcmp: {
offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf("quoteMint"),
bytes: quoteToken.mint.toBase58(),
},
},
{
memcmp: {
offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf("marketProgramId"),
bytes: OPENBOOK_PROGRAM_ID.toBase58(),
},
},
{
memcmp: {
offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf("status"),
bytes: bs58.encode([6, 0, 0, 0, 0, 0, 0, 0]),
},
},
]
);
////监听器2:openbook 的program_id (因为在raydium添加流动性之前,必须创建openbookid之后,它需要添加流动性)
const openBookSubscriptionId = solanaConnection.onProgramAccountChange(
OPENBOOK_PROGRAM_ID,
async (updatedAccountInfo) => {
// console.log("代币地址(mint Account)>>> key: " + updatedAccountInfo.accountId + " executable: " + updatedAccountInfo.accountInfo.executable + " lamports: " + updatedAccountInfo.accountInfo.lamports + " owner: " + updatedAccountInfo.accountInfo.owner);
const key = updatedAccountInfo.accountId.toString();
const existing = existingOpenBookMarkets.has(key);
if (!existing) {
existingOpenBookMarkets.add(key);
processOpenBookMarket(updatedAccountInfo);
}
},
COMMITMENT_LEVEL,
[
{ dataSize: MARKET_STATE_LAYOUT_V3.span },
{
memcmp: {
offset: MARKET_STATE_LAYOUT_V3.offsetOf("quoteMint"),
bytes: quoteToken.mint.toBase58(),
},
},
]
);
////监听器3:代币交易监听
const walletSubscriptionId = solanaConnection.onProgramAccountChange(
TOKEN_PROGRAM_ID,
async (updatedAccountInfo) => {
console.log("##########################3代币地址(mint Account)>>> " + updatedAccountInfo.accountId + " executable: " + updatedAccountInfo.accountInfo.executable + "lamports: " + updatedAccountInfo.accountInfo.lamports + "owner: " + updatedAccountInfo.accountInfo.owner);
const accountData = AccountLayout.decode(
updatedAccountInfo.accountInfo!.data
);
if (updatedAccountInfo.accountId.equals(quoteTokenAssociatedAddress)) {
return;
}
},
COMMITMENT_LEVEL,
[
{
dataSize: 165,
},
{
memcmp: {
offset: 32,
// bytes: wallet.publicKey.toBase58(),
bytes: 'Mj5YZ1ARNkTJNLBBmtht4X6DDnkhCb5qq5M4kBaejTr',
},
},
]
);
logger.info(`Listening for wallet changes: ${walletSubscriptionId}`);
// logger.info(`Listening for raydium changes: ${raydiumSubscriptionId}`);
// logger.info(`Listening for open book changes: ${openBookSubscriptionId}`);
if (USE_SNIPE_LIST) {
setInterval(loadSnipeList, SNIPE_LIST_REFRESH_INTERVAL);
}
};
runListener();
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/dengdingdong/solana-sniper-bot.git
git@gitee.com:dengdingdong/solana-sniper-bot.git
dengdingdong
solana-sniper-bot
solana-sniper-bot
master

搜索帮助