use crate::agg::AggregateOrders;
use crate::signing::{permit_signing_info, SignedPermitError, SigningError};
use crate::SignedOrder;
use alloy::{
network::TransactionBuilder, primitives::Address, rpc::types::TransactionRequest,
signers::Signer, sol_types::SolCall,
};
use chrono::Utc;
use serde::{Deserialize, Serialize};
use signet_constants::SignetSystemConstants;
use signet_zenith::RollupOrders::Order;
use signet_zenith::{
BundleHelper::{FillPermit2, IOrders},
RollupOrders::{fillPermit2Call, Output, Permit2Batch, TokenPermissions},
};
use std::{borrow::Cow, collections::HashMap};
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct SignedFill {
#[serde(flatten)]
pub permit: Permit2Batch,
pub outputs: Vec<Output>,
}
impl SignedFill {
pub const fn new(permit: Permit2Batch, outputs: Vec<Output>) -> Self {
Self { permit, outputs }
}
pub fn validate(&self, timestamp: u64) -> Result<(), SignedPermitError> {
let deadline = self.permit.permit.deadline.saturating_to::<u64>();
if timestamp > deadline {
return Err(SignedPermitError::DeadlinePassed { current: timestamp, deadline });
}
if self.outputs.len() != self.permit.permit.permitted.len() {
return Err(SignedPermitError::PermitMismatch);
}
for (output, permit) in self.outputs.iter().zip(self.permit.permit.permitted.iter()) {
if output.token != permit.token {
return Err(SignedPermitError::PermitMismatch);
}
if output.amount != permit.amount {
return Err(SignedPermitError::PermitMismatch);
}
}
Ok(())
}
pub fn to_fill_tx(&self, order_contract: Address) -> TransactionRequest {
let fill_data =
fillPermit2Call { outputs: self.outputs.clone(), permit2: self.permit.clone() }
.abi_encode();
TransactionRequest::default().with_input(fill_data).with_to(order_contract)
}
}
impl From<SignedFill> for FillPermit2 {
fn from(fill: SignedFill) -> Self {
FillPermit2 {
permit2: fill.permit.into(),
outputs: fill.outputs.into_iter().map(IOrders::Output::from).collect(),
}
}
}
impl From<&SignedFill> for FillPermit2 {
fn from(fill: &SignedFill) -> Self {
FillPermit2 {
permit2: (&fill.permit).into(),
outputs: fill.outputs.iter().map(IOrders::Output::from).collect(),
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct UnsignedFill<'a> {
ru_chain_id: Option<u64>,
orders: Cow<'a, AggregateOrders>,
deadline: Option<u64>,
nonce: Option<u64>,
target_chains: HashMap<u64, Address>,
}
impl Default for UnsignedFill<'_> {
fn default() -> Self {
Self {
ru_chain_id: None,
orders: Cow::Owned(AggregateOrders::default()),
deadline: None,
nonce: None,
target_chains: HashMap::new(),
}
}
}
impl<'a> From<&'a AggregateOrders> for UnsignedFill<'a> {
fn from(orders: &'a AggregateOrders) -> Self {
Self {
ru_chain_id: None,
orders: Cow::Borrowed(orders),
deadline: None,
nonce: None,
target_chains: HashMap::new(),
}
}
}
impl From<Order> for UnsignedFill<'static> {
fn from(order: Order) -> Self {
let mut aggregate_orders = AggregateOrders::default();
aggregate_orders.ingest(&order);
Self {
ru_chain_id: None,
orders: Cow::Owned(aggregate_orders),
deadline: None,
nonce: None,
target_chains: HashMap::new(),
}
}
}
impl<'a> UnsignedFill<'a> {
pub fn new() -> Self {
Self {
ru_chain_id: None,
orders: Cow::Owned(AggregateOrders::default()),
deadline: None,
nonce: None,
target_chains: HashMap::new(),
}
}
pub fn fill_raw(mut self, order: &Order) -> UnsignedFill<'static> {
self.orders.to_mut().ingest(order);
UnsignedFill {
ru_chain_id: self.ru_chain_id,
orders: Cow::Owned(self.orders.into_owned()),
deadline: self.deadline,
nonce: self.nonce,
target_chains: self.target_chains,
}
}
pub fn fill(mut self, order: &SignedOrder) -> UnsignedFill<'static> {
self.orders.to_mut().ingest_signed(order);
UnsignedFill {
ru_chain_id: self.ru_chain_id,
orders: Cow::Owned(self.orders.into_owned()),
deadline: self.deadline,
nonce: self.nonce,
target_chains: self.target_chains,
}
}
pub fn with_nonce(self, nonce: u64) -> Self {
Self { nonce: Some(nonce), ..self }
}
pub fn with_deadline(self, deadline: u64) -> Self {
Self { deadline: Some(deadline), ..self }
}
#[deprecated(since = "0.14.1", note = "Use `with_chain` instead.")]
pub fn with_ru_chain_id(self, ru_chain_id: u64) -> Self {
Self { ru_chain_id: Some(ru_chain_id), ..self }
}
pub fn with_chain(mut self, constants: SignetSystemConstants) -> Self {
self.target_chains.insert(constants.ru_chain_id(), constants.ru_orders());
self.target_chains.insert(constants.host_chain_id(), constants.host_orders());
Self { ru_chain_id: Some(constants.ru_chain_id()), ..self }
}
pub async fn sign<S: Signer>(
&self,
signer: &S,
) -> Result<HashMap<u64, SignedFill>, SigningError> {
let mut fills = HashMap::new();
for target_chain_id in self.orders.target_chain_ids() {
let signed_fill = self.sign_for(target_chain_id, signer).await?;
fills.insert(target_chain_id, signed_fill);
}
Ok(fills)
}
pub async fn sign_for<S: Signer>(
&self,
target_chain_id: u64,
signer: &S,
) -> Result<SignedFill, SigningError> {
let now = Utc::now();
let nonce = self.nonce.unwrap_or(now.timestamp_micros() as u64);
let deadline = self.deadline.unwrap_or(now.timestamp() as u64 + 12);
let target_order_address = self
.target_chains
.get(&target_chain_id)
.ok_or(SigningError::MissingOrderContract(target_chain_id))?;
let ru_chain_id = self.ru_chain_id.ok_or(SigningError::MissingChainId)?;
let outputs = self.orders.outputs_for(target_chain_id, ru_chain_id);
let permitted: Vec<TokenPermissions> = outputs.iter().map(Into::into).collect();
let permit = permit_signing_info(
outputs,
permitted,
deadline,
nonce,
target_chain_id,
*target_order_address,
);
let signature = signer.sign_hash(&permit.signing_hash).await?;
Ok(SignedFill {
permit: Permit2Batch {
permit: permit.permit,
owner: signer.address(),
signature: signature.as_bytes().into(),
},
outputs: permit.outputs,
})
}
}