Skip to content

Instantly share code, notes, and snippets.

@zhe-t
Last active June 3, 2025 07:05
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
}

@0xhammadali
Copy link

0xhammadali 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);
	}
}

}
`

@ankitzm
Copy link

ankitzm commented Jan 9, 2025

Did anyone got here txn success with jito using api call. How did you sent the transaction from frontend ?

@winer328
Copy link

winer328 commented Jan 9, 2025

it's same as be.

Did anyone got here txn success with jito using api call. How did you sent the transaction from frontend ?

@ankitzm
Copy link

ankitzm commented Jan 9, 2025

Sorry, I didn't understand.
sendBundle calls the api to get bundleId, but how it it send on chain from frontend ?

@winer328
Copy link

winer328 commented Jan 9, 2025

Sorry, I didn't understand. sendBundle calls the api to get bundleId, but how it it send on chain from frontend ?

we implement jito bundle with api request.
so also we can call it as api request.
Only sign tx with wallet in fe is different from be.
@Cool0328 (TG)

@soggyfeet3
Copy link

anyone know how to fix this

MEV opportunity found! Potential Profit: 0.000307478 SOL
Executing flash loan swaps with Jito...
Creating MarginFi account...
------ Transaction Details ๐Ÿ‘‡ ------
๐Ÿ“ Executing 1 transaction
๐Ÿ“ก Broadcast type: BUNDLE
๐Ÿ’ธ Bundle tip: undefined SOL

Error: API bundle failed: Bundle must tip at least 1000 lamports

@yanCode
Copy link

yanCode commented Jun 2, 2025

๐Ÿ’ธ Bundle tip: undefined SOL
Error: API bundle failed: Bundle must tip at least 1000 lamports
@soggyfeet3 as it says the Bundle tip you offered is undefined. it at least should be 1000 lamports.
basically you need to bribe jito using tip, then jito will help land your transaction fast

@soggyfeet3
Copy link

๐Ÿ’ธ Bundle tip: undefined SOL Error: API bundle failed: Bundle must tip at least 1000 lamports @soggyfeet3 as it says the Bundle tip you offered is undefined. it at least should be 1000 lamports. basically you need to bribe jito using tip, then jito will help land your transaction fast

@yanCode i solved this issue by using an older version of marginfi but the issue im stuck on now is I donโ€™t know how to use Jupiter quote/swap without getting an error about solana transaction max account limit or unit8array errorโ€ฆ you know how to fix that?

@yanCode
Copy link

yanCode commented Jun 3, 2025

@soggyfeet3 I did a quick check on marginfi, i think you mean this project: https://github.com/mrgnlabs/mrgn-ts
i saw where you have the error ๐Ÿ’ธ Bundle tip:undefined SOL

if (broadcastType === "BUNDLE") {
    console.log(
      `๐Ÿ’ธ Bundle tip: ${maxCapUi ? Math.min(processOpts.bundleTipUi ?? 0, maxCapUi) : processOpts.bundleTipUi} SOL`
    );
  } 

it seems both maxCapUi and bundleTipUi are all undefined. but from the static logic (without running), I found there are many parts in the whole project can call this part. I need to know which part exactly calls this logic. then I can find the reason why these two values are undefined

@soggyfeet3
Copy link

@yanCode thats not my issue anymore I resolved that part by downgrading marginfi to 4.04. My main issue now is Iโ€™m trying to use Jupiter quote/swaps an I get a unit8array or getting over the solana transaction size limit of 1232. I also see ppl using a custom on chain program and their transactions succeeds.

you have discord or telegram we can chat there and if you got time can assist me!

@winer328
Copy link

winer328 commented Jun 3, 2025

anyone know how to fix this

MEV opportunity found! Potential Profit: 0.000307478 SOL

Executing flash loan swaps with Jito...
Creating MarginFi account...
------ Transaction Details ๐Ÿ‘‡ ------
๐Ÿ“ Executing 1 transaction
๐Ÿ“ก Broadcast type: BUNDLE
๐Ÿ’ธ Bundle tip: undefined SOL
Error: API bundle failed: Bundle must tip at least 1000 lamports

I'm very familiar with MEV project and made some profits from it.
If you need help, please let me know @cool0328 in telegram.

@yanCode
Copy link

yanCode commented Jun 3, 2025

@yanCode thats not my issue anymore I resolved that part by downgrading marginfi to 4.04. My main issue now is Iโ€™m trying to use Jupiter quote/swaps an I get a unit8array or getting over the solana transaction size limit of 1232. I also see ppl using a custom on chain program and their transactions succeeds.

you have discord or telegram we can chat there and if you got time can assist me!
one solution i know to bypass the transaction limit is to split the oversize transaction to many, jito bundle can submit up to 5 transactions together.
but if the transaction is returned by Jupiter quote/swaps, i don't think it should be oversize. because itself can execute the swap transaction if we don't send it via jito bundle.
I also see ppl using a custom on chain program, well, it sounds intertesting to me.
my telgram is @tszsy

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