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