Skip to content

Instantly share code, notes, and snippets.

@zhe-t
Last active December 3, 2024 22:42
Show Gist options
  • Save zhe-t/60938c69e29276b7a9f098e1b0672c79 to your computer and use it in GitHub Desktop.
Save zhe-t/60938c69e29276b7a9f098e1b0672c79 to your computer and use it in GitHub Desktop.
Smart send a transaction using Jito dedicated endpoint
import { Connection, SendOptions } from '@solana/web3.js';
export type JitoRegion = 'mainnet' | 'amsterdam' | 'frankfurt' | 'ny' | 'tokyo';
export const JitoEndpoints = {
mainnet: 'https://mainnet.block-engine.jito.wtf/api/v1/transactions',
amsterdam: 'https://amsterdam.mainnet.block-engine.jito.wtf/api/v1/transactions',
frankfurt: 'https://frankfurt.mainnet.block-engine.jito.wtf/api/v1/transactions',
ny: 'https://ny.mainnet.block-engine.jito.wtf/api/v1/transactions',
tokyo: 'https://tokyo.mainnet.block-engine.jito.wtf/api/v1/transactions',
};
export function getJitoEndpoint(region: JitoRegion) {
return JitoEndpoints[region];
}
/**
* Send a transaction using Jito. This only supports sending a single transaction on mainnet only.
* See https://jito-labs.gitbook.io/mev/searcher-resources/json-rpc-api-reference/transactions-endpoint/sendtransaction.
* @param args.serialisedTx - A single transaction to be sent, in serialised form
* @param args.region - The region of the Jito endpoint to use
*/
export async function sendTxUsingJito({
serializedTx,
region = 'mainnet'
}: {
serializedTx: Uint8Array | Buffer | number[];
region: JitoRegion;
}) {
let rpcEndpoint = getJitoEndpoint(region);
let encodedTx = bs58.encode(serializedTx);
let payload = {
jsonrpc: "2.0",
id: 1,
method: "sendTransaction",
params: [encodedTx]
};
let res = await fetch(`${rpcEndpoint}?bundleOnly=true`, {
method: 'POST',
body: JSON.stringify(payload),
headers: { 'Content-Type': 'application/json' }
});
let json = await res.json();
if (json.error) {
throw new Error(json.error.message);
}
return json;
}
@crypt0miester
Copy link

hello ser, you have the wrong endpoints. otherwise the txn won't go thru.

  export const JitoEndpoints = {
    mainnet: 'https://mainnet.block-engine.jito.wtf/api/v1/transactions',
    amsterdam: 'https://amsterdam.mainnet.block-engine.jito.wtf/api/v1/transactions',
    frankfurt: 'https://frankfurt.mainnet.block-engine.jito.wtf/api/v1/transactions',
    ny: 'https://ny.mainnet.block-engine.jito.wtf/api/v1/transactions',
    tokyo: 'https://tokyo.mainnet.block-engine.jito.wtf/api/v1/transactions',
  };

if you add ?bundleOnly=true at the end, then you can send bundles.

https://jito-labs.gitbook.io/mev/searcher-resources/json-rpc-api-reference/transactions-endpoint/sendtransaction

@zhe-t
Copy link
Author

zhe-t commented Apr 20, 2024

Thx! Updated

@yujianFresh
Copy link

hi bro, how to build the serializedTx, only add tip instruction in it, right ? for example, one transfer tx, only contain transfer ins,if i want to send jito, i need add another ins for the tip, some thing wrong?

@SC4RECOIN
Copy link

SC4RECOIN commented Jul 26, 2024

modified for sending bundles:

export async function sendTxUsingJito(serializedTxs: (Uint8Array | Buffer | number[])[]): Promise<string> {
  let endpoint = 'https://mainnet.block-engine.jito.wtf/api/v1/bundles';

  let payload = {
    jsonrpc: "2.0",
    id: 1,
    method: "sendBundle",
    params: [serializedTxs.map(t => bs58.encode(t))]
  };

  let res = await fetch(endpoint, {
    method: 'POST',
    body: JSON.stringify(payload),
    headers: {'Content-Type': 'application/json'}
  });

  let json = await res.json();
  if (json.error) {
    throw new Error(json.error.message);
  }

  // return bundle ID
  return json.result;
}

