signet_orders/
fee_policy.rs1use crate::{BundleSubmitter, FillSubmitter, OrdersAndFills, TxBuilder};
2use alloy::primitives::Address;
3use alloy::{
4 eips::eip2718::Encodable2718,
5 network::{Ethereum, Network, TransactionBuilder},
6 primitives::Bytes,
7 providers::{fillers::FillerControlFlow, SendableTx},
8 rpc::types::mev::EthSendBundle,
9 transports::{RpcError, TransportErrorKind},
10};
11use futures_util::{stream, StreamExt, TryStreamExt};
12use signet_bundle::SignetEthBundle;
13use signet_constants::SignetSystemConstants;
14#[cfg(doc)]
15use signet_types::SignedFill;
16use tracing::{error, instrument};
17
18#[derive(Debug, thiserror::Error)]
20#[non_exhaustive]
21pub enum FeePolicyError {
22 #[error("no fills provided for submission")]
24 NoFills,
25 #[error("RPC error: {0}")]
27 Rpc(#[source] RpcError<TransportErrorKind>),
28 #[error("transaction missing required properties: {0:?}")]
30 IncompleteTransaction(Vec<(&'static str, Vec<&'static str>)>),
31 #[error("failed to submit bundle: {0}")]
33 Submission(#[source] Box<dyn core::error::Error + Send + Sync>),
34}
35
36impl From<FillerControlFlow> for FeePolicyError {
37 fn from(filler_control_flow: FillerControlFlow) -> Self {
38 match filler_control_flow {
39 FillerControlFlow::Missing(missing) => Self::IncompleteTransaction(missing),
40 FillerControlFlow::Finished | FillerControlFlow::Ready => {
41 error!("fill returned Builder but status is {filler_control_flow:?}");
42 Self::IncompleteTransaction(Vec::new())
43 }
44 }
45 }
46}
47
48#[derive(Debug, Clone)]
58pub struct FeePolicySubmitter<RuP, HostP, B> {
59 ru_provider: RuP,
60 host_provider: HostP,
61 submitter: B,
62 constants: SignetSystemConstants,
63}
64
65impl<RuP, HostP, B> FeePolicySubmitter<RuP, HostP, B> {
66 pub const fn new(
68 ru_provider: RuP,
69 host_provider: HostP,
70 submitter: B,
71 constants: SignetSystemConstants,
72 ) -> Self {
73 Self { ru_provider, host_provider, submitter, constants }
74 }
75
76 pub const fn ru_provider(&self) -> &RuP {
78 &self.ru_provider
79 }
80
81 pub const fn host_provider(&self) -> &HostP {
83 &self.host_provider
84 }
85
86 pub const fn submitter(&self) -> &B {
88 &self.submitter
89 }
90
91 pub const fn constants(&self) -> &SignetSystemConstants {
93 &self.constants
94 }
95}
96
97impl<RuP, HostP, B> FillSubmitter for FeePolicySubmitter<RuP, HostP, B>
98where
99 RuP: TxBuilder<Ethereum>,
100 HostP: TxBuilder<Ethereum>,
101 B: BundleSubmitter + Send + Sync,
102{
103 type Response = B::Response;
104 type Error = FeePolicyError;
105
106 #[instrument(skip_all, fields(order_count = orders.len(), fill_count = fills.len()))]
107 async fn submit_fills(
108 &self,
109 OrdersAndFills { orders, fills, signer_address }: OrdersAndFills,
110 ) -> Result<Self::Response, Self::Error> {
111 if fills.is_empty() {
112 return Err(FeePolicyError::NoFills);
113 }
114
115 let fill_iter = fills
117 .get(&self.constants.ru_chain_id())
118 .map(|fill| fill.to_fill_tx(self.constants.ru_orders()))
119 .into_iter();
120 let order_iter = orders
121 .iter()
122 .map(|order| order.to_initiate_tx(signer_address, self.constants.ru_orders()));
123 let rollup_txs: Vec<Bytes> = stream::iter(fill_iter.chain(order_iter))
124 .then(|tx_request| sign_and_encode_tx(&self.ru_provider, tx_request, signer_address))
125 .try_collect()
126 .await?;
127
128 let host_txs = match fills.get(&self.constants.host_chain_id()) {
130 Some(fill) => {
131 let tx_request = fill.to_fill_tx(self.constants.host_orders());
132 vec![sign_and_encode_tx(&self.host_provider, tx_request, signer_address).await?]
133 }
134 None => vec![],
135 };
136
137 let target_block =
140 self.ru_provider.get_block_number().await.map_err(FeePolicyError::Rpc)? + 1;
141
142 let bundle = SignetEthBundle::new(
143 EthSendBundle { txs: rollup_txs, block_number: target_block, ..Default::default() },
144 host_txs,
145 );
146
147 self.submitter
148 .submit_bundle(bundle)
149 .await
150 .map_err(|error| FeePolicyError::Submission(Box::new(error)))
151 }
152}
153
154#[instrument(skip_all)]
156async fn sign_and_encode_tx<N, P>(
157 provider: &P,
158 mut tx_request: N::TransactionRequest,
159 signer_address: Address,
160) -> Result<Bytes, FeePolicyError>
161where
162 N: Network,
163 P: TxBuilder<N>,
164 N::TxEnvelope: Encodable2718,
165{
166 tx_request = tx_request.with_from(signer_address);
167 let sendable = provider.fill(tx_request).await.map_err(FeePolicyError::Rpc)?;
168
169 let envelope = match sendable {
170 SendableTx::Envelope(envelope) => envelope,
171 SendableTx::Builder(tx) => {
172 return Err(FeePolicyError::from(provider.status(&tx)));
173 }
174 };
175
176 Ok(Bytes::from(envelope.encoded_2718()))
177}