Skip to main content

signet_orders/
filler.rs

1use crate::{FillSubmitter, OrderSource};
2use alloy::{primitives::Address, signers::Signer};
3use chrono::Utc;
4use futures_util::{Stream, StreamExt};
5use signet_constants::SignetSystemConstants;
6use signet_types::{SignedFill, SignedOrder, SigningError, UnsignedFill};
7use std::collections::HashMap;
8use tracing::instrument;
9
10/// Errors returned by [`Filler`].
11#[derive(Debug, thiserror::Error)]
12#[non_exhaustive]
13pub enum FillerError {
14    /// Order source error.
15    #[error("failed to get orders: {0}")]
16    Source(#[source] Box<dyn core::error::Error + Send + Sync>),
17    /// No orders to fill.
18    #[error("no orders to fill")]
19    NoOrders,
20    /// Failed to sign fills for orders.
21    #[error("failed to sign fills: {0}")]
22    Signing(#[from] SigningError),
23    /// Fill submission failed.
24    #[error("failed to submit fills: {0}")]
25    Submission(#[source] Box<dyn core::error::Error + Send + Sync>),
26}
27
28/// Options for configuring the [`Filler`].
29#[derive(Debug, Clone, Copy, Default)]
30pub struct FillerOptions {
31    /// Optional deadline offset in seconds for fills.
32    pub deadline_offset: Option<u64>,
33    /// Optional nonce to use for permit2 signatures.
34    pub nonce: Option<u64>,
35}
36
37impl FillerOptions {
38    /// Create a new [`FillerOptions`] with default values.
39    pub const fn new() -> Self {
40        Self { deadline_offset: None, nonce: None }
41    }
42
43    /// Set the deadline offset.
44    pub const fn with_deadline_offset(mut self, offset: u64) -> Self {
45        self.deadline_offset = Some(offset);
46        self
47    }
48
49    /// Set the nonce.
50    pub const fn with_nonce(mut self, nonce: u64) -> Self {
51        self.nonce = Some(nonce);
52        self
53    }
54}
55
56/// A small struct to ensure the relevant orders remain paired with the fills generated from them
57/// and with the signer's address.
58#[derive(Debug, Clone)]
59pub struct OrdersAndFills {
60    pub(crate) orders: Vec<SignedOrder>,
61    pub(crate) fills: HashMap<u64, SignedFill>,
62    pub(crate) signer_address: Address,
63}
64
65impl OrdersAndFills {
66    /// Get the orders.
67    pub fn orders(&self) -> &[SignedOrder] {
68        &self.orders
69    }
70
71    /// Get the fills.
72    pub const fn fills(&self) -> &HashMap<u64, SignedFill> {
73        &self.fills
74    }
75
76    /// Get the signer address.
77    pub const fn signer_address(&self) -> Address {
78        self.signer_address
79    }
80}
81
82/// Fills orders by fetching from a source, signing fills, and submitting them.
83///
84/// `Filler` is generic over:
85/// - `Sign`: A [`Signer`] for signing fills
86/// - `Source`: An [`OrderSource`] for fetching orders
87/// - `Submit`: A [`FillSubmitter`] for submitting signed fills
88#[derive(Debug, Clone)]
89pub struct Filler<Sign, Source, Submit> {
90    signer: Sign,
91    order_source: Source,
92    submitter: Submit,
93    constants: SignetSystemConstants,
94    options: FillerOptions,
95}
96
97impl<Sign, Source, Submit> Filler<Sign, Source, Submit> {
98    /// Create a new filler instance.
99    pub const fn new(
100        signer: Sign,
101        order_source: Source,
102        submitter: Submit,
103        constants: SignetSystemConstants,
104        options: FillerOptions,
105    ) -> Self {
106        Self { signer, order_source, submitter, constants, options }
107    }
108
109    /// Get a reference to the signer.
110    pub const fn signer(&self) -> &Sign {
111        &self.signer
112    }
113
114    /// Get a reference to the order source.
115    pub const fn order_source(&self) -> &Source {
116        &self.order_source
117    }
118
119    /// Get a reference to the submitter.
120    pub const fn submitter(&self) -> &Submit {
121        &self.submitter
122    }
123
124    /// Get a reference to the system constants.
125    pub const fn constants(&self) -> &SignetSystemConstants {
126        &self.constants
127    }
128
129    /// Get a reference to the filler options.
130    pub const fn options(&self) -> &FillerOptions {
131        &self.options
132    }
133}
134
135impl<Sign, Source, Submit> Filler<Sign, Source, Submit>
136where
137    Source: OrderSource + Send + Sync,
138{
139    /// Query the source for signed orders.
140    pub fn get_orders(
141        &self,
142    ) -> impl Stream<Item = Result<SignedOrder, FillerError>> + Send + use<'_, Sign, Source, Submit>
143    {
144        self.order_source
145            .get_orders()
146            .map(|result| result.map_err(|e| FillerError::Source(Box::new(e))))
147    }
148}
149
150impl<Sign, Source, Submit> Filler<Sign, Source, Submit>
151where
152    Sign: Signer + Send + Sync,
153{
154    /// Sign fills for the given orders.
155    ///
156    /// Returns a map of chain ID to signed fill for each target chain.
157    pub async fn sign_fills(
158        &self,
159        orders: Vec<SignedOrder>,
160    ) -> Result<OrdersAndFills, FillerError> {
161        let mut unsigned_fill = UnsignedFill::new().with_chain(self.constants.clone());
162
163        if let Some(deadline_offset) = self.options.deadline_offset {
164            let deadline = Utc::now().timestamp() as u64 + deadline_offset;
165            unsigned_fill = unsigned_fill.with_deadline(deadline);
166        }
167
168        if let Some(nonce) = self.options.nonce {
169            unsigned_fill = unsigned_fill.with_nonce(nonce);
170        }
171
172        for order in &orders {
173            unsigned_fill = unsigned_fill.fill(order);
174        }
175
176        let fills = unsigned_fill.sign(&self.signer).await?;
177        let signer_address = self.signer.address();
178        Ok(OrdersAndFills { orders, fills, signer_address })
179    }
180}
181
182impl<Sign, Source, Submit> Filler<Sign, Source, Submit>
183where
184    Sign: Signer + Send + Sync,
185    Submit: FillSubmitter + Send + Sync,
186{
187    /// Fill one or more orders.
188    ///
189    /// Signs fills for all orders and submits them via the [`FillSubmitter`].
190    ///
191    /// Returns an error if `orders` is empty, or if signing or submission fails.
192    #[instrument(skip_all, fields(order_count = orders.len()))]
193    pub async fn fill(&self, orders: Vec<SignedOrder>) -> Result<Submit::Response, FillerError> {
194        if orders.is_empty() {
195            return Err(FillerError::NoOrders);
196        }
197
198        let orders_and_fills = self.sign_fills(orders).await?;
199        self.submitter
200            .submit_fills(orders_and_fills)
201            .await
202            .map_err(|error| FillerError::Submission(Box::new(error)))
203    }
204}