jito-rpc-transaction-executor.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import {
  2. BlockhashWithExpiryBlockHeight,
  3. Keypair,
  4. PublicKey,
  5. SystemProgram,
  6. Connection,
  7. TransactionMessage,
  8. VersionedTransaction,
  9. } from '@solana/web3.js';
  10. import { TransactionExecutor } from './transaction-executor.interface';
  11. import { logger } from '../helpers';
  12. import axios, { AxiosError } from 'axios';
  13. import bs58 from 'bs58';
  14. import { Currency, CurrencyAmount } from '@raydium-io/raydium-sdk';
  15. export class JitoTransactionExecutor implements TransactionExecutor {
  16. // https://jito-labs.gitbook.io/mev/searcher-resources/json-rpc-api-reference/bundles/gettipaccounts
  17. private jitpTipAccounts = [
  18. 'Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY',
  19. 'DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL',
  20. '96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5',
  21. '3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT',
  22. 'HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe',
  23. 'ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49',
  24. 'ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt',
  25. 'DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh',
  26. ];
  27. private JitoFeeWallet: PublicKey;
  28. constructor(
  29. private readonly jitoFee: string,
  30. private readonly connection: Connection,
  31. ) {
  32. this.JitoFeeWallet = this.getRandomValidatorKey();
  33. }
  34. private getRandomValidatorKey(): PublicKey {
  35. const randomValidator = this.jitpTipAccounts[Math.floor(Math.random() * this.jitpTipAccounts.length)];
  36. return new PublicKey(randomValidator);
  37. }
  38. public async executeAndConfirm(
  39. transaction: VersionedTransaction,
  40. payer: Keypair,
  41. latestBlockhash: BlockhashWithExpiryBlockHeight,
  42. ): Promise<{ confirmed: boolean; signature?: string }> {
  43. logger.debug('Starting Jito transaction execution...');
  44. this.JitoFeeWallet = this.getRandomValidatorKey(); // Update wallet key each execution
  45. logger.trace(`Selected Jito fee wallet: ${this.JitoFeeWallet.toBase58()}`);
  46. try {
  47. const fee = new CurrencyAmount(Currency.SOL, this.jitoFee, false).raw.toNumber();
  48. logger.trace(`Calculated fee: ${fee} lamports`);
  49. const jitTipTxFeeMessage = new TransactionMessage({
  50. payerKey: payer.publicKey,
  51. recentBlockhash: latestBlockhash.blockhash,
  52. instructions: [
  53. SystemProgram.transfer({
  54. fromPubkey: payer.publicKey,
  55. toPubkey: this.JitoFeeWallet,
  56. lamports: fee,
  57. }),
  58. ],
  59. }).compileToV0Message();
  60. const jitoFeeTx = new VersionedTransaction(jitTipTxFeeMessage);
  61. jitoFeeTx.sign([payer]);
  62. const jitoTxsignature = bs58.encode(jitoFeeTx.signatures[0]);
  63. // Serialize the transactions once here
  64. const serializedjitoFeeTx = bs58.encode(jitoFeeTx.serialize());
  65. const serializedTransaction = bs58.encode(transaction.serialize());
  66. const serializedTransactions = [serializedjitoFeeTx, serializedTransaction];
  67. // https://jito-labs.gitbook.io/mev/searcher-resources/json-rpc-api-reference/url
  68. const endpoints = [
  69. 'https://mainnet.block-engine.jito.wtf/api/v1/bundles',
  70. 'https://amsterdam.mainnet.block-engine.jito.wtf/api/v1/bundles',
  71. 'https://frankfurt.mainnet.block-engine.jito.wtf/api/v1/bundles',
  72. 'https://ny.mainnet.block-engine.jito.wtf/api/v1/bundles',
  73. 'https://tokyo.mainnet.block-engine.jito.wtf/api/v1/bundles',
  74. ];
  75. const requests = endpoints.map((url) =>
  76. axios.post(url, {
  77. jsonrpc: '2.0',
  78. id: 1,
  79. method: 'sendBundle',
  80. params: [serializedTransactions],
  81. }),
  82. );
  83. logger.trace('Sending transactions to endpoints...');
  84. const results = await Promise.all(requests.map((p) => p.catch((e) => e)));
  85. const successfulResults = results.filter((result) => !(result instanceof Error));
  86. if (successfulResults.length > 0) {
  87. logger.trace(`At least one successful response`);
  88. logger.debug(`Confirming jito transaction...`);
  89. return await this.confirm(jitoTxsignature, latestBlockhash);
  90. } else {
  91. logger.debug(`No successful responses received for jito`);
  92. }
  93. return { confirmed: false };
  94. } catch (error) {
  95. if (error instanceof AxiosError) {
  96. logger.trace({ error: error.response?.data }, 'Failed to execute warp transaction');
  97. }
  98. logger.error('Error during transaction execution', error);
  99. return { confirmed: false };
  100. }
  101. }
  102. private async confirm(signature: string, latestBlockhash: BlockhashWithExpiryBlockHeight) {
  103. const confirmation = await this.connection.confirmTransaction(
  104. {
  105. signature,
  106. lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
  107. blockhash: latestBlockhash.blockhash,
  108. },
  109. this.connection.commitment,
  110. );
  111. return { confirmed: !confirmation.value.err, signature };
  112. }
  113. }