mirror of
https://github.com/fdundjer/solana-sniper-bot.git
synced 2025-11-23 13:11:09 +10:00
Merge branch 'master' into jour-master
# Conflicts: # buy.ts
This commit is contained in:
@ -27,7 +27,7 @@ To run the script you need to:
|
|||||||
- Run the script by typing: `npm run buy` in terminal
|
- Run the script by typing: `npm run buy` in terminal
|
||||||
|
|
||||||
You should see the following output:
|
You should see the following output:
|
||||||

|

|
||||||
|
|
||||||
## Snipe list
|
## Snipe list
|
||||||
By default, script buys each token which has a new liquidity pool created and open for trading.
|
By default, script buys each token which has a new liquidity pool created and open for trading.
|
||||||
@ -47,10 +47,10 @@ It will buy only when new pool is open for trading. If you want to buy token tha
|
|||||||
By default, auto sell is enabled. If you want to disable it, you need to:
|
By default, auto sell is enabled. If you want to disable it, you need to:
|
||||||
- Change variable `AUTO_SELL` to `false`
|
- Change variable `AUTO_SELL` to `false`
|
||||||
- Update `MAX_SELL_RETRIES` to set the maximum number of retries for selling token
|
- Update `MAX_SELL_RETRIES` to set the maximum number of retries for selling token
|
||||||
- Update `SELL_DELAY` to the number of milliseconds you want to wait before selling the token
|
- Update `AUTO_SELL_DELAY` to the number of milliseconds you want to wait before selling the token
|
||||||
- This will sell the token after the specified delay. (+- RPC node speed)
|
- This will sell the token after the specified delay. (+- RPC node speed)
|
||||||
|
|
||||||
If you set SELL_DELAY to 0, token will be sold immediately after it is bought.
|
If you set AUTO_SELL_DELAY to 0, token will be sold immediately after it is bought.
|
||||||
|
|
||||||
There is no guarantee that the token will be sold at a profit or even sold at all. The developer is not responsible for any losses incurred by using this feature.
|
There is no guarantee that the token will be sold at a profit or even sold at all. The developer is not responsible for any losses incurred by using this feature.
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ To collect more information on an issue, please change `LOG_LEVEL` to `debug`.
|
|||||||
it means that wallet you provided doesn't have USDC/WSOL token account.
|
it means that wallet you provided doesn't have USDC/WSOL token account.
|
||||||
- FIX: Go to dex and swap some SOL to USDC/WSOL. For example when you swap sol to wsol you should see it in wallet as shown below:
|
- FIX: Go to dex and swap some SOL to USDC/WSOL. For example when you swap sol to wsol you should see it in wallet as shown below:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Contact
|
## Contact
|
||||||
[](https://discord.gg/xYUETCA2aP)
|
[](https://discord.gg/xYUETCA2aP)
|
||||||
|
|||||||
90
buy.ts
90
buy.ts
@ -24,67 +24,52 @@ import {
|
|||||||
KeyedAccountInfo,
|
KeyedAccountInfo,
|
||||||
TransactionMessage,
|
TransactionMessage,
|
||||||
VersionedTransaction,
|
VersionedTransaction,
|
||||||
Commitment,
|
|
||||||
} from '@solana/web3.js';
|
} from '@solana/web3.js';
|
||||||
import { getTokenAccounts, RAYDIUM_LIQUIDITY_PROGRAM_ID_V4, OPENBOOK_PROGRAM_ID, createPoolKeys } from './liquidity';
|
import { getTokenAccounts, RAYDIUM_LIQUIDITY_PROGRAM_ID_V4, OPENBOOK_PROGRAM_ID, createPoolKeys } from './liquidity';
|
||||||
import { retrieveEnvVariable } from './utils';
|
import { logger } from './utils';
|
||||||
import { getMinimalMarketV3, MinimalMarketLayoutV3 } from './market';
|
import { getMinimalMarketV3, MinimalMarketLayoutV3 } from './market';
|
||||||
import { MintLayout } from './types';
|
import { MintLayout } from './types';
|
||||||
import pino from 'pino';
|
|
||||||
import bs58 from 'bs58';
|
import bs58 from 'bs58';
|
||||||
|
import BN from 'bn.js';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import BN from 'bn.js';
|
import {
|
||||||
|
AUTO_SELL,
|
||||||
const transport = pino.transport({
|
AUTO_SELL_DELAY,
|
||||||
target: 'pino-pretty',
|
CHECK_IF_MINT_IS_RENOUNCED,
|
||||||
});
|
COMMITMENT_LEVEL,
|
||||||
|
LOG_LEVEL,
|
||||||
export const logger = pino(
|
MAX_SELL_RETRIES,
|
||||||
{
|
NETWORK,
|
||||||
level: 'info',
|
PRIVATE_KEY,
|
||||||
redact: ['poolKeys'],
|
QUOTE_AMOUNT,
|
||||||
serializers: {
|
QUOTE_MINT,
|
||||||
error: pino.stdSerializers.err,
|
RPC_ENDPOINT,
|
||||||
},
|
RPC_WEBSOCKET_ENDPOINT,
|
||||||
base: undefined,
|
SNIPE_LIST_REFRESH_INTERVAL,
|
||||||
},
|
USE_SNIPE_LIST,
|
||||||
transport,
|
MIN_POOL_SIZE,
|
||||||
);
|
} from './constants';
|
||||||
|
|
||||||
const network = 'mainnet-beta';
|
|
||||||
const RPC_ENDPOINT = retrieveEnvVariable('RPC_ENDPOINT', logger);
|
|
||||||
const RPC_WEBSOCKET_ENDPOINT = retrieveEnvVariable('RPC_WEBSOCKET_ENDPOINT', logger);
|
|
||||||
const LOG_LEVEL = retrieveEnvVariable('LOG_LEVEL', logger);
|
|
||||||
|
|
||||||
const solanaConnection = new Connection(RPC_ENDPOINT, {
|
const solanaConnection = new Connection(RPC_ENDPOINT, {
|
||||||
wsEndpoint: RPC_WEBSOCKET_ENDPOINT,
|
wsEndpoint: RPC_WEBSOCKET_ENDPOINT,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type MinimalTokenAccountData = {
|
export interface MinimalTokenAccountData {
|
||||||
mint: PublicKey;
|
mint: PublicKey;
|
||||||
address: PublicKey;
|
address: PublicKey;
|
||||||
poolKeys?: LiquidityPoolKeys;
|
poolKeys?: LiquidityPoolKeys;
|
||||||
market?: MinimalMarketLayoutV3;
|
market?: MinimalMarketLayoutV3;
|
||||||
};
|
};
|
||||||
|
|
||||||
let existingLiquidityPools: Set<string> = new Set<string>();
|
const existingLiquidityPools: Set<string> = new Set<string>();
|
||||||
let existingOpenBookMarkets: Set<string> = new Set<string>();
|
const existingOpenBookMarkets: Set<string> = new Set<string>();
|
||||||
let existingTokenAccounts: Map<string, MinimalTokenAccountData> = new Map<string, MinimalTokenAccountData>();
|
const existingTokenAccounts: Map<string, MinimalTokenAccountData> = new Map<string, MinimalTokenAccountData>();
|
||||||
|
|
||||||
let wallet: Keypair;
|
let wallet: Keypair;
|
||||||
let quoteToken: Token;
|
let quoteToken: Token;
|
||||||
let quoteTokenAssociatedAddress: PublicKey;
|
let quoteTokenAssociatedAddress: PublicKey;
|
||||||
let quoteAmount: TokenAmount;
|
let quoteAmount: TokenAmount;
|
||||||
let commitment: Commitment = retrieveEnvVariable('COMMITMENT_LEVEL', logger) as Commitment;
|
|
||||||
|
|
||||||
const CHECK_IF_MINT_IS_RENOUNCED = retrieveEnvVariable('CHECK_IF_MINT_IS_RENOUNCED', logger) === 'true';
|
|
||||||
const USE_SNIPE_LIST = retrieveEnvVariable('USE_SNIPE_LIST', logger) === 'true';
|
|
||||||
const SNIPE_LIST_REFRESH_INTERVAL = Number(retrieveEnvVariable('SNIPE_LIST_REFRESH_INTERVAL', logger));
|
|
||||||
const AUTO_SELL = retrieveEnvVariable('AUTO_SELL', logger) === 'true';
|
|
||||||
const MAX_SELL_RETRIES = Number(retrieveEnvVariable('MAX_SELL_RETRIES', logger));
|
|
||||||
const AUTO_SELL_DELAY = Number(retrieveEnvVariable('AUTO_SELL_DELAY', logger));
|
|
||||||
const MIN_POOL_SIZE = new BN(retrieveEnvVariable('MIN_POOL_SIZE', logger));
|
|
||||||
|
|
||||||
let snipeList: string[] = [];
|
let snipeList: string[] = [];
|
||||||
|
|
||||||
@ -92,13 +77,10 @@ async function init(): Promise<void> {
|
|||||||
logger.level = LOG_LEVEL;
|
logger.level = LOG_LEVEL;
|
||||||
|
|
||||||
// get wallet
|
// get wallet
|
||||||
const PRIVATE_KEY = retrieveEnvVariable('PRIVATE_KEY', logger);
|
|
||||||
wallet = Keypair.fromSecretKey(bs58.decode(PRIVATE_KEY));
|
wallet = Keypair.fromSecretKey(bs58.decode(PRIVATE_KEY));
|
||||||
logger.info(`Wallet Address: ${wallet.publicKey}`);
|
logger.info(`Wallet Address: ${wallet.publicKey}`);
|
||||||
|
|
||||||
// get quote mint and amount
|
// get quote mint and amount
|
||||||
const QUOTE_MINT = retrieveEnvVariable('QUOTE_MINT', logger);
|
|
||||||
const QUOTE_AMOUNT = retrieveEnvVariable('QUOTE_AMOUNT', logger);
|
|
||||||
switch (QUOTE_MINT) {
|
switch (QUOTE_MINT) {
|
||||||
case 'WSOL': {
|
case 'WSOL': {
|
||||||
quoteToken = Token.WSOL;
|
quoteToken = Token.WSOL;
|
||||||
@ -126,7 +108,7 @@ async function init(): Promise<void> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// check existing wallet for associated token account of quote mint
|
// check existing wallet for associated token account of quote mint
|
||||||
const tokenAccounts = await getTokenAccounts(solanaConnection, wallet.publicKey, commitment);
|
const tokenAccounts = await getTokenAccounts(solanaConnection, wallet.publicKey, COMMITMENT_LEVEL);
|
||||||
|
|
||||||
for (const ta of tokenAccounts) {
|
for (const ta of tokenAccounts) {
|
||||||
existingTokenAccounts.set(ta.accountInfo.mint.toString(), <MinimalTokenAccountData>{
|
existingTokenAccounts.set(ta.accountInfo.mint.toString(), <MinimalTokenAccountData>{
|
||||||
@ -237,7 +219,7 @@ async function buy(accountId: PublicKey, accountData: LiquidityStateV4): Promise
|
|||||||
|
|
||||||
if (!tokenAccount) {
|
if (!tokenAccount) {
|
||||||
// it's possible that we didn't have time to fetch open book data
|
// it's possible that we didn't have time to fetch open book data
|
||||||
const market = await getMinimalMarketV3(solanaConnection, accountData.marketId, commitment);
|
const market = await getMinimalMarketV3(solanaConnection, accountData.marketId, COMMITMENT_LEVEL);
|
||||||
tokenAccount = saveTokenAccount(accountData.baseMint, market);
|
tokenAccount = saveTokenAccount(accountData.baseMint, market);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +239,7 @@ async function buy(accountId: PublicKey, accountData: LiquidityStateV4): Promise
|
|||||||
);
|
);
|
||||||
|
|
||||||
const latestBlockhash = await solanaConnection.getLatestBlockhash({
|
const latestBlockhash = await solanaConnection.getLatestBlockhash({
|
||||||
commitment: commitment,
|
commitment: COMMITMENT_LEVEL,
|
||||||
});
|
});
|
||||||
const messageV0 = new TransactionMessage({
|
const messageV0 = new TransactionMessage({
|
||||||
payerKey: wallet.publicKey,
|
payerKey: wallet.publicKey,
|
||||||
@ -277,7 +259,7 @@ async function buy(accountId: PublicKey, accountData: LiquidityStateV4): Promise
|
|||||||
const transaction = new VersionedTransaction(messageV0);
|
const transaction = new VersionedTransaction(messageV0);
|
||||||
transaction.sign([wallet, ...innerTransaction.signers]);
|
transaction.sign([wallet, ...innerTransaction.signers]);
|
||||||
const signature = await solanaConnection.sendRawTransaction(transaction.serialize(), {
|
const signature = await solanaConnection.sendRawTransaction(transaction.serialize(), {
|
||||||
preflightCommitment: commitment,
|
preflightCommitment: COMMITMENT_LEVEL,
|
||||||
});
|
});
|
||||||
logger.info({ mint: accountData.baseMint, signature }, `Sent buy tx`);
|
logger.info({ mint: accountData.baseMint, signature }, `Sent buy tx`);
|
||||||
const confirmation = await solanaConnection.confirmTransaction(
|
const confirmation = await solanaConnection.confirmTransaction(
|
||||||
@ -286,14 +268,14 @@ async function buy(accountId: PublicKey, accountData: LiquidityStateV4): Promise
|
|||||||
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
||||||
blockhash: latestBlockhash.blockhash,
|
blockhash: latestBlockhash.blockhash,
|
||||||
},
|
},
|
||||||
commitment,
|
COMMITMENT_LEVEL,
|
||||||
);
|
);
|
||||||
if (!confirmation.value.err) {
|
if (!confirmation.value.err) {
|
||||||
logger.info(
|
logger.info(
|
||||||
{
|
{
|
||||||
mint: accountData.baseMint,
|
mint: accountData.baseMint,
|
||||||
signature,
|
signature,
|
||||||
url: `https://solscan.io/tx/${signature}?cluster=${network}`,
|
url: `https://solscan.io/tx/${signature}?cluster=${NETWORK}`,
|
||||||
},
|
},
|
||||||
`Confirmed buy tx`,
|
`Confirmed buy tx`,
|
||||||
);
|
);
|
||||||
@ -353,7 +335,7 @@ async function sell(accountId: PublicKey, mint: PublicKey, amount: BigNumberish)
|
|||||||
);
|
);
|
||||||
|
|
||||||
const latestBlockhash = await solanaConnection.getLatestBlockhash({
|
const latestBlockhash = await solanaConnection.getLatestBlockhash({
|
||||||
commitment: commitment,
|
commitment: COMMITMENT_LEVEL,
|
||||||
});
|
});
|
||||||
const messageV0 = new TransactionMessage({
|
const messageV0 = new TransactionMessage({
|
||||||
payerKey: wallet.publicKey,
|
payerKey: wallet.publicKey,
|
||||||
@ -368,7 +350,7 @@ async function sell(accountId: PublicKey, mint: PublicKey, amount: BigNumberish)
|
|||||||
const transaction = new VersionedTransaction(messageV0);
|
const transaction = new VersionedTransaction(messageV0);
|
||||||
transaction.sign([wallet, ...innerTransaction.signers]);
|
transaction.sign([wallet, ...innerTransaction.signers]);
|
||||||
const signature = await solanaConnection.sendRawTransaction(transaction.serialize(), {
|
const signature = await solanaConnection.sendRawTransaction(transaction.serialize(), {
|
||||||
preflightCommitment: commitment,
|
preflightCommitment: COMMITMENT_LEVEL,
|
||||||
});
|
});
|
||||||
logger.info({ mint, signature }, `Sent sell tx`);
|
logger.info({ mint, signature }, `Sent sell tx`);
|
||||||
const confirmation = await solanaConnection.confirmTransaction(
|
const confirmation = await solanaConnection.confirmTransaction(
|
||||||
@ -377,7 +359,7 @@ async function sell(accountId: PublicKey, mint: PublicKey, amount: BigNumberish)
|
|||||||
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
||||||
blockhash: latestBlockhash.blockhash,
|
blockhash: latestBlockhash.blockhash,
|
||||||
},
|
},
|
||||||
commitment,
|
COMMITMENT_LEVEL,
|
||||||
);
|
);
|
||||||
if (confirmation.value.err) {
|
if (confirmation.value.err) {
|
||||||
logger.debug(confirmation.value.err);
|
logger.debug(confirmation.value.err);
|
||||||
@ -390,7 +372,7 @@ async function sell(accountId: PublicKey, mint: PublicKey, amount: BigNumberish)
|
|||||||
dex: `https://dexscreener.com/solana/${mint}?maker=${wallet.publicKey}`,
|
dex: `https://dexscreener.com/solana/${mint}?maker=${wallet.publicKey}`,
|
||||||
mint,
|
mint,
|
||||||
signature,
|
signature,
|
||||||
url: `https://solscan.io/tx/${signature}?cluster=${network}`,
|
url: `https://solscan.io/tx/${signature}?cluster=${NETWORK}`,
|
||||||
},
|
},
|
||||||
`Confirmed sell tx`,
|
`Confirmed sell tx`,
|
||||||
);
|
);
|
||||||
@ -442,7 +424,7 @@ const runListener = async () => {
|
|||||||
const _ = processRaydiumPool(updatedAccountInfo.accountId, poolState);
|
const _ = processRaydiumPool(updatedAccountInfo.accountId, poolState);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
commitment,
|
COMMITMENT_LEVEL,
|
||||||
[
|
[
|
||||||
{ dataSize: LIQUIDITY_STATE_LAYOUT_V4.span },
|
{ dataSize: LIQUIDITY_STATE_LAYOUT_V4.span },
|
||||||
{
|
{
|
||||||
@ -476,7 +458,7 @@ const runListener = async () => {
|
|||||||
const _ = processOpenBookMarket(updatedAccountInfo);
|
const _ = processOpenBookMarket(updatedAccountInfo);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
commitment,
|
COMMITMENT_LEVEL,
|
||||||
[
|
[
|
||||||
{ dataSize: MARKET_STATE_LAYOUT_V3.span },
|
{ dataSize: MARKET_STATE_LAYOUT_V3.span },
|
||||||
{
|
{
|
||||||
@ -500,7 +482,7 @@ const runListener = async () => {
|
|||||||
|
|
||||||
const _ = sell(updatedAccountInfo.accountId, accountData.mint, accountData.amount);
|
const _ = sell(updatedAccountInfo.accountId, accountData.mint, accountData.amount);
|
||||||
},
|
},
|
||||||
commitment,
|
COMMITMENT_LEVEL,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
dataSize: 165,
|
dataSize: 165,
|
||||||
|
|||||||
17
constants/constants.ts
Normal file
17
constants/constants.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Commitment } from "@solana/web3.js";
|
||||||
|
import { logger, retrieveEnvVariable } from "../utils";
|
||||||
|
|
||||||
|
export const NETWORK = 'mainnet-beta';
|
||||||
|
export const COMMITMENT_LEVEL: Commitment = retrieveEnvVariable('COMMITMENT_LEVEL', logger) as Commitment;
|
||||||
|
export const RPC_ENDPOINT = retrieveEnvVariable('RPC_ENDPOINT', logger);
|
||||||
|
export const RPC_WEBSOCKET_ENDPOINT = retrieveEnvVariable('RPC_WEBSOCKET_ENDPOINT', logger);
|
||||||
|
export const LOG_LEVEL = retrieveEnvVariable('LOG_LEVEL', logger);
|
||||||
|
export const CHECK_IF_MINT_IS_RENOUNCED = retrieveEnvVariable('CHECK_IF_MINT_IS_RENOUNCED', logger) === 'true';
|
||||||
|
export const USE_SNIPE_LIST = retrieveEnvVariable('USE_SNIPE_LIST', logger) === 'true';
|
||||||
|
export const SNIPE_LIST_REFRESH_INTERVAL = Number(retrieveEnvVariable('SNIPE_LIST_REFRESH_INTERVAL', logger));
|
||||||
|
export const AUTO_SELL = retrieveEnvVariable('AUTO_SELL', logger) === 'true';
|
||||||
|
export const MAX_SELL_RETRIES = Number(retrieveEnvVariable('MAX_SELL_RETRIES', logger));
|
||||||
|
export const AUTO_SELL_DELAY = Number(retrieveEnvVariable('AUTO_SELL_DELAY', logger));
|
||||||
|
export const PRIVATE_KEY = retrieveEnvVariable('PRIVATE_KEY', logger);
|
||||||
|
export const QUOTE_MINT = retrieveEnvVariable('QUOTE_MINT', logger);
|
||||||
|
export const QUOTE_AMOUNT = retrieveEnvVariable('QUOTE_AMOUNT', logger);
|
||||||
1
constants/index.ts
Normal file
1
constants/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './constants';
|
||||||
@ -2,7 +2,8 @@
|
|||||||
"name": "solana-sniper-bot",
|
"name": "solana-sniper-bot",
|
||||||
"author": "Filip Dundjer",
|
"author": "Filip Dundjer",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"buy": "ts-node buy.ts"
|
"buy": "ts-node buy.ts",
|
||||||
|
"tsc": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@raydium-io/raydium-sdk": "^1.3.1-beta.47",
|
"@raydium-io/raydium-sdk": "^1.3.1-beta.47",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
@ -1 +1,2 @@
|
|||||||
export * from './utils';
|
export * from './utils';
|
||||||
|
export * from './logger';
|
||||||
17
utils/logger.ts
Normal file
17
utils/logger.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import pino from "pino";
|
||||||
|
|
||||||
|
const transport = pino.transport({
|
||||||
|
target: 'pino-pretty',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const logger = pino(
|
||||||
|
{
|
||||||
|
level: 'info',
|
||||||
|
redact: ['poolKeys'],
|
||||||
|
serializers: {
|
||||||
|
error: pino.stdSerializers.err,
|
||||||
|
},
|
||||||
|
base: undefined,
|
||||||
|
},
|
||||||
|
transport,
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user