and confirming bundles

export type bundleStatus = {
  jsonrpc: string
  result: {
    context: {
      slot: number
    }
    value: {
      bundle_id: string
      transactions: string[]
      slot: number
      confirmation_status: string
      err: any
    }[]
  }
  id: number
}

export async function getBundleStatus(id: string): Promise<bundleStatus> {
  let endpoint = 'https://mainnet.block-engine.jito.wtf/api/v1/bundles';

  let payload = {
    jsonrpc: "2.0",
    id: 1,
    method: "getBundleStatuses",
    params: [[id]]
  };

  let res = await fetch(endpoint, {
    method: 'POST',
    body: JSON.stringify(payload),
    headers: {'Content-Type': 'application/json'}
  });

  let json = await res.json();
  if (json.error) {
    throw new Error(json.error.message);
  }

  return json
}

@winer328
Copy link

winer328 commented Aug 6, 2024

Hi, bro.
I'm not sure how can I build serialized tx and include jito tip instruction.
Could you explain more detail?
I implemented with another way and I want know your method, too.

@catstersol
Copy link

I am getting a result but this result is not found on solscan. So I am not sure how it means, seems like it failed but there was no error.

{
jsonrpc: '2.0',
result: 'yeAuVKQRYVbJm7J5AfByZcihesgAu1ZzKFA6GVvvWcy41AuiDPwM5Bb6gVKrUA3bEDcjWhPVSnJZwTx2jCqErKd',
id: 1
}

@hammad-ali18
Copy link

hammad-ali18 commented Sep 5, 2024

I am getting this issue

Error: 7 PERMISSION_DENIED: The supplied pubkey is not authorized to generate a token.

configurations I am using
BLOCK_ENGINE_URL=block-engine.mainnet.frankfurt.jito.wtf AUTH_KEYPAIR_PATH=/home/xyz/wallet/keypair1.json BUNDLE_TRANSACTION_LIMIT=5 RPC_URL=my_own_rpc
While using sendBundle on mainnet

`
const sendBundle = async (
c: SearcherClient,
instructions1:TransactionInstruction[],
instructions2:TransactionInstruction[],
bundleTransactionLimit: number,
keypair: Keypair,
conn:Connection
)=>{
const _tipAccount = ( await c.getTipAccounts())[0];
console.log("tipAccount: ",_tipAccount);

const tipAccount = new PublicKey(_tipAccount);
const balance  = await conn.getBalance(keypair.publicKey);
console.log("current account has balance: ",balance);

let isLeaderSlot = false;

while(!isLeaderSlot){
    const next_leader = await c.getNextScheduledLeader();
    const num_slots = next_leader.nextLeaderSlot - next_leader.currentSlot;
    isLeaderSlot = num_slots <=2;
    console.log(`next jito leader slot in ${num_slots} slots`)
    await new Promise(r=> setTimeout(r,500));
}

const blockHash = await conn.getLatestBlockhash();
const b = new Bundle([],bundleTransactionLimit);
 
console.log(blockHash.blockhash);

const bundles = [b];

let maybeBundle = b.addTransactions(
buildMemoTransaction(keypair,instructions1,blockHash.blockhash),
buildMemoTransaction(keypair,instructions2,blockHash.blockhash)
);

if (isError(maybeBundle)){
    throw maybeBundle;
}

maybeBundle = maybeBundle.addTipTx(
    keypair,
    100_000,
    tipAccount,
    blockHash.blockhash
);

if (isError(maybeBundle)) {
    throw maybeBundle;
  }

bundles.map( async b => {
    try {
        const resp = await c.sendBundle(b);
        console.log('resp: ',resp);
    } catch (error) {
        console.error('error sending bundle:', error);

    }
});

}`

