1use crate::signing::{permit_signing_info, SignedPermitError, SigningError};
2use alloy::{
3 network::TransactionBuilder,
4 primitives::{keccak256, Address, Bytes, B256, U256},
5 rpc::types::TransactionRequest,
6 signers::Signer,
7 sol_types::{SolCall, SolValue},
8};
9use chrono::Utc;
10use serde::{Deserialize, Serialize};
11use signet_constants::SignetSystemConstants;
12use signet_zenith::RollupOrders::{
13 initiatePermit2Call, Input, Order, Output, Permit2Batch, TokenPermissions,
14};
15use std::{borrow::Cow, sync::OnceLock};
16
17#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
31pub struct SignedOrder {
32 #[serde(flatten)]
34 permit: Permit2Batch,
35 outputs: Vec<Output>,
37
38 #[serde(skip)]
39 order_hash: OnceLock<B256>,
40 #[serde(skip)]
41 order_hash_pre_image: OnceLock<Bytes>,
42}
43
44impl SignedOrder {
45 pub const fn new(permit: Permit2Batch, outputs: Vec<Output>) -> Self {
47 Self { permit, outputs, order_hash: OnceLock::new(), order_hash_pre_image: OnceLock::new() }
48 }
49
50 pub const fn permit(&self) -> &Permit2Batch {
52 &self.permit
53 }
54
55 pub fn outputs(&self) -> &[Output] {
57 &self.outputs
58 }
59
60 pub fn into_parts(self) -> (Permit2Batch, Vec<Output>) {
62 (self.permit, self.outputs)
63 }
64
65 pub fn validate(&self, timestamp: u64) -> Result<(), SignedPermitError> {
70 let deadline = self.permit.permit.deadline.saturating_to::<u64>();
71 if timestamp > deadline {
72 return Err(SignedPermitError::DeadlinePassed { current: timestamp, deadline });
73 }
74
75 Ok(())
76 }
77
78 pub fn to_initiate_tx(
80 &self,
81 filler_token_recipient: Address,
82 order_contract: Address,
83 ) -> TransactionRequest {
84 let initiate_data = initiatePermit2Call {
86 tokenRecipient: filler_token_recipient,
87 outputs: self.outputs.clone(),
88 permit2: self.permit.clone(),
89 }
90 .abi_encode();
91
92 TransactionRequest::default().with_input(initiate_data).with_to(order_contract)
94 }
95
96 pub fn order_hash(&self) -> &B256 {
108 self.order_hash.get_or_init(|| keccak256(self.order_hash_pre_image()))
109 }
110
111 #[doc(hidden)]
115 pub fn order_hash_pre_image(&self) -> &Bytes {
116 self.order_hash_pre_image.get_or_init(|| self.compute_order_hash_pre_image())
117 }
118
119 #[doc(hidden)]
121 fn compute_order_hash_pre_image(&self) -> Bytes {
122 let mut buf = Vec::with_capacity(128);
124
125 buf.extend_from_slice(keccak256(self.permit.permit.abi_encode()).as_slice());
126 buf.extend_from_slice(keccak256(self.permit.owner.abi_encode()).as_slice());
127 buf.extend_from_slice(keccak256(self.outputs.abi_encode()).as_slice());
128
129 let signature =
131 alloy::primitives::Signature::from_raw(&self.permit.signature).unwrap().normalized_s();
132 buf.extend_from_slice(keccak256(signature.as_bytes()).as_slice());
133
134 buf.into()
135 }
136}
137
138#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Default)]
144pub struct UnsignedOrder<'a> {
145 order: Cow<'a, Order>,
146 nonce: Option<u64>,
147 rollup_chain_id: Option<u64>,
148 rollup_order_address: Option<Address>,
149}
150
151impl<'a> From<&'a Order> for UnsignedOrder<'a> {
152 fn from(order: &'a Order) -> Self {
153 Self { order: Cow::Borrowed(order), ..Default::default() }
154 }
155}
156
157impl<'a> UnsignedOrder<'a> {
158 pub fn new() -> Self {
160 Self {
161 order: Cow::Owned(Order::default()),
162 nonce: None,
163 rollup_chain_id: None,
164 rollup_order_address: None,
165 }
166 }
167
168 pub fn inputs(&self) -> &[Input] {
170 self.order.inputs()
171 }
172
173 pub fn with_raw_input(self, input: Input) -> UnsignedOrder<'static> {
175 let order = self.order.into_owned().with_input(input);
176
177 UnsignedOrder { order: Cow::Owned(order), ..self }
178 }
179
180 pub fn with_input(self, token: Address, amount: U256) -> UnsignedOrder<'static> {
182 self.with_raw_input(Input { token, amount })
183 }
184
185 pub fn outputs(&self) -> &[Output] {
187 self.order.outputs()
188 }
189
190 pub fn with_raw_output(self, output: Output) -> UnsignedOrder<'static> {
192 let order = self.order.into_owned().with_output(output);
193
194 UnsignedOrder { order: Cow::Owned(order), ..self }
195 }
196
197 pub fn with_output(
199 self,
200 token: Address,
201 amount: U256,
202 recipient: Address,
203 chain_id: u32,
204 ) -> UnsignedOrder<'static> {
205 self.with_raw_output(Output { token, amount, recipient, chainId: chain_id })
206 }
207
208 pub fn with_deadline(self, deadline: u64) -> UnsignedOrder<'static> {
210 let order = self.order.into_owned().with_deadline(deadline);
211
212 UnsignedOrder { order: Cow::Owned(order), ..self }
213 }
214
215 pub fn with_nonce(self, nonce: u64) -> Self {
217 Self { nonce: Some(nonce), ..self }
218 }
219
220 pub fn with_chain(self, constants: &SignetSystemConstants) -> Self {
223 Self {
224 rollup_chain_id: Some(constants.ru_chain_id()),
225 rollup_order_address: Some(constants.ru_orders()),
226 ..self
227 }
228 }
229
230 pub fn to_order(&self) -> Order {
233 self.order.clone().into_owned()
234 }
235
236 pub fn into_order(self) -> Cow<'a, Order> {
238 self.order
239 }
240
241 pub async fn sign<S: Signer>(&self, signer: &S) -> Result<SignedOrder, SigningError> {
243 let nonce = self.nonce.unwrap_or(Utc::now().timestamp_micros() as u64);
245
246 let rollup_chain_id = self.rollup_chain_id.ok_or(SigningError::MissingChainId)?;
248 let rollup_order_contract =
249 self.rollup_order_address.ok_or(SigningError::MissingOrderContract(rollup_chain_id))?;
250
251 let outputs = self.order.outputs().to_vec();
253 let permitted: Vec<TokenPermissions> = self.order.inputs().iter().map(Into::into).collect();
255
256 let permit = permit_signing_info(
258 outputs,
259 permitted,
260 self.order.deadline(),
261 nonce,
262 rollup_chain_id,
263 rollup_order_contract,
264 );
265
266 let signature = signer.sign_hash(&permit.signing_hash).await?;
268
269 Ok(SignedOrder::new(
271 Permit2Batch {
272 permit: permit.permit,
273 owner: signer.address(),
274 signature: signature.as_bytes().into(),
275 },
276 permit.outputs,
277 ))
278 }
279}
280
281#[cfg(test)]
282mod tests {
283 use alloy::primitives::{b256, Signature, U256};
284 use signet_zenith::HostOrders::{PermitBatchTransferFrom, TokenPermissions};
285
286 use super::*;
287
288 fn basic_order() -> SignedOrder {
289 SignedOrder::new(
290 Permit2Batch {
291 permit: PermitBatchTransferFrom {
292 permitted: vec![TokenPermissions { token: Address::ZERO, amount: U256::ZERO }],
293 nonce: U256::ZERO,
294 deadline: U256::ZERO,
295 },
296 owner: Address::ZERO,
297 signature: Signature::test_signature().as_bytes().into(),
298 },
299 vec![Output {
300 token: Address::ZERO,
301 amount: U256::ZERO,
302 recipient: Address::ZERO,
303 chainId: 0,
304 }],
305 )
306 }
307
308 #[test]
309 fn test_order_hash() {
310 let order = basic_order();
311 let hash = order.order_hash();
312 let pre_image = order.order_hash_pre_image();
313
314 assert_eq!(hash, &keccak256(pre_image));
315 assert_eq!(
316 hash,
317 &b256!("0xba359dd4f891bed0a2cf87c306e59fb6ee099e02b5b0fa86584cdcc44bf6c272")
318 );
319 }
320}