Last updated

Using P2C

How to receive the post-pack stream and act on it.

Payment paths (use one or both):

  • PSA — selling / reselling pre-conf. You pay an epoch fee based on txn Updates.
  • MCA — backrun. Share 65% of backrun profit; pre-conf is free for backrun-only (no PSA). If you also sell, fund a PSA too.

The validator connects out to your gRPC server and pushes transactions at the point of no return. Your reply bundle still needs a tip for scheduling priority — see Minimum Tip Requirements.

This page covers Relayer gRPC services, packet layout, reply bundles, and validator Admin RPC for endpoints.

Audience: Searchers and TIN partners consuming the stream; validator operators managing endpoints.

Related: Post-pack overview · PSA · MCA · Setup guide · Tips


1. Setup: Required gRPC services

Run a gRPC server and share that URL with the Rakurai team — see Setup guide. Keep a funded PSA if you resell, and/or an active MCA if you backrun — otherwise the matching stream stops.

Sample URL to share:

https://sample-server.com:20000
The validator connects to you

You run a server and listen. The validator dials your URL. Bind so validators can reach you (public IP or hostname). Both http:// and https:// work.

1.1. Services your server must expose

Protos: auth.proto, block_engine.proto.

ServicePurpose
auth.AuthServiceAuth with role RELAYER (challenge signed by the validator identity, then a bearer token)
block_engine.BlockEngineValidatorGetBlockEngineEndpoints so validators can pick the best region. On a P2C-only host, SubscribePackets / SubscribeBundles may return UNIMPLEMENTED
block_engine.BlockEngineRelayerThe transaction streams in the table below

1.2. Relayer streams — which RPC to implement

Pick your use case first. That decides which streams you need.

Your use caseConfig flagsStreams you need
Resell / pre-conf (PSA)resellStartExpiringPacketStream (required)
Backrun (MCA)mevStartExpiringMevPacketStream (required)
Backrun + TPUmev + enable_tpu_p2c_updateMev stream plus optional StartExpiringTpuPacketStream
Resell + Backrunresell + mevPacketStream and MevPacketStream (optional TPU on Mev path)
Relayer RPCWhen requiredWhat you receiveFront-run protected?
StartExpiringPacketStreamRequired if resell is onPost-pack confirmations at the point of no return (resell / no tip-boost hash). Scheduler only — no TPU on this path.Yes
StartExpiringMevPacketStreamRequired if mev is onPost-pack confirmations at the point of no return (Mev / tip-boost-eligible hash). Reply with a backrun bundle (original packet unchanged + your txs).Yes
StartExpiringTpuPacketStreamOptional — only with mev + enable_tpu_p2c_updateTransactions the validator sees on TPU while it is not the leader. Same message shape as the other streams.No — not yet at the point of no return
StartP2cUpdateCountStreamOptionalOnce per slot: how many transactions were sent on the post-pack confirmation and TPU streams (uuid, slot, counts).—

All streams send the same PacketBatchUpdate message type. Tell them apart by which gRPC method delivered the message — there is no source field on the packet. Details: §2.2 · Setup guide §8.1.

Missing optional streams is fine; missing a required stream is not

If TPU or count RPCs are missing (or return UNIMPLEMENTED), newer validators keep the post-pack confirmation stream(s) and continue. If resell is on and StartExpiringPacketStream is missing — or mev is on and StartExpiringMevPacketStream is missing — the connection fails. Sample toggles: p2c_server --disable-tpu-packet-stream / --disable-p2c-update-count (both optional streams are on by default).

enable_tpu_p2c_update is set per P2C URL (on-chain / Admin). When it is false, the validator does not open the TPU stream even if your server implements it.

1.3. Reusing your block-engine URL

If one host handles both bundles (you → validator) and P2C (validator → you), register that single URL for both paths. The server must expose the Relayer role and the Validator role — otherwise bundles may work while P2C never starts.

Full details: Setup guide — same URL for bundles and P2C. Or register a separate P2C URL that implements Relayer + GetBlockEngineEndpoints (and leaves bundle subscribe RPCs unimplemented).

Once your endpoint is on-chain, opted-in Rakurai validators push transactions to you. Settle with PSA for resell, MCA for backrun, or both — see Post-pack — access and payments.


2. Transaction / packet structure

packet.proto

For each transaction, the validator sends one PacketBatchUpdate with msg = batches on either the scheduler or TPU Relayer stream (same shape; which stream you accepted is the source):

PacketBatchUpdate
  └── batches: ExpiringPacketBatch
        ├── header.ts
        ├── batch: PacketBatch
        │     └── packets[]: Packet
        │           ├── data    ← raw Solana wire transaction bytes
        │           └── meta: Meta
        │                 ├── size
        │                 ├── addr
        │                 ├── port
        │                 ├── flags: PacketFlags
        │                 └── sender_stake
        └── expiry_ms = <working-bank slot as u32>   ← field name is historical; value is a slot, not a timeout in ms
`expiry_ms` holds the slot, not a timeout

The field is named expiry_ms for Jito wire compatibility. On the P2C path it carries the validator’s working-bank slot as a u32 — the leader slot that produced the update — not a millisecond expiry.

2.1. Per-slot counts (StartP2cUpdateCountStream)

When enabled, the validator pushes one P2cUpdateCount once per slot (and flushes on disconnect). Each message is a count of transactions successfully sent through the P2C update streams for that slot:

FieldMeaning
uuidPost-pack endpoint UUID
slotSlot these counts belong to
scheduler_countTransactions successfully sent on the post-pack confirmation stream (StartExpiringPacketStream or StartExpiringMevPacketStream)
tpu_countTransactions successfully sent on StartExpiringTpuPacketStream
total_countscheduler_count + tpu_count
p2c_tpu_enabledWhether this endpoint has TPU updates enabled in config

