1use crate::agg::AggregateOrders;
2use crate::signing::{permit_signing_info, SignedPermitError, SigningError};
3use crate::SignedOrder;
4use alloy::{
5 network::TransactionBuilder, primitives::Address, rpc::types::TransactionRequest,
6 signers::Signer, sol_types::SolCall,
7};
8use chrono::Utc;
9use serde::{Deserialize, Serialize};
10use signet_constants::SignetSystemConstants;
11use signet_zenith::RollupOrders::Order;
12use signet_zenith::{
13 BundleHelper::{FillPermit2, IOrders},
14 RollupOrders::{fillPermit2Call, Output, Permit2Batch, TokenPermissions},
15};
16use std::{borrow::Cow, collections::HashMap};
17
18#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
41pub struct SignedFill {
42 #[serde(flatten)]
44 pub permit: Permit2Batch,
45 pub outputs: Vec<Output>,
47}
48
49impl SignedFill {
50 pub const fn new(permit: Permit2Batch, outputs: Vec<Output>) -> Self {
52 Self { permit, outputs }
53 }
54
55 pub fn validate(&self, timestamp: u64) -> Result<(), SignedPermitError> {
62 let deadline = self.permit.permit.deadline.saturating_to::<u64>();
63 if timestamp > deadline {
64 return Err(SignedPermitError::DeadlinePassed { current: timestamp, deadline });
65 }
66
67 if self.outputs.len() != self.permit.permit.permitted.len() {
69 return Err(SignedPermitError::PermitMismatch);
70 }
71
72 for (output, permit) in self.outputs.iter().zip(self.permit.permit.permitted.iter()) {
73 if output.token != permit.token {
75 return Err(SignedPermitError::PermitMismatch);
76 }
77 if output.amount != permit.amount {
79 return Err(SignedPermitError::PermitMismatch);
80 }
81 }
82
83 Ok(())
84 }
85
86 pub fn to_fill_tx(&self, order_contract: Address) -> TransactionRequest {
88 let fill_data =
90 fillPermit2Call { outputs: self.outputs.clone(), permit2: self.permit.clone() }
91 .abi_encode();
92
93 TransactionRequest::default().with_input(fill_data).with_to(order_contract)
95 }
96}
97
98impl From<SignedFill> for FillPermit2 {
99 fn from(fill: SignedFill) -> Self {
100 FillPermit2 {
101 permit2: fill.permit.into(),
102 outputs: fill.outputs.into_iter().map(IOrders::Output::from).collect(),
103 }
104 }
105}
106
107impl From<&SignedFill> for FillPermit2 {
108 fn from(fill: &SignedFill) -> Self {
109 FillPermit2 {
110 permit2: (&fill.permit).into(),
111 outputs: fill.outputs.iter().map(IOrders::Output::from).collect(),
112 }
113 }
114}
115
116#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
119pub struct UnsignedFill<'a> {
120 ru_chain_id: Option<u64>,
123 orders: Cow<'a, AggregateOrders>,
126 deadline: Option<u64>,
128 nonce: Option<u64>,
130 target_chains: HashMap<u64, Address>,
132}
133
134impl Default for UnsignedFill<'_> {
135 fn default() -> Self {
136 Self {
137 ru_chain_id: None,
138 orders: Cow::Owned(AggregateOrders::default()),
139 deadline: None,
140 nonce: None,
141 target_chains: HashMap::new(),
142 }
143 }
144}
145
146impl<'a> From<&'a AggregateOrders> for UnsignedFill<'a> {
147 fn from(orders: &'a AggregateOrders) -> Self {
148 Self {
149 ru_chain_id: None,
150 orders: Cow::Borrowed(orders),
151 deadline: None,
152 nonce: None,
153 target_chains: HashMap::new(),
154 }
155 }
156}
157
158impl From<Order> for UnsignedFill<'static> {
159 fn from(order: Order) -> Self {
160 let mut aggregate_orders = AggregateOrders::default();
161 aggregate_orders.ingest(&order);
162 Self {
163 ru_chain_id: None,
164 orders: Cow::Owned(aggregate_orders),
165 deadline: None,
166 nonce: None,
167 target_chains: HashMap::new(),
168 }
169 }
170}
171
172impl<'a> UnsignedFill<'a> {
173 pub fn new() -> Self {
175 Self {
176 ru_chain_id: None,
177 orders: Cow::Owned(AggregateOrders::default()),
178 deadline: None,
179 nonce: None,
180 target_chains: HashMap::new(),
181 }
182 }
183
184 pub fn fill_raw(mut self, order: &Order) -> UnsignedFill<'static> {
186 self.orders.to_mut().ingest(order);
187 UnsignedFill {
188 ru_chain_id: self.ru_chain_id,
189 orders: Cow::Owned(self.orders.into_owned()),
190 deadline: self.deadline,
191 nonce: self.nonce,
192 target_chains: self.target_chains,
193 }
194 }
195
196 pub fn fill(mut self, order: &SignedOrder) -> UnsignedFill<'static> {
198 self.orders.to_mut().ingest_signed(order);
199 UnsignedFill {
200 ru_chain_id: self.ru_chain_id,
201 orders: Cow::Owned(self.orders.into_owned()),
202 deadline: self.deadline,
203 nonce: self.nonce,
204 target_chains: self.target_chains,
205 }
206 }
207
208 pub fn with_nonce(self, nonce: u64) -> Self {
210 Self { nonce: Some(nonce), ..self }
211 }
212
213 pub fn with_deadline(self, deadline: u64) -> Self {
215 Self { deadline: Some(deadline), ..self }
216 }
217
218 #[deprecated(since = "0.14.1", note = "Use `with_chain` instead.")]
220 pub fn with_ru_chain_id(self, ru_chain_id: u64) -> Self {
221 Self { ru_chain_id: Some(ru_chain_id), ..self }
222 }
223
224 pub fn with_chain(mut self, constants: SignetSystemConstants) -> Self {
228 self.target_chains.insert(constants.ru_chain_id(), constants.ru_orders());
229 self.target_chains.insert(constants.host_chain_id(), constants.host_orders());
230 Self { ru_chain_id: Some(constants.ru_chain_id()), ..self }
231 }
232
233 pub async fn sign<S: Signer>(
236 &self,
237 signer: &S,
238 ) -> Result<HashMap<u64, SignedFill>, SigningError> {
239 let mut fills = HashMap::new();
240
241 for target_chain_id in self.orders.target_chain_ids() {
243 let signed_fill = self.sign_for(target_chain_id, signer).await?;
244 fills.insert(target_chain_id, signed_fill);
245 }
246
247 Ok(fills)
249 }
250
251 pub async fn sign_for<S: Signer>(
257 &self,
258 target_chain_id: u64,
259 signer: &S,
260 ) -> Result<SignedFill, SigningError> {
261 let now = Utc::now();
262 let nonce = self.nonce.unwrap_or(now.timestamp_micros() as u64);
264 let deadline = self.deadline.unwrap_or(now.timestamp() as u64 + 12);
266
267 let target_order_address = self
269 .target_chains
270 .get(&target_chain_id)
271 .ok_or(SigningError::MissingOrderContract(target_chain_id))?;
272
273 let ru_chain_id = self.ru_chain_id.ok_or(SigningError::MissingChainId)?;
275
276 let outputs = self.orders.outputs_for(target_chain_id, ru_chain_id);
278 let permitted: Vec<TokenPermissions> = outputs.iter().map(Into::into).collect();
280
281 let permit = permit_signing_info(
283 outputs,
284 permitted,
285 deadline,
286 nonce,
287 target_chain_id,
288 *target_order_address,
289 );
290
291 let signature = signer.sign_hash(&permit.signing_hash).await?;
293
294 Ok(SignedFill {
296 permit: Permit2Batch {
297 permit: permit.permit,
298 owner: signer.address(),
299 signature: signature.as_bytes().into(),
300 },
301 outputs: permit.outputs,
302 })
303 }
304}