use alloy_primitives::{Address, U256};
use alloy_signer_local::PrivateKeySigner;
use rand::Rng;
use std::time::{SystemTime, UNIX_EPOCH};
use super::constants::get_exchange_address;
use super::eip712::sign_order;
use super::types::{Order, OrderData, SignatureType, SignedOrder};
use crate::error::{PolymarketError, Result};
pub fn generate_salt() -> U256 {
let mut rng = rand::thread_rng();
let salt: u32 = rng.r#gen();
U256::from(salt)
}
#[allow(dead_code)]
pub fn generate_salt_ts_style() -> U256 {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
let mut rng = rand::thread_rng();
let random: f64 = rng.r#gen();
let salt = (random * now as f64).round() as u128;
U256::from(salt)
}
#[derive(Debug, Clone)]
pub struct ExchangeOrderBuilder {
signer: PrivateKeySigner,
chain_id: u64,
signature_type: SignatureType,
funder_address: Option<Address>,
}
impl ExchangeOrderBuilder {
pub fn new(
signer: PrivateKeySigner,
chain_id: u64,
signature_type: Option<SignatureType>,
funder_address: Option<Address>,
) -> Self {
Self {
signer,
chain_id,
signature_type: signature_type.unwrap_or(SignatureType::Eoa),
funder_address,
}
}
pub fn signer_address(&self) -> Address {
self.signer.address()
}
pub fn maker_address(&self) -> Address {
self.funder_address.unwrap_or_else(|| self.signer.address())
}
pub async fn build_signed_order(
&self,
order_data: OrderData,
neg_risk: bool,
) -> Result<SignedOrder> {
let order = self.build_order(order_data)?;
let contract = get_exchange_address(self.chain_id, neg_risk)?;
let signature = sign_order(&self.signer, &order, self.chain_id, contract).await?;
Ok(SignedOrder::new(order, signature))
}
pub fn build_order(&self, order_data: OrderData) -> Result<Order> {
let signer_address = order_data.signer.unwrap_or(order_data.maker);
if signer_address != self.signer.address() && order_data.signer.is_some() {
return Err(PolymarketError::other(format!(
"Signer mismatch: expected {:?}, got {:?}",
self.signer.address(),
signer_address
)));
}
let salt = generate_salt();
let expiration = order_data.expiration.unwrap_or(U256::ZERO);
let signature_type = order_data.signature_type.unwrap_or(self.signature_type);
Ok(Order {
salt,
maker: order_data.maker,
signer: signer_address,
taker: order_data.taker,
tokenId: order_data.token_id,
makerAmount: order_data.maker_amount,
takerAmount: order_data.taker_amount,
expiration,
nonce: order_data.nonce,
feeRateBps: order_data.fee_rate_bps,
side: order_data.side as u8,
signatureType: signature_type as u8,
})
}
}
#[cfg(test)]
mod tests {
use super::super::types::Side;
use super::*;
#[test]
fn test_generate_salt() {
let salt1 = generate_salt();
let salt2 = generate_salt();
assert!(salt1 != U256::ZERO || salt2 != U256::ZERO);
assert_ne!(salt1, salt2);
}
#[test]
fn test_exchange_order_builder_new() {
let signer = PrivateKeySigner::random();
let builder = ExchangeOrderBuilder::new(signer.clone(), 137, None, None);
assert_eq!(builder.chain_id, 137);
assert_eq!(builder.signature_type, SignatureType::Eoa);
assert!(builder.funder_address.is_none());
assert_eq!(builder.signer_address(), signer.address());
}
#[test]
fn test_exchange_order_builder_with_funder() {
let signer = PrivateKeySigner::random();
let funder = Address::repeat_byte(1);
let builder = ExchangeOrderBuilder::new(
signer.clone(),
137,
Some(SignatureType::PolyProxy),
Some(funder),
);
assert_eq!(builder.signature_type, SignatureType::PolyProxy);
assert_eq!(builder.funder_address, Some(funder));
assert_eq!(builder.maker_address(), funder);
}
#[test]
fn test_build_order() {
let signer = PrivateKeySigner::random();
let builder = ExchangeOrderBuilder::new(signer.clone(), 137, None, None);
let order_data = OrderData {
maker: signer.address(),
taker: Address::ZERO,
token_id: U256::from(12345),
maker_amount: U256::from(1_000_000),
taker_amount: U256::from(500_000),
side: Side::Buy,
fee_rate_bps: U256::from(100),
nonce: U256::ZERO,
signer: None,
expiration: None,
signature_type: None,
};
let order = builder.build_order(order_data).unwrap();
assert_eq!(order.maker, signer.address());
assert_eq!(order.signer, signer.address());
assert_eq!(order.taker, Address::ZERO);
assert_eq!(order.tokenId, U256::from(12345));
assert_eq!(order.makerAmount, U256::from(1_000_000));
assert_eq!(order.takerAmount, U256::from(500_000));
assert_eq!(order.side, 0);
assert_eq!(order.signatureType, 0);
}
#[tokio::test]
async fn test_build_signed_order() {
let signer = PrivateKeySigner::random();
let builder = ExchangeOrderBuilder::new(signer.clone(), 137, None, None);
let order_data = OrderData {
maker: signer.address(),
taker: Address::ZERO,
token_id: U256::from(12345),
maker_amount: U256::from(1_000_000),
taker_amount: U256::from(500_000),
side: Side::Buy,
fee_rate_bps: U256::from(100),
nonce: U256::ZERO,
signer: None,
expiration: None,
signature_type: None,
};
let signed_order = builder.build_signed_order(order_data, false).await.unwrap();
assert!(!signed_order.signature.is_empty());
assert!(signed_order.signature.starts_with("0x"));
}
}