arb_rpc/
arbtimeboost.rs

1//! `arbtimeboost_*` and `arbtimeboostauctioneer_*` RPC namespaces.
2//!
3//! Arbitrum Timeboost is an optional sequencer-side priority-lane
4//! feature: an auctioneer resolves bids for express-lane slots, and
5//! winning txs are submitted through `arbtimeboost_*` with round +
6//! sequence metadata. The feature is off by default.
7//!
8//! When timeboost isn't configured on the node, both namespaces
9//! return "not enabled" errors (matching Nitro's behavior when the
10//! `txPublisher` has no timeboost backend set).
11
12use alloy_primitives::{Address, Bytes, B256, U256};
13use jsonrpsee::{
14    core::RpcResult,
15    proc_macros::rpc,
16    types::{error::INTERNAL_ERROR_CODE, ErrorObject},
17};
18use serde::{Deserialize, Serialize};
19
20fn not_enabled(feature: &str) -> ErrorObject<'static> {
21    ErrorObject::owned(
22        INTERNAL_ERROR_CODE,
23        format!("{feature} is not enabled on this node"),
24        None::<()>,
25    )
26}
27
28/// Wire format of an express-lane submission. Matches Nitro's
29/// `timeboost.JsonExpressLaneSubmission` (see
30/// `timeboost/express_lane_service.go`). Fields are accepted as-is
31/// and passed through to the transaction publisher — we don't
32/// validate the signature ourselves.
33#[derive(Debug, Clone, Serialize, Deserialize)]
34#[serde(rename_all = "camelCase")]
35pub struct ExpressLaneSubmission {
36    pub chain_id: U256,
37    pub round: u64,
38    pub auction_contract_address: Address,
39    pub sequence: u64,
40    pub transaction: Bytes,
41    pub signature: Bytes,
42}
43
44/// `arbtimeboost` RPC namespace — sequencer-facing submission API.
45#[rpc(server, namespace = "arbtimeboost")]
46pub trait ArbTimeboostApi {
47    /// Submit a signed express-lane transaction for inclusion in the
48    /// current round.
49    #[method(name = "sendExpressLaneTransaction")]
50    async fn send_express_lane_transaction(&self, msg: ExpressLaneSubmission) -> RpcResult<()>;
51}
52
53/// `arbtimeboostauctioneer` RPC namespace — auctioneer-facing
54/// resolution API.
55#[rpc(server, namespace = "arbtimeboostauctioneer")]
56pub trait ArbTimeboostAuctioneerApi {
57    /// Submit the winning auction resolution tx. The transaction
58    /// encodes round + winner + bids. Only the configured auctioneer
59    /// may call this.
60    #[method(name = "submitAuctionResolutionTransaction")]
61    async fn submit_auction_resolution_transaction(&self, raw_tx: Bytes) -> RpcResult<B256>;
62}
63
64/// Configuration for the timeboost namespaces.
65#[derive(Debug, Clone, Default)]
66pub struct ArbTimeboostConfig {
67    /// When false, all methods return "not enabled".
68    pub express_lane_enabled: bool,
69    /// When false, the auctioneer method returns "not enabled".
70    pub auctioneer_enabled: bool,
71}
72
73/// Handler for both `arbtimeboost_*` and `arbtimeboostauctioneer_*`.
74#[derive(Debug, Clone)]
75pub struct ArbTimeboostHandler {
76    config: ArbTimeboostConfig,
77}
78
79impl ArbTimeboostHandler {
80    pub fn new(config: ArbTimeboostConfig) -> Self {
81        Self { config }
82    }
83}
84
85#[async_trait::async_trait]
86impl ArbTimeboostApiServer for ArbTimeboostHandler {
87    async fn send_express_lane_transaction(&self, _msg: ExpressLaneSubmission) -> RpcResult<()> {
88        if !self.config.express_lane_enabled {
89            return Err(not_enabled("timeboost express lane"));
90        }
91        // TODO: hand off to a timeboost-aware tx publisher.
92        Err(not_enabled("timeboost express lane publisher"))
93    }
94}
95
96#[async_trait::async_trait]
97impl ArbTimeboostAuctioneerApiServer for ArbTimeboostHandler {
98    async fn submit_auction_resolution_transaction(&self, _raw_tx: Bytes) -> RpcResult<B256> {
99        if !self.config.auctioneer_enabled {
100            return Err(not_enabled("timeboost auctioneer"));
101        }
102        // TODO: validate caller == configured auctioneer, decode tx,
103        // forward to the auction-resolution publisher.
104        Err(not_enabled("timeboost auctioneer publisher"))
105    }
106}