feat: complete rewrite

This commit is contained in:
Filip Dunder
2024-04-15 15:27:22 +02:00
parent b13f9864d2
commit 643002ae8a
36 changed files with 3898 additions and 705 deletions

57
helpers/constants.ts Normal file
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1 @@
export const sleep = (ms = 0) => new Promise((resolve) => setTimeout(resolve, ms));

23
helpers/token.ts Normal file
View 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
View 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));
}