mirror of
https://github.com/fdundjer/solana-sniper-bot.git
synced 2025-11-09 20:12:06 +10:00
feat: complete rewrite
This commit is contained in:
57
helpers/constants.ts
Normal file
57
helpers/constants.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { Logger } from 'pino';
|
||||
import dotenv from 'dotenv';
|
||||
import { Commitment } from '@solana/web3.js';
|
||||
import { logger } from './logger';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const retrieveEnvVariable = (variableName: string, logger: Logger) => {
|
||||
const variable = process.env[variableName] || '';
|
||||
if (!variable) {
|
||||
logger.error(`${variableName} is not set`);
|
||||
process.exit(1);
|
||||
}
|
||||
return variable;
|
||||
};
|
||||
|
||||
// Wallet
|
||||
export const PRIVATE_KEY = retrieveEnvVariable('PRIVATE_KEY', logger);
|
||||
|
||||
// Connection
|
||||
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);
|
||||
|
||||
// Bot
|
||||
export const LOG_LEVEL = retrieveEnvVariable('LOG_LEVEL', logger);
|
||||
export const ONE_TOKEN_AT_A_TIME = retrieveEnvVariable('ONE_TOKEN_AT_A_TIME', logger) === 'true';
|
||||
export const COMPUTE_UNIT_LIMIT = Number(retrieveEnvVariable('COMPUTE_UNIT_LIMIT', logger));
|
||||
export const COMPUTE_UNIT_PRICE = Number(retrieveEnvVariable('COMPUTE_UNIT_PRICE', logger));
|
||||
export const PRE_LOAD_EXISTING_MARKETS = retrieveEnvVariable('PRE_LOAD_EXISTING_MARKETS', logger) === 'true';
|
||||
export const CACHE_NEW_MARKETS = retrieveEnvVariable('CACHE_NEW_MARKETS', logger) === 'true';
|
||||
|
||||
// Buy
|
||||
export const AUTO_BUY_DELAY = Number(retrieveEnvVariable('AUTO_BUY_DELAY', logger));
|
||||
export const QUOTE_MINT = retrieveEnvVariable('QUOTE_MINT', logger);
|
||||
export const QUOTE_AMOUNT = retrieveEnvVariable('QUOTE_AMOUNT', logger);
|
||||
export const MAX_BUY_RETRIES = Number(retrieveEnvVariable('MAX_BUY_RETRIES', logger));
|
||||
export const BUY_SLIPPAGE = Number(retrieveEnvVariable('BUY_SLIPPAGE', logger));
|
||||
|
||||
// Sell
|
||||
export const AUTO_SELL = retrieveEnvVariable('AUTO_SELL', logger) === 'true';
|
||||
export const AUTO_SELL_DELAY = Number(retrieveEnvVariable('AUTO_SELL_DELAY', logger));
|
||||
export const MAX_SELL_RETRIES = Number(retrieveEnvVariable('MAX_SELL_RETRIES', logger));
|
||||
export const TAKE_PROFIT = Number(retrieveEnvVariable('TAKE_PROFIT', logger));
|
||||
export const STOP_LOSS = Number(retrieveEnvVariable('STOP_LOSS', logger));
|
||||
export const PRICE_CHECK_INTERVAL = Number(retrieveEnvVariable('PRICE_CHECK_INTERVAL', logger));
|
||||
export const PRICE_CHECK_DURATION = Number(retrieveEnvVariable('PRICE_CHECK_DURATION', logger));
|
||||
export const SELL_SLIPPAGE = Number(retrieveEnvVariable('SELL_SLIPPAGE', logger));
|
||||
|
||||
// Filters
|
||||
export const CHECK_IF_MINT_IS_RENOUNCED = retrieveEnvVariable('CHECK_IF_MINT_IS_RENOUNCED', logger) === 'true';
|
||||
export const CHECK_IF_BURNED = retrieveEnvVariable('CHECK_IF_BURNED', logger) === 'true';
|
||||
export const MIN_POOL_SIZE = retrieveEnvVariable('MIN_POOL_SIZE', logger);
|
||||
export const MAX_POOL_SIZE = retrieveEnvVariable('MAX_POOL_SIZE', logger);
|
||||
export const USE_SNIPE_LIST = retrieveEnvVariable('USE_SNIPE_LIST', logger) === 'true';
|
||||
export const SNIPE_LIST_REFRESH_INTERVAL = Number(retrieveEnvVariable('SNIPE_LIST_REFRESH_INTERVAL', logger));
|
||||
7
helpers/index.ts
Normal file
7
helpers/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export * from './market';
|
||||
export * from './liquidity';
|
||||
export * from './logger';
|
||||
export * from './constants';
|
||||
export * from './token';
|
||||
export * from './wallet';
|
||||
export * from './promises'
|
||||
43
helpers/liquidity.ts
Normal file
43
helpers/liquidity.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import { Liquidity, LiquidityPoolKeys, LiquidityStateV4, MAINNET_PROGRAM_ID, Market } from '@raydium-io/raydium-sdk';
|
||||
import { MinimalMarketLayoutV3 } from './market';
|
||||
|
||||
export function createPoolKeys(
|
||||
id: PublicKey,
|
||||
accountData: LiquidityStateV4,
|
||||
minimalMarketLayoutV3: MinimalMarketLayoutV3,
|
||||
): LiquidityPoolKeys {
|
||||
return {
|
||||
id,
|
||||
baseMint: accountData.baseMint,
|
||||
quoteMint: accountData.quoteMint,
|
||||
lpMint: accountData.lpMint,
|
||||
baseDecimals: accountData.baseDecimal.toNumber(),
|
||||
quoteDecimals: accountData.quoteDecimal.toNumber(),
|
||||
lpDecimals: 5,
|
||||
version: 4,
|
||||
programId: MAINNET_PROGRAM_ID.AmmV4,
|
||||
authority: Liquidity.getAssociatedAuthority({
|
||||
programId: MAINNET_PROGRAM_ID.AmmV4,
|
||||
}).publicKey,
|
||||
openOrders: accountData.openOrders,
|
||||
targetOrders: accountData.targetOrders,
|
||||
baseVault: accountData.baseVault,
|
||||
quoteVault: accountData.quoteVault,
|
||||
marketVersion: 3,
|
||||
marketProgramId: accountData.marketProgramId,
|
||||
marketId: accountData.marketId,
|
||||
marketAuthority: Market.getAssociatedAuthority({
|
||||
programId: accountData.marketProgramId,
|
||||
marketId: accountData.marketId,
|
||||
}).publicKey,
|
||||
marketBaseVault: accountData.baseVault,
|
||||
marketQuoteVault: accountData.quoteVault,
|
||||
marketBids: minimalMarketLayoutV3.bids,
|
||||
marketAsks: minimalMarketLayoutV3.asks,
|
||||
marketEventQueue: minimalMarketLayoutV3.eventQueue,
|
||||
withdrawQueue: accountData.withdrawQueue,
|
||||
lpVault: accountData.lpVault,
|
||||
lookupTableAccount: PublicKey.default,
|
||||
};
|
||||
}
|
||||
17
helpers/logger.ts
Normal file
17
helpers/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,
|
||||
);
|
||||
22
helpers/market.ts
Normal file
22
helpers/market.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { Commitment, Connection, PublicKey } from '@solana/web3.js';
|
||||
import { GetStructureSchema, MARKET_STATE_LAYOUT_V3, publicKey, struct } from '@raydium-io/raydium-sdk';
|
||||
|
||||
export const MINIMAL_MARKET_STATE_LAYOUT_V3 = struct([publicKey('eventQueue'), publicKey('bids'), publicKey('asks')]);
|
||||
export type MinimalMarketStateLayoutV3 = typeof MINIMAL_MARKET_STATE_LAYOUT_V3;
|
||||
export type MinimalMarketLayoutV3 = GetStructureSchema<MinimalMarketStateLayoutV3>;
|
||||
|
||||
export async function getMinimalMarketV3(
|
||||
connection: Connection,
|
||||
marketId: PublicKey,
|
||||
commitment?: Commitment,
|
||||
): Promise<MinimalMarketLayoutV3> {
|
||||
const marketInfo = await connection.getAccountInfo(marketId, {
|
||||
commitment,
|
||||
dataSlice: {
|
||||
offset: MARKET_STATE_LAYOUT_V3.offsetOf('eventQueue'),
|
||||
length: 32 * 3,
|
||||
},
|
||||
});
|
||||
|
||||
return MINIMAL_MARKET_STATE_LAYOUT_V3.decode(marketInfo!.data);
|
||||
}
|
||||
1
helpers/promises.ts
Normal file
1
helpers/promises.ts
Normal file
@ -0,0 +1 @@
|
||||
export const sleep = (ms = 0) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
23
helpers/token.ts
Normal file
23
helpers/token.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Token } from '@raydium-io/raydium-sdk';
|
||||
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
|
||||
export function getToken(token: string) {
|
||||
switch (token) {
|
||||
case 'WSOL': {
|
||||
return Token.WSOL;
|
||||
}
|
||||
case 'USDC': {
|
||||
return new Token(
|
||||
TOKEN_PROGRAM_ID,
|
||||
new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
|
||||
6,
|
||||
'USDC',
|
||||
'USDC',
|
||||
);
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unsupported quote mint "${token}". Supported values are USDC and WSOL`);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
helpers/wallet.ts
Normal file
21
helpers/wallet.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { Keypair } from '@solana/web3.js';
|
||||
import bs58 from 'bs58';
|
||||
import { mnemonicToSeedSync } from 'bip39';
|
||||
import { derivePath } from 'ed25519-hd-key';
|
||||
|
||||
export function getWallet(wallet: string): Keypair {
|
||||
// most likely someone pasted the private key in binary format
|
||||
if (wallet.startsWith('[')) {
|
||||
return Keypair.fromSecretKey(JSON.parse(wallet));
|
||||
}
|
||||
|
||||
// most likely someone pasted mnemonic
|
||||
if (wallet.split(' ').length > 1) {
|
||||
const seed = mnemonicToSeedSync(wallet, '');
|
||||
const path = `m/44'/501'/0'/0'`; // we assume it's first path
|
||||
return Keypair.fromSeed(derivePath(path, seed.toString('hex')).key);
|
||||
}
|
||||
|
||||
// most likely someone pasted base58 encoded private key
|
||||
return Keypair.fromSecretKey(bs58.decode(wallet));
|
||||
}
|
||||
Reference in New Issue
Block a user