An Ethereum bundle is an ordered array of fully signed raw transactions plus an optional target block, submitted to a block builder rather than to the public mempool. Inclusion is atomic over the array: either every transaction lands consecutively in the target block or none of them do. eth_sendBundle is not part of the Ethereum execution-apis specification; it is a builder-specific method, so pointing it at a standard full node or a general-purpose RPC provider returns a method-not-found error rather than a submission failure. The three distinct reasons a bundle is not included are malformed (invalid signature, nonce, or gas), valid-but-unprofitable (simulation succeeds but a better bid wins), and wrong-endpoint (the call never reached a builder). This article covers the request shape, the simulation-before-submission workflow, a runnable Node.js example, and a diagnostic method you can run against your own endpoint.
Bundle Mechanics: Atomic Ordered Transaction Arrays
A bundle is an ordered array of fully signed raw transactions plus an optional target block, submitted to a block builder rather than to the public mempool. The defining property is atomicity over the array: either every transaction in the bundle lands consecutively in the target block or none of them do. This is what makes bundles useful for backrun and arbitrage patterns, where a profitable sequence only works if all legs execute in order.
The same property makes a single malformed member poison the whole submission. If one transaction has a bad signature, a stale nonce, or insufficient gas, the builder cannot include the array atomically and the entire bundle is discarded. This is a structural difference from a plain transaction, which is evaluated independently and can be replaced or dropped without affecting anything else.
Because bundle members are pre-signed, replacing a member means re-signing the whole bundle. A bundle whose first transaction was already included by another path leaves the remaining members either atomic-failing or, if submitted separately, racing on nonce. Nonce management is therefore a first-class concern; see EVM nonce management with eth_getTransactionCount for the read side of that problem.
- Bundle = ordered array of signed raw transactions + optional target block.
- Atomicity: all members land consecutively in the target block, or none do.
- One malformed member invalidates the entire bundle.
- Pre-signed members mean replacement requires re-signing the whole bundle.
Why a Bundle Endpoint Is Not a Regular RPC Endpoint
eth_sendBundle is not part of the Ethereum execution-apis specification at all. It is a builder-specific method, documented by Flashbots at docs.flashbots.net alongside eth_callBundle and eth_cancelBundle. Pointing a bundle call at a standard full node or a general-purpose RPC provider returns a method-not-found error rather than a submission failure.
Availability and the exact response shape vary by provider. Some builders expose only eth_sendBundle; others add simulation or cancellation methods. Treat the method set as documented / varies by provider, and verify it against the endpoint you actually intend to use before building a submission pipeline around it.
For ordinary read and write traffic, a standard Ethereum endpoint is still the right tool. OnFinality's Ethereum network page and the RPC endpoints guide describe the conventional JSON-RPC surface, which is where eth_sendRawTransaction, eth_getTransactionCount, and block reads belong.
- eth_sendBundle is builder-specific, not part of execution-apis.
- Wrong endpoint returns method-not-found, not a submission failure.
- Method availability and response shape are documented / varies by provider.
Request Fields and What Each One Constrains
The bundle request shape documented by Flashbots contains three fields. The signed transaction array is the ordered list of raw transactions. The target block number or hash constrains eligibility: a bundle is only eligible for the block it is addressed to, so a stale target silently never includes. The optional reverting-transaction-hashes field lets a searcher declare which members may revert without invalidating the bundle.
The transaction fields inside each raw transaction follow the Ethereum execution-apis specification, including the EIP-1559 fee fields that determine the effective priority fee a bundle must bid. EIP-1559 defines maxFeePerGas, maxPriorityFeePerGas, and the base fee burn, which together determine what a builder actually receives from a bundle.
A common mistake is to treat the target block as advisory. It is not. If you build a bundle for block N and submit it after N is produced, the bundle is ineligible regardless of how profitable it would have been. Re-targeting means rebuilding and re-signing.
- signed transaction array: ordered, fully signed raw transactions.
- target block: number or hash; eligibility is limited to that block.
- reverting-transaction-hashes: members allowed to revert without invalidating the bundle.
- Fee fields follow EIP-1559 and determine the effective priority fee bid.
Simulation Before Submission with eth_callBundle
eth_callBundle previews a bundle against current state for a given block, returning realised gas usage and the coinbase transfer. This is the only way to learn that a bundle would revert before burning a block on it. A simulation that returns a JSON-RPC error points at a malformed bundle; a simulation that succeeds but shows a low coinbase transfer points at a valid-but-unprofitable bundle.
Run the simulation against the same block you intend to target. Simulating against a different block state can produce a result that does not reflect what the builder will see. If the simulation succeeds, record the gas and coinbase values so you can compare them against what actually happens after submission.
Simulation is not a guarantee of inclusion. It tells you the bundle is well-formed and executable against the state you simulated; it does not tell you whether a competing searcher will bid more for the same opportunity.
- eth_callBundle returns realised gas and coinbase transfer.
- Simulate against the same block you intend to target.
- Simulation success is necessary but not sufficient for inclusion.
Runnable Node.js Example: Build, Simulate, Submit, Verify
The script below builds a bundle request, calls eth_callBundle to simulate it, submits with eth_sendBundle, and then verifies inclusion by fetching the target block and checking that the bundle's transaction hashes appear in the expected order. It uses the global fetch API available in modern Node.js and assumes you have already signed the raw transactions.
Replace the endpoint URL with the builder endpoint you intend to use. The script does not assume any particular provider; it will surface a method-not-found error if the endpoint is not a builder endpoint, which is itself a useful diagnostic.
const ENDPOINT = process.env.BUNDLE_RPC_URL; // builder endpoint
const TARGET_BLOCK = process.env.TARGET_BLOCK; // e.g. "0x112a880"
const RAW_TXS = JSON.parse(process.env.RAW_TXS); // array of 0x-prefixed signed raw txs
async function rpc(method, params) {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params })
});
const json = await res.json();
if (json.error) throw new Error(method + " error: " + JSON.stringify(json.error));
return json.result;
}
async function main() {
const bundle = { txs: RAW_TXS, blockNumber: TARGET_BLOCK };
// 1. Simulate against the target block
const sim = await rpc("eth_callBundle", [bundle, TARGET_BLOCK]);
console.log("simulation:", JSON.stringify(sim, null, 2));
// 2. Submit
const submitted = await rpc("eth_sendBundle", [bundle]);
console.log("submitted bundle hash:", submitted.bundleHash);
// 3. Verify inclusion in the target block
const block = await rpc("eth_getBlockByNumber", [TARGET_BLOCK, false]);
if (!block) {
console.log("target block not yet produced");
return;
}
const included = block.transactions;
const expected = RAW_TXS.map((raw) => {
// derive hash from raw tx using your signing library, e.g. ethers.Transaction.from(raw).hash
return raw;
});
console.log("block tx count:", included.length);
console.log("bundle members present:", expected.every((h) => included.includes(h)));
}
main().catch((e) => { console.error(e.message); process.exit(1); });Results Table: Measuring Against Your Own Endpoint
Because bundle endpoints and their method sets vary by provider, the only reliable way to characterise one is to measure it yourself. Fill in the table below for each endpoint you intend to use. Do not assume values from another provider transfer.
Run the script above against each endpoint and record the outcome. The pattern of results tells you whether the endpoint is a builder endpoint at all, whether it supports simulation, and whether your bundles are reaching the inclusion stage.
- Endpoint URL: the builder endpoint you tested.
- eth_callBundle supported: yes / no / method-not-found.
- eth_sendBundle supported: yes / no / method-not-found.
- Simulation result: success with gas and coinbase values, or error.
- Submission response: bundle hash returned, or error.
- Inclusion observed in target block: yes / no / block not yet produced.
The Three Failure Classes Kept Separate
Malformed bundles are invalid: a signature, nonce, or gas error makes the bundle unusable, and this surfaces as a JSON-RPC error from eth_callBundle. The fix is to re-sign or rebuild the offending member and re-simulate. This class is deterministic and reproducible.
Valid-but-unprofitable bundles simulate successfully but the builder found a better bid. This surfaces as a submission that returns a bundle hash and then simply does not appear in the target block. There is no error to catch; the bundle was correct and lost on economics. The fix is to improve the bid or the opportunity, not to debug the request.
Wrong-endpoint failures occur when the call never reached a builder, surfacing as a method-not-found or an unexpected response shape. This is the class that produces the classic empty result from eth_sendBundle: a correctly formed bundle sent to an endpoint that does not implement the method. Verify the endpoint before interpreting any response.
- Malformed: JSON-RPC error from eth_callBundle; fix by re-signing.
- Valid-but-unprofitable: bundle hash returned, no inclusion; fix by improving the bid.
- Wrong-endpoint: method-not-found or unexpected shape; fix by using a builder endpoint.
Nonce and Replacement Interaction
Because bundle members are pre-signed, replacing a member means re-signing the whole bundle. If the first transaction of a bundle was already included by another path, the remaining members either atomic-fail or, if submitted separately, race on nonce. This is the interaction that bites hardest in production.
The read side of nonce management is covered in EVM nonce management with eth_getTransactionCount, and the replacement semantics for ordinary transactions are covered in eth_sendRawTransaction replacement and underpriced errors. Bundles do not inherit those replacement rules; they are rebuilt, not replaced.
If you need to inspect what is already pending before rebuilding, the Ethereum transaction pool and the txpool namespace page describes the inspection surface. After inclusion, eth_getBlockReceipts: bulk receipts in one call is a convenient way to confirm the status of every member in one request.
- Replacing a bundle member requires re-signing the entire bundle.
- A partially included bundle leaves remaining members atomic-failing or nonce-racing.
- Bundles are rebuilt, not replaced, unlike ordinary transactions.
Troubleshooting: Diagnosing an Empty eth_sendBundle Result
An empty result from eth_sendBundle is the most common symptom reported in public forums, and it maps to one of the three failure classes. Work through them in order: first confirm the endpoint implements the method, then confirm the bundle simulates, then confirm the target block is still current.
If eth_callBundle returns a JSON-RPC error, the bundle is malformed. If it succeeds but the submission returns a bundle hash and no inclusion follows, the bundle was valid but lost on economics. If the submission itself returns a method-not-found or an unexpected shape, the endpoint is not a builder endpoint.
A stale target block is a silent failure: the bundle is well-formed and may simulate, but it is ineligible for the block it was addressed to. Always re-check the target block number against the current head before interpreting a non-inclusion as an economic loss.
- Confirm the endpoint implements eth_sendBundle before interpreting any response.
- Run eth_callBundle; a JSON-RPC error means malformed.
- A bundle hash with no inclusion means valid-but-unprofitable.
- A method-not-found means wrong endpoint.
- A stale target block is a silent, non-economic failure.
Limitations and Tradeoffs
Bundle inclusion is a builder's commercial decision rather than a protocol guarantee. Nothing in this article is a claim about any provider's inclusion rate. A bundle that simulates successfully may still never be included, and there is no on-chain mechanism that forces a builder to accept it.
Targeting an unfinalised block means your own reorg and confirmation policy still applies. A bundle included in a block that is later reorged is not a settled outcome. Treat inclusion as provisional until your confirmation threshold is met.
Builder endpoints and their method sets change without notice. Methods documented today may be renamed, removed, or supplemented. Build your integration so that a method-not-found or an unexpected response shape is handled explicitly rather than assumed away.
- Inclusion is a builder's commercial decision, not a protocol guarantee.
- No claim is made here about any provider's inclusion rate.
- Unfinalised target blocks mean your reorg policy still applies.
- Builder endpoints and method sets change without notice.
Next Steps: Building a Bundle Pipeline
Start by measuring your chosen endpoint with the results table above. Confirm that eth_callBundle and eth_sendBundle are both supported, and record the response shapes you observe. This gives you a baseline before you build anything on top.
Then separate your logging by failure class. Log malformed bundles with the simulation error, log valid-but-unprofitable bundles with the coinbase value you simulated, and log wrong-endpoint failures separately. This makes the three classes distinguishable in production.
For the surrounding infrastructure, the OnFinality Learn hub collects the adjacent transaction, nonce, and receipt pages, and the API service and RPC pricing pages describe the conventional endpoint surface you will still need alongside any builder endpoint.
- Measure your endpoint before building on it.
- Log by failure class: malformed, unprofitable, wrong-endpoint.
- Keep a standard Ethereum endpoint alongside any builder endpoint.