2.2. meta fields

Meta and PacketFlags are the packet gRPC types in packet.proto. Decode the transaction from data; treat meta as P2C fills it, not as a TPU/relayer packet.

FieldTypeWhat the proto isWhat P2C sends
sizeuint64Byte length of datadata.len()
addrstringSource address on a normal packetNot an IP. P2C copies a per-transaction string here. On the TPU path that string is the validator identity signature over the transaction signature (proof this leader emitted the update). Do not parse it as a socket address.
portuint32Source portAlways 0
flagsPacketFlagsPer-packet flags (see below)Omitted (None) — treat as unset / all false
sender_stakeuint64Stake of the sending nodeAlways 0

PacketFlags (present in the proto, not set on P2C):

FlagMeaning on a normal packet
discardPacket should be dropped
forwardedAlready forwarded
repairRepair traffic
simple_vote_txSimple vote
tracer_packetTracer
from_staked_nodeCame from a staked node

Do not use sender_stake, port, or flags to decide whether to backrun. When you reply, put the original Packet unchanged — data and meta — as the first packet(s) of the bundle.

Decode in Rust:

use solana_transaction::versioned::VersionedTransaction;

let txn: VersionedTransaction = bincode::deserialize(&packet.data)?;

packet.data is the raw Solana wire transaction. Decode it, inspect accounts and instructions, then decide whether to backrun.


3. Send a bundle

Receive the post-pack confirmation Packet, then send back a bundle (SendBundle through block-engine).

When building the bundle, include:

  1. The original post-pack confirmation packet(s) unchanged (the same Packet you received)
  2. Any additional transactions (e.g. backrun / arbitrage)
  3. A transaction or instruction with a tip to one of Rakurai’s tip accounts
Minimum Tip Requirements
  1. P2C MEV: 0.001 SOL — Minimum tip to receive the 20% virtual priority boost on P2C MEV bundles. A lower tip may result in reduced QoS and no priority boost.
  2. Non-P2C: 0.0005 SOL — Recommended tip for regular non-P2C bundles to get the best QoS. Lower tip may lead to potentially lower QoS.
  3. Below 100K lamports: Bundles with low tips are processed on a best-effort basis.

A Bundle is a header plus a list of Packets — the same packet shape as the stream:

Bundle
  ├── header
  └── packets[]: Packet     ← original post-pack packet(s) first, then your txs

4. Commands

Searcher / TIN partner (on-chain money paths):

WhatCLI
Fund and inspect PSArakurai-p2c
Report and settle MCArakurai-revshare

Validator operators use Admin RPC below to inspect which post-pack endpoints are active and to blocklist services. Registering a new endpoint (adding your gRPC URL on-chain) is not done here — share your endpoint with the Rakurai team via Setup guide.

Admin IPC is request/response: keep the socket open briefly so socat can read the reply before stdin closes.

4.1. getPostPackConfirmationConfig

Returns the live status maintained by the scheduler (admin + on-chain merge, blocklist, and what is actually connected).

FieldDescription
onchain_entriesEntries loaded from the on-chain PDA (url, uuid, enable_tpu_p2c_update)
blocklisted_uuidsEndpoint UUIDs blocked via setPostPackConfirmationUuidBlocklist
blocklisted_entriesFull merged entries whose uuid is blocklisted
active_entriesMerged admin + on-chain (admin wins on same URL), excluding blocklisted UUIDs — these are the endpoints receiving scheduler updates (and TPU when enable_tpu_p2c_update is true)
(echo '{"jsonrpc":"2.0","id":1,"method":"getPostPackConfirmationConfig","params":[]}'; sleep 1) \
  | socat - UNIX-CONNECT:admin.rpc | jq

Example response:

{
  "admin_entries": [
    {"url":"http://127.0.0.1:20000","uuid":"PostPackConfig2","enable_tpu_p2c_update":true},
    {"url":"http://127.0.0.1:10000","uuid":"PostPackConfig1","enable_tpu_p2c_update":false}
  ],
  "onchain_entries": [],
  "blocklisted_uuids": ["PostPackConfig1"],
  "blocklisted_entries": [
    {"url":"http://127.0.0.1:10000","uuid":"PostPackConfig1","enable_tpu_p2c_update":false}
  ],
  "active_entries": [
    {"url":"http://127.0.0.1:20000","uuid":"PostPackConfig2","enable_tpu_p2c_update":true}
  ]
}

4.2. setPostPackConfirmationUuidBlocklist

Blocklists post-pack confirmation endpoints by UUID. Each call replaces the full blocklist. Pass an empty array to clear.

Blocklisted UUIDs are removed from active_entries on the next scheduler config sync. If a blocklisted endpoint already has an open gRPC connection, it is torn down immediately on sync; other endpoints stay connected.

Example — block one endpoint by UUID:

(echo '{"jsonrpc":"2.0","id":1,"method":"setPostPackConfirmationUuidBlocklist","params":[["PostPackConfig1"]]}'; sleep 1) \
  | socat - UNIX-CONNECT:admin.rpc

Example — clear blocklist (reconnect blocklisted endpoints on next sync):

(echo '{"jsonrpc":"2.0","id":1,"method":"setPostPackConfirmationUuidBlocklist","params":[[]]}'; sleep 1) \
  | socat - UNIX-CONNECT:admin.rpc
Clearing the blocklist

setPostPackConfirmationUuidBlocklist takes one parameter: an array of UUIDs. To clear the blocklist you must pass an empty array inside the parameter list — params:[[]]. Writing params:[] omits the parameter entirely and the blocklist is not cleared.


  • Tips — tip for reply bundles and virtual priority
  • PSA · MCA
  • Setup guide — Validator gRPC and discovery