Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/content/docs/mev-share.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,24 @@ while (true) {
}
```

### Large Hints

Mainnet hints carrying big `logs` or `calldata` arrays are longer than the default
read buffer. `on` handles those transparently: lines that fit the transfer buffer are
read without allocating, and longer lines are accumulated on the heap up to
`max_line_size`. Both bounds are tunable on the client:

```zig
var client = eth.mev_share.MevShareClient.initMainnet(allocator, auth_key, io);
client.stream_opts = .{
.transfer_buffer_size = 256 * 1024, // lines below this never allocate
.max_line_size = 4 * 1024 * 1024, // error.LineTooLong beyond this
};
```

An event whose data exceeds the parser's fixed 64 KiB data buffer arrives with
`truncated` set; `on` skips those rather than reporting a JSON parse failure.

### Backrun Loop Sketch

A minimal backrunner: watch for hints touching a target pool, then submit a bundle
Expand Down
46 changes: 31 additions & 15 deletions src/mev_share.zig
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,10 @@ pub const MevShareClient = struct {
stream_url: []const u8,
/// HTTP client for the unauthenticated stream/history endpoints.
client: std.http.Client,
/// Read-path sizing for `on`. Raise `transfer_buffer_size` if a stream is
/// known to carry unusually large hints; raise `max_line_size` to allow
/// longer single lines before `error.LineTooLong`.
stream_opts: sse_transport.StreamOpts = .{},

/// Create a client for the given relay and event stream endpoints.
/// `auth_key` is the Flashbots reputation key (not a funded key).
Expand Down Expand Up @@ -426,22 +430,34 @@ pub const MevShareClient = struct {
var response = try req.receiveHead(&redirect_buf);
if (response.head.status != .ok) return error.BadStatus;

var transfer_buf: [8192]u8 = undefined;
const reader = response.reader(&transfer_buf);

while (true) {
const line_with_nl = reader.takeDelimiterInclusive('\n') catch |err| switch (err) {
error.EndOfStream => return, // normal close
else => return err,
};
const line = line_with_nl[0 .. line_with_nl.len - 1];

if (parser.feedLine(line)) |evt| {
const event = parseEventData(self.allocator, evt.data) catch continue;
defer freePendingEvent(self.allocator, &event);
callback(event);
const transfer_buf = try self.allocator.alloc(u8, self.stream_opts.transfer_buffer_size);
defer self.allocator.free(transfer_buf);
const reader = response.reader(transfer_buf);

const Ctx = struct {
client: *MevShareClient,
cb: *const fn (event: PendingEvent) void,

fn onEvent(ctx: *@This(), evt: sse_transport.SseEvent) anyerror!void {
// The parser's data buffer is fixed; a payload larger than it
// arrives as a prefix that would fail to parse as JSON. Skip it
// explicitly rather than reporting a bogus parse error.
if (evt.truncated) return;
const event = parseEventData(ctx.client.allocator, evt.data) catch return;
defer freePendingEvent(ctx.client.allocator, &event);
ctx.cb(event);
}
}
};
var ctx = Ctx{ .client = self, .cb = callback };

try sse_transport.pumpEvents(
self.allocator,
reader,
&parser,
self.stream_opts.max_line_size,
&ctx,
Ctx.onEvent,
);
}

/// Fetch historical event stream data (GET /api/v1/history). Caller
Expand Down
Loading
Loading