use alloy_primitives::Address;
use alloy_signer::SignerSync;
use crate::{
AppDataDoc, Chain, DomainSeparator, Error, OrderBookApi, OrderCreation, OrderData,
OrderQuoteResponse, OrderUid, QuoteRequest, Result, signing_scheme::EcdsaSigningScheme,
};
#[derive(Debug, Clone)]
pub struct SwapOrder<'a> {
pub request: QuoteRequest,
pub app_data: &'a AppDataDoc,
pub scheme: EcdsaSigningScheme,
pub partner_fee_bps: u32,
pub slippage_bps: u32,
pub protocol_fee_bps_override: Option<String>,
}
impl<'a> SwapOrder<'a> {
pub const fn eip712(request: QuoteRequest, app_data: &'a AppDataDoc) -> Self {
Self {
request,
app_data,
scheme: EcdsaSigningScheme::Eip712,
partner_fee_bps: 0,
slippage_bps: 50,
protocol_fee_bps_override: None,
}
}
pub const fn with_partner_fee_bps(mut self, bps: u32) -> Self {
self.partner_fee_bps = bps;
self
}
pub const fn with_slippage_bps(mut self, bps: u32) -> Self {
self.slippage_bps = bps;
self
}
pub const fn with_ethsign(mut self) -> Self {
self.scheme = EcdsaSigningScheme::EthSign;
self
}
}
#[derive(Debug, Clone)]
pub struct PostedSwapOrder {
pub uid: OrderUid,
pub order_data: OrderData,
pub quote: OrderQuoteResponse,
}
#[derive(Debug, Clone)]
pub struct TradingClient {
api: OrderBookApi,
chain: Chain,
}
impl TradingClient {
pub fn new(chain: Chain) -> Self {
Self {
api: OrderBookApi::new(chain),
chain,
}
}
pub const fn from_orderbook(chain: Chain, api: OrderBookApi) -> Self {
Self { api, chain }
}
pub const fn chain(&self) -> Chain {
self.chain
}
pub const fn orderbook(&self) -> &OrderBookApi {
&self.api
}
pub async fn post_swap_order<S>(
&self,
params: SwapOrder<'_>,
signer: &S,
) -> Result<PostedSwapOrder>
where
S: SignerSync,
{
let app_data_hash = params.app_data.hash();
let app_data_json = params.app_data.canonical_json();
let quote = self.api.get_quote(¶ms.request).await?;
let order_data = quote.to_signed_order_data_with_costs(
¶ms.request,
params.partner_fee_bps,
params.slippage_bps,
params.protocol_fee_bps_override.as_deref(),
app_data_hash,
)?;
let domain = DomainSeparator::new(self.chain.id(), self.chain.settlement());
let signature = order_data
.sign(params.scheme, &domain, signer)
.map_err(Error::Signature)?;
if params.request.from == Address::ZERO {
return Err(Error::OrderCreationInvalid {
field: "from",
reason: "QuoteRequest.from must be the order owner; \
TradingClient does not infer it from the signer",
});
}
let from = params.request.from;
let body = OrderCreation::from_signed_order_data(
order_data,
signature,
from,
app_data_json.clone(),
Some(quote.id),
)?;
self.api
.put_app_data(
&app_data_hash,
&crate::AppDataDocument {
full_app_data: app_data_json,
},
)
.await?;
let uid = self.api.post_order(&body).await?;
Ok(PostedSwapOrder {
uid,
order_data,
quote,
})
}
}