Procházet zdrojové kódy

Added Jito-RPC Executer

engmabdulwahab před 1 rokem
rodič
revize
39ef59f5a6
6 změnil soubory, kde provedl 174 přidání a 18 odebrání
  1. 4 4
      .env.copy
  2. 25 9
      README.md
  3. 4 1
      bot.ts
  4. 1 1
      helpers/constants.ts
  5. 9 3
      index.ts
  6. 131 0
      transactions/jito-rpc-transaction-executor.ts

+ 4 - 4
.env.copy

@@ -11,13 +11,13 @@ LOG_LEVEL=trace
 ONE_TOKEN_AT_A_TIME=true
 PRE_LOAD_EXISTING_MARKETS=false
 CACHE_NEW_MARKETS=false
-# default or warp
+# default or warp or jito
 TRANSACTION_EXECUTOR=default
-# if using default executor fee below will be applied
+# if using default executor, fee below will be applied
 COMPUTE_UNIT_LIMIT=101337
 COMPUTE_UNIT_PRICE=421197
-# if using warp executor fee below will be applied
-WARP_FEE=0.006
+# if using warp or jito executor, fee below will be applied
+CUSTOM_FEE=0.006
 
 # Buy
 QUOTE_MINT=WSOL

+ 25 - 9
README.md

@@ -1,12 +1,14 @@
-
 # Solana Trading Bot (Beta)
-The Solana Trading Bot is a software tool designed to automate the buying and selling of tokens on the Solana blockchain. 
-It is configured to execute trades based on predefined parameters and strategies set by the user. 
+
+The Solana Trading Bot is a software tool designed to automate the buying and selling of tokens on the Solana blockchain.
+It is configured to execute trades based on predefined parameters and strategies set by the user.
 
 The bot can monitor market conditions in real-time, such as pool burn, mint renounced and other factors, and it will execute trades when these conditions are fulfilled.
 
 ## Setup
+
 To run the script you need to:
+
 - Create a new empty Solana wallet
 - Transfer some SOL to it.
 - Convert some SOL to USDC or WSOL.
@@ -22,14 +24,17 @@ You should see the following output:
 ### Configuration
 
 #### Wallet
+
 - `PRIVATE_KEY` - Your wallet's private key.
 
 #### Connection
+
 - `RPC_ENDPOINT` - HTTPS RPC endpoint for interacting with the Solana network.
 - `RPC_WEBSOCKET_ENDPOINT` - WebSocket RPC endpoint for real-time updates from the Solana network.
 - `COMMITMENT_LEVEL`- The commitment level of transactions (e.g., "finalized" for the highest level of security).
 
 #### Bot
+
 - `LOG_LEVEL` - Set logging level, e.g., `info`, `debug`, `trace`, etc.
 - `ONE_TOKEN_AT_A_TIME` - Set to `true` to process buying one token at a time.
 - `COMPUTE_UNIT_LIMIT` - Compute limit used to calculate fees.
@@ -38,13 +43,14 @@ You should see the following output:
   - This option should not be used with public RPC.
 - `CACHE_NEW_MARKETS` - Set to `true` to cache new markets.
   - This option should not be used with public RPC.