@cryptofie
Copy link

The supplied pubkey is not authorized to generate a token.

Were you able to get through this ?

@chilljello
Copy link

no I cant

@HIOXOIH
Copy link

HIOXOIH commented Nov 3, 2024

I am getting this issue

Error: 7 PERMISSION_DENIED: The supplied pubkey is not authorized to generate a token.

configurations I am using BLOCK_ENGINE_URL=block-engine.mainnet.frankfurt.jito.wtf AUTH_KEYPAIR_PATH=/home/xyz/wallet/keypair1.json BUNDLE_TRANSACTION_LIMIT=5 RPC_URL=my_own_rpc While using sendBundle on mainnet

` const sendBundle = async ( c: SearcherClient, instructions1:TransactionInstruction[], instructions2:TransactionInstruction[], bundleTransactionLimit: number, keypair: Keypair, conn:Connection )=>{ const _tipAccount = ( await c.getTipAccounts())[0]; console.log("tipAccount: ",_tipAccount);

const tipAccount = new PublicKey(_tipAccount);
const balance  = await conn.getBalance(keypair.publicKey);
console.log("current account has balance: ",balance);

let isLeaderSlot = false;

while(!isLeaderSlot){
    const next_leader = await c.getNextScheduledLeader();
    const num_slots = next_leader.nextLeaderSlot - next_leader.currentSlot;
    isLeaderSlot = num_slots <=2;
    console.log(`next jito leader slot in ${num_slots} slots`)
    await new Promise(r=> setTimeout(r,500));
}

const blockHash = await conn.getLatestBlockhash();
const b = new Bundle([],bundleTransactionLimit);
 
console.log(blockHash.blockhash);

const bundles = [b];

let maybeBundle = b.addTransactions(
buildMemoTransaction(keypair,instructions1,blockHash.blockhash),
buildMemoTransaction(keypair,instructions2,blockHash.blockhash)
);

if (isError(maybeBundle)){
    throw maybeBundle;
}

maybeBundle = maybeBundle.addTipTx(
    keypair,
    100_000,
    tipAccount,
    blockHash.blockhash
);

if (isError(maybeBundle)) {
    throw maybeBundle;
  }

bundles.map( async b => {
    try {
        const resp = await c.sendBundle(b);
        console.log('resp: ',resp);
    } catch (error) {
        console.error('error sending bundle:', error);

    }
});

}`

bro have you solved this? i encountered the same issue.

@youngqqcn
Copy link

jito do not need auth key now, so remove authKeypair when creating searcherClient, e.g:

update

const c = searcherClient(blockEngineUrl, authKeypair);

to

const c = searcherClient(blockEngineUrl);

@winer328
Copy link

Yes, you are exactly right.

@growwnoww
Copy link

growwnoww commented Dec 3, 2024

@youngqqcn thank you. i sent my bundle . it returns me bundleId . but after that there nothing on terminal why here is my code
export async function sendBundle(bundledTxns: VersionedTransaction[]) { try { console.log('controlled reached') const bundleId = await searcherClient.sendBundle(new JitoBundle(bundledTxns, bundledTxns.length)); console.log(Bundle ${bundleId} sent.`);

	///*
	// Assuming onBundleResult returns a Promise<BundleResult>
	const result = await new Promise((resolve, reject) => {
		searcherClient.onBundleResult(
			(result) => {
				console.log("Received bundle result:", result);
				resolve(result); // Resolve the promise with the result
			},
			(e: Error) => {
				console.error("Error receiving bundle result:", e);
				reject(e); // Reject the promise if there's an error
			}
		);
	});

	console.log("Result:", result);
	//*/
} catch (error) {
	const err = error as any;
	console.error("Error sending bundle:", err.message);

	if (err?.message?.includes("Bundle Dropped, no connected leader up soon")) {
		console.error("Error sending bundle: Bundle Dropped, no connected leader up soon.");
	} else {
		console.error("An unexpected error occurred:", err.message);
	}
}

}
`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment