Add Flashbots MEV bundle submission support#32
Conversation
Implement Flashbots relay client with eth_sendBundle, mev_sendBundle, eth_callBundle, and eth_cancelBundle. Includes EIP-191 auth signing (X-Flashbots-Signature header) and Bundle convenience builder.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds a new Zig Flashbots/MEV relay client with bundle builder, Relay type (sendBundle, callBundle, mevSendBundle, cancelBundle), Flashbots auth header signing, JSON-RPC parameter builders/parsers, public bundle/result types, tests, JSON-RPC method constants, and a root export. Changes
Sequence DiagramsequenceDiagram
participant Client
participant Relay
participant Signer as Auth_Signer
participant HTTP as HTTP_Client
participant Server as Relay_Server
Client->>Relay: sendBundle(opts)
Relay->>Relay: buildSendBundleParams(opts)
Relay->>Relay: serialize JSON body
Relay->>Signer: computeAuthHeader(body)
Signer->>Signer: keccak256(body) and secp256k1 sign
Signer-->>Relay: X-Flashbots-Signature header
Relay->>HTTP: POST JSON-RPC with header
HTTP->>Server: transmit request
Server-->>HTTP: response
HTTP-->>Relay: response payload
Relay->>Relay: checkRpcError & parse result
Relay-->>Client: SendBundleResult / error
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/flashbots.zig (1)
108-133: Consider documenting the ownership model for transaction data.The
Bundlestores slices ([]const u8) to transaction data without copying. This means callers must ensure the transaction data remains valid for the lifetime of theBundle. Consider adding a doc comment clarifying this, or alternatively provide a version that copies data if that's a common use case.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/flashbots.zig` around lines 108 - 133, The Bundle struct currently stores slices in txs without copying, so callers must keep backing memory alive; update the code by adding a clear doc comment on Bundle (and on addTransaction/transactions) that explains the ownership model and lifetime requirement of []const u8 entries, and optionally implement a copying alternative (e.g., addTransactionCopy that allocates via allocator, copies signed_tx into owned storage and appends that slice) so users who expect ownership can use it; reference symbols: Bundle, txs, addTransaction, transactions and the allocator used for any copying routine.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/flashbots.zig`:
- Around line 564-576: The JSON parsing functions currently use
std.heap.page_allocator directly (e.g., checkRpcError), which breaks the
project's allocator threading and harms test/production memory tracking; change
these functions (checkRpcError, parseBundleHashResult, parseCallBundleResult) to
accept an allocator parameter (e.g., allocator: *std.mem.Allocator) and use that
allocator when calling std.json.parseFromSlice, then update all callers in Relay
to pass self.allocator so allocations are tracked consistently.
---
Nitpick comments:
In `@src/flashbots.zig`:
- Around line 108-133: The Bundle struct currently stores slices in txs without
copying, so callers must keep backing memory alive; update the code by adding a
clear doc comment on Bundle (and on addTransaction/transactions) that explains
the ownership model and lifetime requirement of []const u8 entries, and
optionally implement a copying alternative (e.g., addTransactionCopy that
allocates via allocator, copies signed_tx into owned storage and appends that
slice) so users who expect ownership can use it; reference symbols: Bundle, txs,
addTransaction, transactions and the allocator used for any copying routine.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ec2ce26a-e8c1-4130-b2f9-59a4a5de26fa
📒 Files selected for processing (3)
src/flashbots.zigsrc/json_rpc.zigsrc/root.zig
…ship docs Use caller's allocator instead of page_allocator in response parsing functions for consistent memory tracking. Add doc comments clarifying Bundle's borrowing semantics for transaction slices.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/flashbots.zig`:
- Around line 36-45: Change the CallBundleOpts.state_block_number from ?u64 to
an optional string type so callers can pass hex numbers or tags like "latest":
update the field signature in CallBundleOpts (currently state_block_number:
?u64) to state_block_number: ?[]const u8 (or to a small discriminated union type
e.g., BlockTag = union(enum){Number: u64, Tag: []const u8} and then
state_block_number: ?BlockTag) and then adjust any code that serializes or reads
state_block_number (serialization/JSON building for the Flashbots API) to accept
and forward either the string tag or a hex-encoded number appropriately. Ensure
default null behavior remains unchanged.
- Around line 631-634: The parsing function parseHexOrIntU256 only handles hex
strings and must accept base-10 decimal strings used by Flashbots for fields
like bundleGasPrice/coinbaseDiff/ethSentToCoinbase/gasFees; modify
parseHexOrIntU256 so its string branch checks if the string starts with "0x" and
parses as hex otherwise parses as a decimal integer (using the uint256 parsing
utility or equivalent), and ensure the callers (variables bundle_gas_price,
coinbase_diff, eth_sent, gas_fees in src/flashbots.zig) continue to use this
function; also update the test case to supply decimal-format strings (not
0x-prefixed hex) to match the documented Flashbots response.
…ings - Change state_block_number from ?u64 to ?BlockParam so callers can pass block tags like "latest" per the Flashbots spec - Add decimal string parsing for bundleGasPrice/coinbaseDiff/etc fields which Flashbots returns as decimal strings, not hex
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/flashbots.zig`:
- Around line 389-392: The code is appending caller-provided strings
(replacement_uuid, privacy.hints, privacy.builders) directly into the JSON
buffer via buf.appendSlice/append which can break JSON or allow injection;
change the code that handles opts.replacement_uuid in the function that builds
the RPC body and the blocks that append privacy.hints and privacy.builders to
first JSON-escape the string (properly escape quotes, backslashes, and control
chars) and then append the escaped result instead of the raw input, reusing or
adding a helper like jsonEscape(allocator, src) and replace calls around
buf.appendSlice/append accordingly, and add a regression test that supplies
values with quotes/backslashes/control characters to verify the output is valid
JSON and fields are preserved.
- Around line 660-669: The parseDecimalU256 function can miss some overflows
because checking result < prev after result = result *% 10 +% digit is
insufficient; instead, before doing the multiply/add, check overflow by
comparing result against the u256 max thresholds: compute max_div_10 = u256.max
/ 10 and max_rem = u256.max % 10, then if result > max_div_10 or (result ==
max_div_10 and digit > max_rem) return error.InvalidResponse; only then perform
result = result *% 10 +% digit. This replaces the post-wrap check and uses the
u256 overflow bounds to prevent truncated/wrapped values in parseDecimalU256.
- Add appendJsonEscaped helper for RFC 8259 string escaping (quotes, backslashes, control chars) and use it for replacement_uuid, privacy.hints, and privacy.builders - Fix parseDecimalU256 to use pre-multiply overflow bounds check (maxInt(u256)/10) instead of post-wrap comparison
Closes #13
Summary
flashbots.Relayclient with Flashbots auth signing (X-Flashbots-Signatureheader using EIP-191)eth_sendBundle(Flashbots v1),mev_sendBundle(MEV-Share),eth_callBundle(simulation), andeth_cancelBundleflashbots.Bundleconvenience builder for collecting signed transactionsAPI
References
Test plan
zig build test)Summary by CodeRabbit