-- `TRANSACTION_EXECUTOR` - Set to `warp` to use warp infrastructure for executing transactions
+- `TRANSACTION_EXECUTOR` - Set to `warp` to use warp infrastructure for executing transactions, or set it to jito to use JSON-RPC jito executer
   - For more details checkout [warp](#warp-transactions-beta) section
-- `WARP_FEE` - If using warp executor this value will be used for transaction fees instead of `COMPUTE_UNIT_LIMIT` and `COMPUTE_UNIT_LIMIT`
-  - Minimum value is 0.0001 SOL, but we recommend using 0.006 SOL or above 
+- `CUSTOM_FEE` - If using warp or jito executors this value will be used for transaction fees instead of `COMPUTE_UNIT_LIMIT` and `COMPUTE_UNIT_LIMIT`
+  - Minimum value is 0.0001 SOL, but we recommend using 0.006 SOL or above
   - On top of this fee, minimal solana network fee will be applied
 
 #### Buy
+
 - `QUOTE_MINT` - Which pools to snipe, USDC or WSOL.
 - `QUOTE_AMOUNT` - Amount used to buy each new token.
 - `AUTO_BUY_DELAY` - Delay in milliseconds before buying a token.
@@ -52,10 +58,11 @@ You should see the following output:
 - `BUY_SLIPPAGE` - Slippage %
 
 #### Sell
+
 - `AUTO_SELL` - Set to `true` to enable automatic selling of tokens.
   - If you want to manually sell bought tokens, disable this option.
 - `MAX_SELL_RETRIES` - Maximum number of retries for selling a token.
-- `AUTO_SELL_DELAY` -  Delay in milliseconds before auto-selling a token.
+- `AUTO_SELL_DELAY` - Delay in milliseconds before auto-selling a token.
 - `PRICE_CHECK_INTERVAL` - Interval in milliseconds for checking the take profit and stop loss conditions.
   - Set to zero to disable take profit and stop loss.
 - `PRICE_CHECK_DURATION` - Time in milliseconds to wait for stop loss/take profit conditions.
@@ -68,6 +75,7 @@ You should see the following output:
 - `SELL_SLIPPAGE` - Slippage %.
 
 #### Snipe list
+
 - `USE_SNIPE_LIST` - Set to `true` to enable buying only tokens listed in `snipe-list.txt`.
   - Pool must not exist before the bot starts.
   - If token can be traded before bot starts nothing will happen. Bot will not buy the token.
@@ -77,6 +85,7 @@ You should see the following output:
 Note: When using snipe list filters below will be disabled.
 
 #### Filters
+
 - `FILTER_CHECK_INTERVAL` - Interval in milliseconds for checking if pool match the filters.
   - Set to zero to disable filters.
 - `FILTER_CHECK_DURATION` - Time in milliseconds to wait for pool to match the filters.
@@ -93,12 +102,14 @@ Note: When using snipe list filters below will be disabled.
   - Set `0` to disable.
 
 ## Warp transactions (beta)
+
 In case you experience a lot of failed transactions or transaction performance is too slow, you can try using `warp` for executing transactions.
 Warp is hosted service that executes transactions using integrations with third party providers.
 
 Using warp for transactions supports the team behind this project.
 
 ### Security
+
 When using warp, transaction is sent to the hosted service.
 **Payload that is being sent will NOT contain your wallet private key**. Fee transaction is signed on your machine.
 Each request is processed by hosted service and sent to third party provider.
@@ -107,20 +118,24 @@ Each request is processed by hosted service and sent to third party provider.
 Note: Warp transactions are disabled by default.
 
 ### Fees
+
 When using warp for transactions, fee is distributed between developers of warp and third party providers.
 In case TX fails, no fee will be taken from your account.
 
 ## Common issues
+
 If you have an error which is not listed here, please create a new issue in this repository.
 To collect more information on an issue, please change `LOG_LEVEL` to `debug`.
 
 ### Unsupported RPC node
+
 - If you see following error in your log file:  
   `Error: 410 Gone:  {"jsonrpc":"2.0","error":{"code": 410, "message":"The RPC call or parameters have been disabled."}, "id": "986f3599-b2b7-47c4-b951-074c19842bad" }`  
   it means your RPC node doesn't support methods needed to execute script.
   - FIX: Change your RPC node. You can use Helius or Quicknode.
 
 ### No token account
+
 - If you see following error in your log file:  
   `Error: No SOL token account found in wallet: `  
   it means that wallet you provided doesn't have USDC/WSOL token account.
@@ -129,10 +144,11 @@ To collect more information on an issue, please change `LOG_LEVEL` to `debug`.
 ![wsol](readme/wsol.png)
 
 ## Contact
+
 [![](https://img.shields.io/discord/1201826085655023616?color=5865F2&logo=Discord&style=flat-square)](https://discord.gg/xYUETCA2aP)
 
 - If you want to leave a tip, you can send it to the following address:
-`7gm6BPQrSBaTAYaJheuRevBNXcmKsgbkfBCVSjBnt9aP`
+  `7gm6BPQrSBaTAYaJheuRevBNXcmKsgbkfBCVSjBnt9aP`
 
 - If you need custom features or assistance, feel free to contact the admin team on discord for dedicated support.
 
@@ -140,4 +156,4 @@ To collect more information on an issue, please change `LOG_LEVEL` to `debug`.
 
 The Solana Trading Bot is provided as is, for learning purposes.
 Trading cryptocurrencies and tokens involves risk, and past performance is not indicative of future results.
-The use of this bot is at your own risk, and we are not responsible for any losses incurred while using the bot.
+The use of this bot is at your own risk, and we are not responsible for any losses incurred while using the bot.

+ 4 - 1
bot.ts

@@ -22,6 +22,7 @@ import { createPoolKeys, logger, NETWORK, sleep } from './helpers';
 import { Mutex } from 'async-mutex';
 import BN from 'bn.js';
 import { WarpTransactionExecutor } from './transactions/warp-transaction-executor';
+import { JitoTransactionExecutor } from './transactions/jito-rpc-transaction-executor';
 
 export interface BotConfig {
   wallet: Keypair;
@@ -63,6 +64,7 @@ export class Bot {
   private readonly mutex: Mutex;
   private sellExecutionCount = 0;
   public readonly isWarp: boolean = false;
+  public readonly isJito: boolean = false;
 
   constructor(
     private readonly connection: Connection,
@@ -72,6 +74,7 @@ export class Bot {
     readonly config: BotConfig,
   ) {
     this.isWarp = txExecutor instanceof WarpTransactionExecutor;
+    this.isJito = txExecutor instanceof JitoTransactionExecutor;
 
     this.mutex = new Mutex();
     this.poolFilters = new PoolFilters(connection, {
@@ -324,7 +327,7 @@ export class Bot {
       payerKey: wallet.publicKey,
       recentBlockhash: latestBlockhash.blockhash,
       instructions: [
-        ...(this.isWarp
+        ...(this.isWarp || this.isJito
           ? []
           : [
               ComputeBudgetProgram.setComputeUnitPrice({ microLamports: this.config.unitPrice }),

+ 1 - 1
helpers/constants.ts

@@ -31,7 +31,7 @@ export const COMPUTE_UNIT_PRICE = Number(retrieveEnvVariable('COMPUTE_UNIT_PRICE
 export const PRE_LOAD_EXISTING_MARKETS = retrieveEnvVariable('PRE_LOAD_EXISTING_MARKETS', logger) === 'true';
 export const CACHE_NEW_MARKETS = retrieveEnvVariable('CACHE_NEW_MARKETS', logger) === 'true';
 export const TRANSACTION_EXECUTOR = retrieveEnvVariable('TRANSACTION_EXECUTOR', logger);
-export const WARP_FEE = retrieveEnvVariable('WARP_FEE', logger);
+export const CUSTOM_FEE = retrieveEnvVariable('CUSTOM_FEE', logger);
 
 // Buy
 export const AUTO_BUY_DELAY = Number(retrieveEnvVariable('AUTO_BUY_DELAY', logger));

+ 9 - 3
index.ts

@@ -40,13 +40,14 @@ import {
   PRICE_CHECK_INTERVAL,
   SNIPE_LIST_REFRESH_INTERVAL,
   TRANSACTION_EXECUTOR,
-  WARP_FEE,
+  CUSTOM_FEE,
   FILTER_CHECK_INTERVAL,
   FILTER_CHECK_DURATION,
   CONSECUTIVE_FILTER_MATCHES,
 } from './helpers';
 import { version } from './package.json';
 import { WarpTransactionExecutor } from './transactions/warp-transaction-executor';
+import { JitoTransactionExecutor } from './transactions/jito-rpc-transaction-executor';
 
 const connection = new Connection(RPC_ENDPOINT, {
   wsEndpoint: RPC_WEBSOCKET_ENDPOINT,
@@ -80,8 +81,9 @@ function printDetails(wallet: Keypair, quoteToken: Token, bot: Bot) {
   logger.info('- Bot -');
 
   logger.info(`Using warp: ${bot.isWarp}`);
+  logger.info(`Using jito: ${bot.isJito}`);
   if (bot.isWarp) {
-    logger.info(`Warp fee: ${WARP_FEE}`);
+    logger.info(`${TRANSACTION_EXECUTOR} fee: ${CUSTOM_FEE}`);
   } else {
     logger.info(`Compute Unit limit: ${botConfig.unitLimit}`);
     logger.info(`Compute Unit price (micro lamports): ${botConfig.unitPrice}`);
@@ -143,7 +145,11 @@ const runListener = async () => {
 
   switch (TRANSACTION_EXECUTOR) {
     case 'warp': {
-      txExecutor = new WarpTransactionExecutor(WARP_FEE);
+      txExecutor = new WarpTransactionExecutor(CUSTOM_FEE);
+      break;
+    }
+    case 'jito': {
+      txExecutor = new JitoTransactionExecutor(CUSTOM_FEE, connection);
       break;
     }
     default: {

+ 131 - 0
transactions/jito-rpc-transaction-executor.ts

@@ -0,0 +1,131 @@
+import {
+  BlockhashWithExpiryBlockHeight,
+  Keypair,
+  PublicKey,
+  SystemProgram,
+  Connection,
+  TransactionMessage,
+  VersionedTransaction,
+} from '@solana/web3.js';
+import { TransactionExecutor } from './transaction-executor.interface';
+import { logger } from '../helpers';
+import axios, { AxiosError } from 'axios';
+import bs58 from 'bs58';
+import { Currency, CurrencyAmount } from '@raydium-io/raydium-sdk';
+
+export class JitoTransactionExecutor implements TransactionExecutor {
+  // https://jito-labs.gitbook.io/mev/searcher-resources/json-rpc-api-reference/bundles/gettipaccounts
+  private jitpTipAccounts = [
+    'Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY',
+    'DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL',
+    '96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5',
+    '3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT',
+    'HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe',
+    'ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49',
+    'ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt',
+    'DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh',
+  ];
+
+  private JitoFeeWallet: PublicKey;
+
+  constructor(
+    private readonly jitoFee: string,
+    private readonly connection: Connection,
+  ) {
+    this.JitoFeeWallet = this.getRandomValidatorKey();
+  }
+
+  private getRandomValidatorKey(): PublicKey {
+    const randomValidator = this.jitpTipAccounts[Math.floor(Math.random() * this.jitpTipAccounts.length)];
+    return new PublicKey(randomValidator);
+  }
+
+  public async executeAndConfirm(
+    transaction: VersionedTransaction,
+    payer: Keypair,
+    latestBlockhash: BlockhashWithExpiryBlockHeight,
+  ): Promise<{ confirmed: boolean; signature?: string }> {
+    logger.debug('Starting Jito transaction execution...');
+    this.JitoFeeWallet = this.getRandomValidatorKey(); // Update wallet key each execution
+    logger.trace(`Selected Jito fee wallet: ${this.JitoFeeWallet.toBase58()}`);
+
+    try {
+      const fee = new CurrencyAmount(Currency.SOL, this.jitoFee, false).raw.toNumber();
+      logger.trace(`Calculated fee: ${fee} lamports`);
+
+      const jitTipTxFeeMessage = new TransactionMessage({
+        payerKey: payer.publicKey,
+        recentBlockhash: latestBlockhash.blockhash,
+        instructions: [
+          SystemProgram.transfer({
+            fromPubkey: payer.publicKey,
+            toPubkey: this.JitoFeeWallet,
+            lamports: fee,
+          }),
+        ],
+      }).compileToV0Message();
+
+      const jitoFeeTx = new VersionedTransaction(jitTipTxFeeMessage);
+      jitoFeeTx.sign([payer]);
+
+      const jitoTxsignature = bs58.encode(jitoFeeTx.signatures[0]);
+
+      // Serialize the transactions once here
+      const serializedjitoFeeTx = bs58.encode(jitoFeeTx.serialize());
+      const serializedTransaction = bs58.encode(transaction.serialize());
+      const serializedTransactions = [serializedjitoFeeTx, serializedTransaction];
+
+      // https://jito-labs.gitbook.io/mev/searcher-resources/json-rpc-api-reference/url
+      const endpoints = [
+        'https://mainnet.block-engine.jito.wtf/api/v1/bundles',
+        'https://amsterdam.mainnet.block-engine.jito.wtf/api/v1/bundles',
+        'https://frankfurt.mainnet.block-engine.jito.wtf/api/v1/bundles',
+        'https://ny.mainnet.block-engine.jito.wtf/api/v1/bundles',
+        'https://tokyo.mainnet.block-engine.jito.wtf/api/v1/bundles',
+      ];
+
+      const requests = endpoints.map((url) =>
+        axios.post(url, {
+          jsonrpc: '2.0',
+          id: 1,
+          method: 'sendBundle',
+          params: [serializedTransactions],
+        }),
+      );
+
+      logger.trace('Sending transactions to endpoints...');
+      const results = await Promise.all(requests.map((p) => p.catch((e) => e)));
+
+      const successfulResults = results.filter((result) => !(result instanceof Error));
+
+      if (successfulResults.length > 0) {
+        logger.trace(`At least one successful response`);
+        logger.debug(`Confirming jito transaction...`);
+        return await this.confirm(jitoTxsignature, latestBlockhash);
+      } else {
+        logger.debug(`No successful responses received for jito`);
+      }
+
+      return { confirmed: false };
+    } catch (error) {
+      if (error instanceof AxiosError) {
+        logger.trace({ error: error.response?.data }, 'Failed to execute warp transaction');
+      }
+      logger.error('Error during transaction execution', error);
+      return { confirmed: false };
+    }
+  }
+
+  private async confirm(signature: string, latestBlockhash: BlockhashWithExpiryBlockHeight) {
+    const confirmation = await this.connection.confirmTransaction(
+      {
+        signature,
+        lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
+        blockhash: latestBlockhash.blockhash,
+      },
+      this.connection.commitment,
+    );
+
+    return { confirmed: !confirmation.value.err, signature };
+  }
+}