Method and schema reference
TOOL Reference¶
This page defines the public methods and endpoints mentioned in the integration and architecture docs. It focuses on the current TOOL-facing surface: source bundle ingress, the quote flow, native searcher bundles, subscriptions, preconfirmed state reads, TEE attestation, callbacks, and MEV-Boost-compatible relay endpoints. Where bundles go after they are accepted is described in Delivery Rules.
Method Index¶
JSON-RPC
eth_sendBundle
Source bundle ingress with optional callback and builders.
JSON-RPC
ofp_sendUnsignedBundle
Quote flow step 1: request quotes for unsigned transactions.
JSON-RPC
ofp_sendSignedBundle
Quote flow step 5: finalize the chosen quote with signed transactions.
JSON-RPC
tool_sendBundle
Native TOOL bundle submission.
JSON-RPC
tool_simulateBundle
Native TOOL bundle simulation.
JSON-RPC
tool_nextBlockParams
Current block-building reserves and seal offset.
WebSocket
tool_subscribe
Commitments, subslots, next headers, and timing events.
JSON-RPC
Preconfirmed eth_*
TOOL-aware state, nonce, receipt, and block reads.
JSON-RPC
tee_getAttestation
TEE quote bound to nonce and TLS certificate.
HTTP Builder API MEV-Boost status, validator, header, and payload endpoints.
Conventions¶
| Convention | Meaning |
|---|---|
| JSON-RPC envelope | All RPC examples use {"jsonrpc":"2.0","id":1,"method":"...","params":[...]}. |
| Hash | 0x-prefixed 32-byte value. Used for commitment ids, transaction hashes, block hashes, storage slots, and parent hashes. |
| Address | 0x-prefixed 20-byte Ethereum address. |
| Signed transaction | 0x-prefixed Ethereum transaction bytes, not a transaction object. |
| Block number | Ethereum JSON-RPC block number or tag. For eth_sendBundle, ofp_sendUnsignedBundle, and ofp_sendSignedBundle, TOOL accepts only "0x0", "pending", "latest", or the node's exact next block number. Anything else fails with state not ready. |
| Time | Block and timestamp fields are Unix seconds unless otherwise noted. |
| Operation mask | Access masks use 1 for read, 2 for write, and 3 for read/write. |
| WebSocket subscriptions | Use tool_subscribe to create a subscription and tool_unsubscribe with the returned subscription id to cancel it. |
| Request signature | Send an X-Flashbots-Signature header with eth_sendBundle, eth_sendRawTransaction, ofp_sendUnsignedBundle and ofp_sendSignedBundle requests, signed with the standard Flashbots scheme: <address>:<signature> over the Keccak-256 hash of the request body. Clients that already sign requests for the Flashbots relay can use the same signing code. |
Current compatibility boundary
Blob transactions are not supported anywhere: every bundle path rejects them. The Flashbots-style builders field is supported on eth_sendBundle and ofp_sendSignedBundle; see The builders Field. The Flashbots-style replacementUuid field is recognized by the request shape but is not product-supported; clients should omit it.
Shared Schemas¶
Schema: SendBundleArgs
SendBundleArgs is the request object for inbound eth_sendBundle and the body TOOL sends to callbacks and builders (see Callback Receiver Contract). The delivered body never contains builders or callbackUrl.
{
"txs": ["0x..."],
"blockNumber": "0x...",
"minTimestamp": 0,
"maxTimestamp": 0,
"revertingTxHashes": ["0x..."],
"callbackUrl": "https://source.example/tool-callback",
"builders": ["titan"]
}
| Field | Type | Required | Description |
|---|---|---|---|
txs |
string[] |
Yes | Signed Ethereum transactions encoded as 0x bytes. Must be non-empty. Blob transactions are rejected. |
blockNumber |
quantity \| tag |
Yes | Target block: "0x0", "pending", "latest", or the node's exact next block number. Anything else fails with state not ready. |
minTimestamp |
uint64 |
No | eth_sendBundle only. Minimum acceptable TOOL block timestamp. The bundle is rejected if this is greater than the current TOOL block time. |
maxTimestamp |
uint64 |
No | eth_sendBundle only. Maximum acceptable TOOL block timestamp. The bundle is rejected if this is less than the current TOOL block time. |
revertingTxHashes |
hash[] |
No | eth_sendBundle only. Transaction hashes from txs that may revert without invalidating the bundle. Every listed hash must be present in txs. |
callbackUrl |
string |
No | Source callback endpoint. Must be an http or https URL with a host and no more than 512 characters. Rejected with callback URL provided but callback mode is disabled when the node runs without callback mode. |
builders |
string[] \| null |
No | Allow-list of external builders that may receive bundles built from this order flow. Omitted or null: the default (all builders without callbackUrl, none with it). []: none; send it literally, a client that drops empty arrays (Go omitempty) sends no field and gets the default. ["*"]: every registered builder. Names are case-insensitive, surrounding whitespace is ignored, duplicates are removed. Unknown names are dropped silently; if none is known the call fails with unknown builders: .... Registered on mainnet: flashbots, beaverbuild.org, buildernet, titan, btcs, bobthebuilder, eureka, bombora, quasar, ethgas, ultrasound. See The builders Field. |
replacementUuid |
uuid |
No | Flashbots-style field. Not currently product-supported. |
Schema: UnsignedTxArgs
UnsignedTxArgs is an eth_call-style transaction object used in txs of ofp_sendUnsignedBundle. It is not a signed transaction.
{
"from": "0x1111111111111111111111111111111111111111",
"to": "0x2222222222222222222222222222222222222222",
"value": "0x2386f26fc10000",
"gas": "0x5208",
"maxFeePerGas": "0x77359400",
"maxPriorityFeePerGas": "0x3b9aca00",
"nonce": "0x0",
"chainId": "0x0"
}
| Field | Type | Required | Description |
|---|---|---|---|
from |
address |
Yes | A real, funded account. |
to |
address |
No | Omitted for contract creation. |
nonce |
quantity |
Yes | Not filled from chain state. |
gas |
quantity |
Yes | Gas limit. Not estimated. |
value |
quantity |
No | Wei transferred. |
gasPrice |
quantity |
No | Legacy pricing. |
maxFeePerGas, maxPriorityFeePerGas |
quantity |
No | EIP-1559 pricing. |
data / input |
bytes |
No | Call data. |
accessList |
AccessList |
No | EIP-2930 access list. |
chainId |
quantity |
No | Chain id. |
authorizationList |
Authorization[] |
No | EIP-7702 authorizations. |
Schema: SignedBundleArgs
SignedBundleArgs is the request object for ofp_sendSignedBundle.
{
"txs": ["0x02f8b0..."],
"blockNumber": "0x0",
"callbackUrl": "https://your-ofp-endpoint.example.com/tool-final",
"builders": ["titan", "flashbots"],
"unsignedCmtId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
}
| Field | Type | Required | Description |
|---|---|---|---|
txs |
string[] |
Yes | The signed versions of the ofp_sendUnsignedBundle transactions, in the same order. Blob transactions are rejected. |
blockNumber |
quantity \| tag |
Yes | "0x0", "pending", "latest", or the node's exact next block number. |
callbackUrl |
string |
No | Final callback endpoint, independent of the step 1 URL. Without it there is no final callback. Same URL rules as SendBundleArgs. |
builders |
string[] \| null |
No | As in SendBundleArgs, then intersected with the chosen candidate's extBuilders as stored in the pool. |
unsignedCmtId |
hash |
Yes | The id of the chosen quote, not the bundleHash of ofp_sendUnsignedBundle. |
minTimestamp, maxTimestamp, and revertingTxHashes are ignored by this call.
Schema: ToolBundle
ToolBundle is the native request object for tool_sendBundle and tool_simulateBundle.
{
"basedCommitments": ["0x..."],
"blockTime": 0,
"blockParent": "0x...",
"subslot": 0,
"expirySubslot": 14,
"newTxs": ["0x..."]
}
| Field | Type | Required | Description |
|---|---|---|---|
basedCommitments |
hash[] |
No | Existing commitment ids this bundle builds on. At least one basedCommitments entry or one newTxs entry is required. |
blockTime |
uint64 |
No | Target block timestamp. If omitted, TOOL uses the current block time. If provided, it must match the current TOOL block time. |
blockParent |
hash |
No | Target parent block hash. If omitted, TOOL uses the current parent. If provided, it must match the current TOOL parent. |
subslot |
uint8 |
No | First subslot where the resulting commitment may be accepted. Defaults to the current next subslot. |
expirySubslot |
uint8 |
No | Last subslot where the resulting commitment may be accepted. Must not be earlier than the current next subslot. |
newTxs |
string[] |
No | New signed transactions encoded as 0x bytes. Blob transactions are rejected. |
Schema: Commitment
Commitment is the result for native bundle submission/simulation, the commitment of a quote, and the notification payload for tool_subscribe commitments.
{
"id": "0x...",
"blockTime": 0,
"blockParent": "0x...",
"subslot": 0,
"expirySubslot": 14,
"txsSize": 0,
"execGasUsed": 0,
"stateGasUsed": 0,
"maxExecGasLimit": 0,
"maxStateGasLimit": 0,
"fees": "0",
"accesses": {},
"callbackUrl": "0x...<hash of the callback URL>",
"extBuilders": ["flashbots", "titan"],
"unsignedTxs": ["0x..."],
"logs": [],
"stateDiff": { "accounts": {} }
}
| Field | Type | Description |
|---|---|---|
id |
hash |
Commitment identifier. For eth_sendBundle, this is returned as bundleHash. Covers the transactions, parent, subslot, expiry, block time, and reverting transactions; extBuilders and callbackUrl are not part of it. |
blockTime |
uint64 |
Block timestamp the commitment targets. |
blockParent |
hash |
Parent block hash the commitment targets. |
dirty |
boolean |
Present only when set. |
subslot |
uint8 |
First acceptable subslot. |
expirySubslot |
uint8 |
Last acceptable subslot. |
txsSize |
uint64 |
Total encoded transaction size in bytes. |
execGasUsed |
uint64 |
Execution gas consumed by the commitment. |
stateGasUsed |
uint64 |
State gas consumed by the commitment. |
maxExecGasLimit |
uint64 |
Largest remaining execution gas requirement for inclusion. |
maxStateGasLimit |
uint64 |
Largest remaining state gas requirement for inclusion. |
fees |
integer |
Total fees paid by the commitment transactions. |
accesses |
Accesses |
Account and storage read/write footprint. |
callbackUrl |
hash |
Keccak256 of the RLP encoding of the stored callback value, never the URL. Absent when there is no callback. The mixed-origin marker appears as 0x412ef0d083d26bfee59b608aa779a3b180b7b1c2f59dbb9b944c2df04be67fcf. Decoding keeps the hash string as the value, so a commitment decoded from JSON has no usable callback. |
extBuilders |
string[] |
External builders allowed to receive this commitment's bundle. Omitted when empty. |
unsignedTxs |
hash[] |
Hashes of quote-flow transactions not signed yet. |
logs |
Log[] |
Ethereum logs generated by execution. May be omitted when trimmed. |
stateDiff |
StateDiff \| null |
State changes generated by execution. May be null when trimmed. |
Transaction hashes and reverting hashes are not in JSON. A locally built commitment is gossiped without stateDiff and logs when it is 400 KB or more or needs more gas than its subslot offered. Where a commitment is delivered follows from callbackUrl and extBuilders; see Delivery Rules.
Schema: Accesses and StateDiff
Accesses is encoded as an object keyed by account address.
{
"0x0000000000000000000000000000000000000000": {
"balance": 1,
"nonce": 2,
"code": 0,
"storage": {
"0x...slot": 3
}
}
}
| Field | Type | Description |
|---|---|---|
balance |
uint8 |
Balance access mask. 1 read, 2 write, 3 read/write. |
nonce |
uint8 |
Nonce access mask. |
code |
uint8 |
Code access mask. |
storage |
object<hash,uint8> |
Storage slot access masks keyed by slot hash. |
StateDiff is encoded as an object keyed by account address under accounts.
{
"accounts": {
"0x0000000000000000000000000000000000000000": {
"balance": "0",
"nonce": 1,
"code": "0x...",
"storage": {
"0x...slot": "0x...value"
}
}
}
}
| Field | Type | Description |
|---|---|---|
balance |
string |
Net balance change as a signed decimal string. |
nonce |
uint64 |
New account nonce, when changed. |
code |
bytes |
New contract code or delegation bytes, when changed. |
storage |
object<hash,hash> |
New storage values keyed by slot hash. |
Schema: Subslot, BuildSchedule, and BlockParams
Subslot is the notification payload for tool_subscribe subslots.
{
"id": "0x...",
"txs": ["0x..."],
"blockTime": 0,
"blockParent": "0x...",
"index": 0,
"txsSize": 0,
"execGasUsed": 0,
"stateGasUsed": 0,
"receiptGasUsed": 0,
"accesses": {},
"stateDiff": { "accounts": {} },
"receipts": []
}
| Field | Type | Description |
|---|---|---|
id |
hash |
Subslot identifier. |
txs |
hash[] |
Transaction hashes accepted in the subslot. |
blockTime |
uint64 |
Target block timestamp. |
blockParent |
hash |
Target parent block hash. |
index |
uint8 |
Subslot index within the block. |
txsSize |
uint64 |
Total transaction size in bytes. |
execGasUsed |
uint64 |
Execution gas used by accepted transactions. |
stateGasUsed |
uint64 |
State gas used by accepted transactions. Always zero before Amsterdam. |
receiptGasUsed |
uint64 |
Gas used by accepted transactions as reported in receipts. Equals execGasUsed before Amsterdam. |
accesses |
Accesses |
Combined read/write footprint. |
stateDiff |
StateDiff \| null |
Combined state changes. |
receipts |
Receipt[] |
Ethereum receipts for executed transactions. |
BuildSchedule is the notification payload for tool_subscribe buildSchedule.
{
"blockParent": "0x...",
"blockTime": 0,
"firstSubslotStartsAt": "2026-01-01T00:00:00Z",
"blockSealAt": "2026-01-01T00:00:11Z"
}
| Field | Type | Description |
|---|---|---|
blockParent |
hash |
Parent block for the slot. |
blockTime |
uint64 |
Slot block timestamp. |
firstSubslotStartsAt |
time |
Scheduled wall-clock time for the first subslot. |
blockSealAt |
time |
Scheduled wall-clock time when the block is sealed. |
BlockParams is the result for tool_nextBlockParams.
{
"reservedGas": 0,
"reservedSize": 0,
"proposerPreferences": {
"blockSealOffsetMs": 0,
"filterOFAC": false
}
}
| Field | Type | Description |
|---|---|---|
reservedGas |
uint64 |
Gas reserved for non-TOOL block assembly. |
reservedSize |
uint64 |
Block byte size reserved for non-TOOL block assembly. |
proposerPreferences |
object \| null |
TOOL settings selected for the block, or null when none are set. blockSealOffsetMs (uint64) is the block seal offset in milliseconds. filterOFAC (boolean) is true when transactions involving OFAC listed addresses are rejected. |
Schema: GetAttestationRequest and GetAttestationResponse
| Field | Type | Required | Description |
|---|---|---|---|
userNonce |
hex string |
Yes | Caller-generated random nonce, hex without a 0x prefix. Must decode to 16 to 32 bytes. |
certScope |
string |
Yes | TLS certificate scope name to bind into the attestation. |
{
"evidenceBundle": {},
"tlsCertFingerprint": "...",
"challenge": "...",
"sessionId": "...",
"keyDelegation": "..."
}
| Field | Type | Description |
|---|---|---|
evidenceBundle |
object |
Session evidence bundle from the node's attestation portal, used by the verifier. Pass evidenceBundle to the verifier byte for byte as received: re-serializing evidenceBundle invalidates the proof. |
tlsCertFingerprint |
hex string |
SHA-256 fingerprint of the TLS certificate served for certScope, without a 0x prefix. |
challenge |
hex string |
SHA256(keccak256("TOOL_TLS_ATTESTATION_V1") || keccak256(userNonce) || keccak256(tlsCertPubKeyDER)), returned for diagnostics. Verifiers recompute the challenge themselves and do not use the returned challenge. |
sessionId |
hex string |
32-byte id of the attested session that signed keyDelegation. |
keyDelegation |
hex string |
65-byte signature by the attested session key that authorizes the TLS certificate key. |
eth_sendBundle
eth_sendBundle¶
Use eth_sendBundle when an orderflow provider or external PBS builder wants to submit a Flashbots-style bundle into TOOL. TOOL validates the signed transactions, executes the bundle against the current TOOL block state, creates a source commitment, and returns the commitment id as bundleHash. The response confirms only acceptance. Where the bundle and solutions on it go depends on callbackUrl and builders (table in The builders Field): without a callback the bundle itself is forwarded to its builders in the background; with a callback nothing is forwarded and the callback receives solutions. Builder failures are never reported. The bundle is gossiped, so callbacks come from whichever node built a solution, signed with that node's key: expect zero, one, or several per submission.
eth_sendRawTransaction takes the same path in miniature: the transaction is also wrapped into a bundle with all builders and no callback, so it is forwarded to all builders right away.
{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_sendBundle",
"params": [
{
"txs": ["0x..."],
"blockNumber": "0x...",
"minTimestamp": 0,
"maxTimestamp": 0,
"revertingTxHashes": [],
"callbackUrl": "https://source.example/tool-callback",
"builders": ["titan"]
}
]
}
| Parameter | Type | Description |
|---|---|---|
params[0] |
SendBundleArgs |
Source bundle request. |
Common rejection cases (full list in Errors):
| Case | Behavior |
|---|---|
TOOL is not enabled or not preconfirmed for the target block, or blockNumber is not accepted. |
state not ready. |
| Node is not in P2P TOOL mode. | Reject as unavailable in WS mode. |
txs is empty. |
bundle missing txs. |
| Any transaction is a blob transaction. | blob transactions not allowed. |
revertingTxHashes contains a hash not present in txs. |
some reverting txs not found in bundle: .... |
callbackUrl is not an http(s) URL with a host, or is over 512 characters. |
invalid callback URL. |
callbackUrl is set but the node runs without callback mode. |
callback URL provided but callback mode is disabled. |
None of the builders names is registered. |
unknown builders: .... |
| Timestamp does not match current TOOL state. | Reject. |
ofp_sendUnsignedBundle
ofp_sendUnsignedBundle¶
Use ofp_sendUnsignedBundle as step 1 of the quote flow, when the user must not sign until the outcome of a backrun is known. TOOL creates an unsigned commitment and gossips it; searchers build solutions on it, and the node that accepted this call POSTs each candidate to callbackUrl as a quote. Only that node holds the unsigned bodies, so the follow-up ofp_sendSignedBundle must go to the same node.
{
"jsonrpc": "2.0",
"id": 1,
"method": "ofp_sendUnsignedBundle",
"params": [
{
"txs": [{
"from": "0x1111111111111111111111111111111111111111",
"to": "0x2222222222222222222222222222222222222222",
"value": "0x2386f26fc10000",
"gas": "0x5208",
"maxFeePerGas": "0x77359400",
"maxPriorityFeePerGas": "0x3b9aca00",
"nonce": "0x0",
"chainId": "0x0"
}],
"blockNumber": "0x0",
"callbackUrl": "https://your-ofp-endpoint.example.com/tool-quote"
}
]
}
| Field | Type | Required | Description |
|---|---|---|---|
txs |
UnsignedTxArgs[] |
Yes | Call-style transaction objects. nonce is required. |
blockNumber |
quantity \| tag |
Yes | "0x0", "pending", "latest", or the node's exact next block number. |
callbackUrl |
string |
Yes | Quote endpoint. http or https URL with a host, at most 512 characters. |
builders |
string[] |
No | Ignored; the unsigned commitment allows all builders. Pass builders to ofp_sendSignedBundle. |
revertingTxHashes |
hash[] |
No | Rejected. |
Rate limit: 100 requests per second.
| Error | Meaning |
|---|---|
state not ready |
No TOOL state for blockNumber, or a value that is not accepted. |
rate limit exceeded |
More than 100 requests per second. |
invalid callback URL |
Missing, not an http(s) URL with a host, or over 512 characters. |
unsigned bundles are only supported in callback mode |
Callbacks are off on this node. |
revertingTxHashes not supported for unsigned bundle |
The field was sent. |
bundle missing txs |
Empty txs. |
ofp_sendSignedBundle
ofp_sendSignedBundle¶
Use ofp_sendSignedBundle as step 5 of the quote flow to finalize the chosen quote, on the same node as ofp_sendUnsignedBundle. TOOL re-executes the signed transactions with the solver's and compares the resulting state diff with the quoted one: any change of the outcome fails with signed bundle state diff mismatch; differences that leave the state untouched (a higher gas limit with the same gas used) are accepted. The signed bundle then always joins the pool and gossip, so relay merging can include it and searchers can build on it. If callbackUrl is set, TOOL sends the final callback (Callback Receiver Contract) before the RPC response, then forwards the bundle to the selected builders in the background.
If a transaction of the chosen candidate has not reached the node yet, the call waits up to 1 second for the missing transaction, or until the block is sealed if that comes first. A shorter client timeout ends the call first.
{
"jsonrpc": "2.0",
"id": 1,
"method": "ofp_sendSignedBundle",
"params": [
{
"txs": ["0x02f8b0..."],
"blockNumber": "0x0",
"callbackUrl": "https://your-ofp-endpoint.example.com/tool-final",
"builders": ["titan", "flashbots"],
"unsignedCmtId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
}
]
}
| Parameter | Type | Description |
|---|---|---|
params[0] |
SignedBundleArgs |
Signed transactions and the chosen quote id. |
Do not retry after a callback error
The node validates the bundle, stores it, gossips it, and only then delivers it. Your callback endpoint must not wait for the RPC response. If the callback fails, the call returns the error, but the bundle is already in the network and builders may already have it. A retry is rejected as already known.
| Error | Meaning |
|---|---|
state not ready |
No TOOL state for blockNumber, or a value that is not accepted. |
invalid callback URL |
Not an http(s) URL with a host, or over 512 characters. |
blob transactions not allowed |
A blob transaction. |
unknown builders: ... |
None of the names is registered. |
unsignedCmtId is required |
Zero or missing unsignedCmtId. |
unsigned commitment not found |
The id is wrong (use the quote's id), the block moved on, or the candidate left the pool while the call waited. |
signed tx count mismatch with unsigned commitment |
txs is empty or does not line up with the candidate. |
some signed txs not found |
The solver's transactions did not arrive before the block was sealed or changed. |
unsigned txs not found |
The candidate has no state diff here and this node does not hold your unsigned bodies: call the step 1 node. |
signed bundle state diff mismatch |
The signed parameters change the outcome: start again from step 1. |
| a callback delivery error | Your callback endpoint did not acknowledge. The commitment is already stored and gossiped: do not retry. |
tool_sendBundle
tool_sendBundle¶
Use tool_sendBundle when a TOOL searcher has built a native bundle from one or more base commitments and optional new signed transactions. The method creates the solution, stores it in the TOOL pool, gossips it, and starts its delivery in the background. It returns before delivery: the response does not say whether the solution was delivered, and delivery failures are only logged on the node. already known means the solution is already in the network; do not resubmit. The call is bounded by the node's RPC execution timeout, which covers preparation and execution but not delivery.
The solution's callback and builders are derived from its bases: without bases it gets all builders and no callback; with bases the builders are the intersection and the callback is kept only if every base has the same one, otherwise it becomes mixed-origin (see Callback and Builders of a Solution). Read callbackUrl and extBuilders in the returned Commitment to see where it goes. See Submitting Solutions.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tool_sendBundle",
"params": [
{
"basedCommitments": ["0x..."],
"newTxs": ["0x..."],
"blockTime": 0,
"blockParent": "0x...",
"subslot": 0,
"expirySubslot": 14
}
]
}
| Parameter | Type | Description |
|---|---|---|
params[0] |
ToolBundle |
Native TOOL bundle request. At least one base commitment or one new transaction is required. |
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "0x...",
"blockTime": 0,
"blockParent": "0x...",
"subslot": 0,
"expirySubslot": 14,
"txsSize": 0,
"execGasUsed": 0,
"stateGasUsed": 0,
"maxExecGasLimit": 0,
"maxStateGasLimit": 0,
"fees": "0",
"accesses": {},
"extBuilders": ["flashbots", "titan"],
"stateDiff": { "accounts": {} }
}
}
| Result field | Type | Description |
|---|---|---|
result |
Commitment |
Full commitment object created from the native bundle. |
Common rejection cases (see Rejection Checks):
| Case | Behavior |
|---|---|
basedCommitments and newTxs are both empty. |
empty transactions list. |
Any newTxs transaction is a blob transaction. |
blob transactions are not supported. |
Provided blockTime, blockParent, or subslot does not match current TOOL state. |
Reject. |
| A base commitment is unknown, future-dated, or targets a different parent. | Reject. |
| A base expires earlier than the bundle. | invalid commitment <hash> expiration. |
| A base with unsigned transactions is not first. | commitments with unsigned txs must be first in the list. |
| A conflicting base's transactions did not arrive within one second, or the block changed meanwhile. | access lists are not compatible and N based txs not found. |
| An unsigned base without callback mode or without a single callback. | unsigned commitment requires callback mode and a single callback URL (...). |
| No usable callback and no common builder, outside relay merging. | commitment has no delivery target (...). |
| Resulting size or gas exceeds current block/subslot limits. | Reject. |
tool_simulateBundle
tool_simulateBundle¶
Use tool_simulateBundle to evaluate the same native ToolBundle shape used by tool_sendBundle. It runs the same preparation, including the merge rules and rejection checks, and returns the commitment without storing, gossiping, or delivering anything.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tool_simulateBundle",
"params": [
{
"basedCommitments": ["0x..."],
"newTxs": ["0x..."]
}
]
}
| Parameter | Type | Description |
|---|---|---|
params[0] |
ToolBundle |
Native TOOL bundle request. Same schema as tool_sendBundle. |
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "0x...",
"blockTime": 0,
"blockParent": "0x...",
"subslot": 0,
"expirySubslot": 14,
"txsSize": 0,
"execGasUsed": 0,
"stateGasUsed": 0,
"maxExecGasLimit": 0,
"maxStateGasLimit": 0,
"fees": "0",
"accesses": {},
"stateDiff": { "accounts": {} }
}
}
| Result field | Type | Description |
|---|---|---|
result |
Commitment |
Simulated commitment object. It is not submitted or propagated. |
tool_nextBlockParams
tool_nextBlockParams¶
Use tool_nextBlockParams to read the current block-building constraints before creating or sizing a native TOOL bundle.
| Parameter | Type | Description |
|---|---|---|
params[0] |
hash |
Parent block hash expected by the caller. Must match current TOOL state. |
params[1] |
uint64 |
Block timestamp expected by the caller. Must match current TOOL state. |
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"reservedGas": 0,
"reservedSize": 0,
"proposerPreferences": {
"blockSealOffsetMs": 0,
"filterOFAC": false
}
}
}
| Result field | Type | Description |
|---|---|---|
reservedGas |
uint64 |
Gas reserved for non-TOOL block assembly. |
reservedSize |
uint64 |
Byte size reserved for non-TOOL block assembly. |
proposerPreferences |
object \| null |
blockSealOffsetMs and filterOFAC selected for the block, or null when none are set. See BlockParams. |
tool_subscribe
tool_subscribe¶
Use tool_subscribe over WebSocket for live TOOL events. The initial result is a subscription id. Notifications use the standard JSON-RPC subscription shape.
| Topic | Parameters | Notification payload | Use case |
|---|---|---|---|
commitments |
["commitments", {}] or ["commitments", {"0x...address": true}] |
Commitment |
Receive commitment events. Address filters match storage writes for the listed addresses. Use {} for all commitments. |
subslots |
["subslots"] |
Subslot |
Observe accepted subslot events. |
nextHeaders |
["nextHeaders"] |
Ethereum Header |
Observe the header currently being built. |
buildSchedule |
["buildSchedule"] |
BuildSchedule |
Observe the scheduled first subslot start and block seal time after consensus bootstrap. |
{
"jsonrpc": "2.0",
"method": "tool_subscription",
"params": {
"subscription": "0x...",
"result": {}
}
}
| Result source | Type | Description |
|---|---|---|
tool_subscribe result |
subscriptionId |
Subscription id returned by the initial JSON-RPC response. |
tool_subscription.params.subscription |
subscriptionId |
Id of the subscription that produced a notification. |
tool_subscription.params.result |
Commitment, Subslot, Ethereum Header, or BuildSchedule |
Topic-specific event payload. |
tool_unsubscribe result |
boolean |
true when a live subscription was cancelled. |
eth_call / eth_getTransactionCount / eth_getTransactionReceipt / eth_getBlockByHash
Preconfirmed eth_* Overrides¶
TOOL preserves standard Ethereum JSON-RPC method names for selected read paths. When the requested block is available in TOOL preconfirmed state, these methods read from that state. When TOOL state is not available for the selector or object, the implementation falls back to the normal Ethereum behavior.
| Method | Standard parameters |
|---|---|
eth_call |
Transaction call object, block selector, optional state overrides, optional block overrides. |
eth_getTransactionCount |
Address, block selector. |
eth_getTransactionReceipt |
Transaction hash. |
eth_getBlockByHash |
Block hash, fullTx boolean. |
| Method | TOOL result behavior |
|---|---|
eth_call |
Executes the call against TOOL preconfirmed state when available. Revert handling matches the normal eth_call path. |
eth_getTransactionCount |
Returns the TOOL preconfirmed nonce when available. Falls back to the normal nonce otherwise. |
eth_getTransactionReceipt |
Returns a standard receipt for TOOL-layer accepted subslot transactions when available. Falls back to the normal receipt otherwise. |
eth_getBlockByHash |
Returns a standard block object for a TOOL preconfirmed block view when available. Falls back to the normal block lookup otherwise. |
Integration guidance
Treat these as compatibility overrides, not separate TOOL methods. Clients can keep standard Ethereum call shapes and use explicit block selectors where they need preconfirmed state.
tee_getAttestation
tee_getAttestation¶
Use tee_getAttestation when an integrator needs a fresh TEE attestation quote bound to a caller-provided nonce and the TLS certificate public key served by the node.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tee_getAttestation",
"params": [
{
"userNonce": "00112233445566778899aabbccddeeff",
"certScope": "network"
}
]
}
| Parameter | Type | Description |
|---|---|---|
params[0] |
GetAttestationRequest |
Freshness challenge and TLS certificate scope. |
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"evidenceBundle": {},
"tlsCertFingerprint": "...",
"challenge": "...",
"sessionId": "...",
"keyDelegation": "..."
}
}
| Result field | Type | Description |
|---|---|---|
evidenceBundle |
object |
Session evidence bundle used by the verifier. Keep evidenceBundle byte for byte as received. |
tlsCertFingerprint |
hex string |
SHA-256 fingerprint of the TLS certificate served for certScope, without a 0x prefix. |
challenge |
hex string |
The challenge the bundle answers, returned for diagnostics only. |
sessionId |
hex string |
32-byte id of the attested session that signed keyDelegation. |
keyDelegation |
hex string |
65-byte signature by the attested session key that authorizes the TLS certificate key. |
Verifier checklist (tlsCertPubKeyDER is the DER-encoded public key of the certificate observed during the TLS handshake):
| Check | Reason |
|---|---|
userNonce is newly generated and 16 to 32 random bytes. |
Prevents replay. |
tlsCertFingerprint matches the SHA-256 of the certificate observed during the TLS handshake. |
Proves the attested service owns the live TLS endpoint. |
Recompute the challenge yourself: SHA256(keccak256("TOOL_TLS_ATTESTATION_V1") || keccak256(userNonce) || keccak256(tlsCertPubKeyDER)). |
Binds the evidence to your nonce and the certificate public key. |
evidenceBundle, unchanged, verifies against your recomputed challenge and the deployment attestation policy. |
Confirms the expected enclave/service identity and gives the attested session id and session key. |
sessionId equals the verified session id, and keyDelegation recovers to the verified session key over keccak256("ATAKIT_SESSION_SIGN_V1" || keccak256("TOOL_TLS_DELEGATION_V1") || sessionId || keccak256(tlsCertPubKeyDER)). |
Proves the attested CVM holds this TLS key. Without the key delegation check, any server could relay a bundle from another node. |
Callback Receiver Contract¶
TOOL sends a Flashbots-compatible JSON-RPC POST to a callback for solutions on the source's own flow (and for the final ofp_sendSignedBundle bundle). A callback sees only its own source's transactions: a solution that merges bundles of different sources, or with a bundle without a callback, is mixed-origin and is never delivered to a callback. See Delivery Rules.
POST {callbackUrl}
Content-Type: application/json
Accept: application/json
X-Flashbots-Signature: <address>:<hex signature>
{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_sendBundle",
"params": [
{
"txs": ["0x02f8b0...", "0x02f8a3..."],
"blockNumber": "0x152dd21",
"minTimestamp": 1753000000,
"maxTimestamp": 1753000000
}
]
}
| Requirement | Description |
|---|---|
| Method | Callback receivers should accept eth_sendBundle with SendBundleArgs. |
| Body | txs: every transaction in order, bases first (the source's, then the searcher's), ready to broadcast. blockNumber: parent plus one. minTimestamp and maxTimestamp: the block time. revertingTxHashes appears only when not empty. builders is never included. The same body goes to every URL of every allowed builder. |
| Signature header | Signed with the FlashbotsKey of the node that built and delivers the bundle; empty if that node has no FlashbotsKey. Builders also receive their additional_headers. |
| Sender | Solutions are sent from whichever node built them, with that node's key. Expect zero, one, or many calls per submission, from different nodes. Deduplicate by transaction hashes. |
| Timeout | 5 seconds per request, independent of the timeout of the RPC call that caused the delivery. |
| Retries | None. |
| Success response | HTTP status must be below 300, and the JSON-RPC response must not contain an error. |
| Response size | TOOL reads at most 64 KiB of the callback response body. |
Verifying a signed callback (the standard Flashbots scheme):
- Split
X-Flashbots-Signatureon:intosignerAddressandsignatureHex. bodyHashHex= the hex string (0x..., 66 characters) ofkeccak256(rawBody).digest = keccak256("\x19Ethereum Signed Message:\n66" + bodyHashHex).ecrecover(digest, signatureHex)must equalsignerAddressand a TOOL signing address you trust (for solutions built by other nodes, that node's address).
Quote Callback Contract¶
For each candidate solution on an ofp_sendUnsignedBundle commitment, TOOL POSTs a quote to that call's callbackUrl. Quotes come only from the node that accepted the unsigned call, because only it holds the unsigned transaction bodies. The X-Flashbots-Signature header is empty: do not require a signature on quotes. Timeout (5 seconds), success response, and no-retry rules are the same as the Callback Receiver Contract.
{
"commitment": {
"id": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"blockTime": 1753000000,
"blockParent": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"subslot": 0,
"expirySubslot": 15,
"txsSize": 340,
"execGasUsed": 145000,
"stateGasUsed": 0,
"maxExecGasLimit": 30000000,
"maxStateGasLimit": 0,
"fees": "210000000000000",
"accesses": {},
"callbackUrl": "0x5f0e...<hash of your callback URL>",
"extBuilders": ["flashbots", "titan"],
"unsignedTxs": ["0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],
"stateDiff": {
"accounts": {
"0x1111111111111111111111111111111111111111": { "balance": "-3120000000000000", "nonce": 1 },
"0x2222222222222222222222222222222222222222": { "balance": "3000000000000000" }
}
}
},
"messages": [{
"from": "0x1111111111111111111111111111111111111111",
"to": "0x2222222222222222222222222222222222222222",
"value": "0x2386f26fc10000",
"gasLimit": "0x5208",
"gasPrice": "0x77359400",
"gasFeeCap": "0x77359400",
"gasTipCap": "0x3b9aca00",
"data": "0x",
"blobGasFeeCap": "0x0"
}]
}
| Field | Meaning |
|---|---|
commitment |
The candidate Commitment. |
commitment.id |
Pass it as unsignedCmtId to ofp_sendSignedBundle for the candidate you choose. |
commitment.unsignedTxs |
Hashes of your own transactions, identical for every candidate of one submission. Use it to correlate quotes; callbackUrl cannot be used for that, it is a hash of the URL. |
commitment.stateDiff.accounts |
Map keyed by address. balance is a signed decimal string; an absent address means no change. Can be null (and logs absent) for a candidate of 400 KB or more, or one that needs more gas than one subslot offers; such a candidate cannot be ranked by balance. |
commitment.extBuilders |
Builders that may receive this candidate's final bundle; the ofp_sendSignedBundle builders is limited to it. Omitted when empty. |
messages |
Resolved execution arguments of every transaction in the candidate, in order (yours, then the searcher's). |
Choose a candidate by comparing stateDiff.accounts[target].balance across candidates, within the block: candidates stop being resolvable when the chain head moves. Zero quotes is normal; use a timeout. A quote can arrive later than its solution appeared, if the searcher's transactions had not reached the node yet. A quote never contains another source's transactions.
MEV-Boost Builder HTTP¶
The relay surface is HTTP, not JSON-RPC. Endpoints are MEV-Boost-compatible and are served by TOOL's in-process relay.
| Endpoint | Method | Request | Response | Use case |
|---|---|---|---|---|
/eth/v1/builder/status |
GET |
None | 200 OK |
Health check. |
/eth/v1/builder/validators |
POST |
Signed validator registrations | 200 OK |
Register validators and optional block seal offset. |
/eth/v1/builder/header/{slot}/{parentHash}/{pubKey} |
GET |
Path params | Versioned signed builder bid, 204, or 404 |
Retrieve a builder bid. |
/eth/v1/builder/blinded_blocks |
POST |
Signed blinded beacon block | Versioned payload response | Submit blinded block and receive payload. |
/eth/v2/builder/blinded_blocks |
POST |
Signed blinded beacon block | 202 Accepted |
Submit blinded block using the v2 path. |
Relay format and parameter notes:
| Item | Description |
|---|---|
Content-Type / Accept |
Supports application/json and application/octet-stream. Octet-stream uses SSZ. |
Eth-Consensus-Version |
Set on successful header and payload responses. |
block_seal_offset_ms |
Optional query parameter on /validators. Must be no more than 4000 ms. |
slot |
Decimal slot number in the header path. |
parentHash |
0x-encoded 32-byte parent hash in the header path. |
pubKey |
0x-encoded 48-byte validator public key in the header path. |
X-Timeout-Ms and Date-Milliseconds |
Optional request headers used to derive a header-request deadline. |
Errors¶
"unsigned" is ofp_sendUnsignedBundle; "signed" is ofp_sendSignedBundle; tool_* is tool_sendBundle and tool_simulateBundle.
| Error | Calls | Meaning |
|---|---|---|
state not ready |
all OFP calls | No TOOL state for blockNumber, or a value that is not accepted |
rate limit exceeded |
unsigned | More than 100 requests per second |
invalid callback URL |
all OFP calls | Missing (unsigned), not an http(s) URL with a host, or over 512 characters |
callback URL provided but callback mode is disabled |
eth_sendBundle |
Callbacks are off on this node |
unsigned bundles are only supported in callback mode |
unsigned | Callbacks are off on this node |
revertingTxHashes not supported for unsigned bundle |
unsigned | The field was sent |
bundle missing txs |
eth_sendBundle, unsigned |
Empty txs |
blob transactions not allowed |
eth_sendBundle, signed |
A blob transaction |
some reverting txs not found in bundle: ... |
eth_sendBundle |
A revertingTxHashes entry is not in txs |
unknown builders: ... |
eth_sendBundle, signed |
None of the names is registered |
unsignedCmtId is required |
signed | Zero or missing unsignedCmtId |
unsigned commitment not found |
signed | The id is wrong (use the quote's id), the block moved on, or the candidate left the pool while the call waited |
signed tx count mismatch with unsigned commitment |
signed | txs is empty or does not line up with the candidate |
some signed txs not found |
signed | The solver's transactions did not arrive before the block was sealed or changed |
unsigned txs not found |
signed | The candidate has no state diff here and this node does not hold your unsigned bodies: call the node that accepted ofp_sendUnsignedBundle |
signed bundle state diff mismatch |
signed | The signed parameters change the outcome: start again from ofp_sendUnsignedBundle |
| a callback delivery error | signed | Your callback endpoint did not acknowledge. The commitment is already stored and gossiped: do not retry |
already known |
signed, tool_sendBundle |
The bundle or solution is already in the network: do not resubmit |
blob transactions are not supported, empty transactions list |
tool_* |
Pre-validation |
commitments with unsigned txs must be first in the list |
tool_* |
An unsigned (quote-flow) base is not the first base |
invalid commitment <hash> expiration |
tool_* |
A base expires earlier than the bundle |
access lists are not compatible and N based txs not found |
tool_* |
A conflicting base's transactions did not arrive within one second, or the block changed meanwhile; try again a moment later |
unsigned commitment requires callback mode and a single callback URL (...) |
tool_* |
An unsigned base merged with a base that has a different callback, or callbacks are off on this node (Rejection Checks) |
commitment has no delivery target (...) |
tool_* |
No usable callback and no common builder on a node that is not a relay merging node (Rejection Checks) |
Implementation Notes¶
| Area | Current behavior |
|---|---|
| Blob transactions | Not supported anywhere: rejected on eth_sendBundle, ofp_sendSignedBundle, tool_sendBundle, and tool_simulateBundle. |
| Native bundle field name | The JSON field for base commitments is basedCommitments. Role guides may use "based" as conceptual shorthand. |
| Commitment transaction hashes | Commitment JSON includes id and execution metadata, but does not include the internal txs hash list. |
| Commitment trimming | A locally built commitment of 400 KB or more, or needing more gas than its subslot offered, is gossiped without logs and with StateDiff-backed stateDiff set to null. |
| Commitment filters | tool_subscribe commitments address filters match storage writes, not every read or account-level access. |
| Callback routing | A callback receives only solutions whose bases all share its callback; any other merge becomes mixed-origin and goes only to the builders all bases allow, or to the external relay on relay merging nodes. Delivery is best effort and runs in the background except for ofp_sendSignedBundle. See Delivery Rules. |