mirror of
https://github.com/floor-licker/polyfill-rs.git
synced 2026-07-27 20:47:46 +00:00
feat: add prepared order path
This commit is contained in:
@@ -89,6 +89,31 @@ fn benchmark_create_order_eip712(c: &mut Criterion) {
|
||||
black_box(signed_order)
|
||||
})
|
||||
});
|
||||
|
||||
let prepared = builder
|
||||
.prepare_order_path(
|
||||
CHAIN_ID,
|
||||
order_args.token_id.clone(),
|
||||
options.tick_size.unwrap(),
|
||||
options.neg_risk.unwrap(),
|
||||
order_args.builder_code.as_deref(),
|
||||
order_args.metadata.as_deref(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
c.bench_function("prepared_create_order_eip712_signature", |b| {
|
||||
b.iter(|| {
|
||||
let signed_order = prepared
|
||||
.create_limit_order(
|
||||
black_box(order_args.side),
|
||||
black_box(order_args.price),
|
||||
black_box(order_args.size),
|
||||
black_box(order_args.expiration),
|
||||
)
|
||||
.unwrap();
|
||||
black_box(signed_order)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// Benchmark: Serialize a signed order body and build L2 auth headers for POST /order.
|
||||
|
||||
+31
-9
@@ -8,7 +8,7 @@ use crate::types::ApiCredentials;
|
||||
use alloy_primitives::{hex::encode_prefixed, Address, B256, U256};
|
||||
use alloy_signer::SignerSync;
|
||||
use alloy_signer_local::PrivateKeySigner;
|
||||
use alloy_sol_types::{eip712_domain, sol};
|
||||
use alloy_sol_types::{eip712_domain, sol, Eip712Domain};
|
||||
use base64::engine::Engine;
|
||||
use hmac::{Hmac, Mac};
|
||||
use serde::Serialize;
|
||||
@@ -142,6 +142,25 @@ pub struct SignedOrderMessage {
|
||||
pub builder: B256,
|
||||
}
|
||||
|
||||
/// Prepared EIP-712 domain for signing orders against one exchange contract.
|
||||
#[derive(Clone)]
|
||||
pub struct PreparedOrderDomain {
|
||||
domain: Eip712Domain,
|
||||
}
|
||||
|
||||
impl PreparedOrderDomain {
|
||||
pub fn new(chain_id: u64, verifying_contract: Address) -> Self {
|
||||
let domain = eip712_domain!(
|
||||
name: "Polymarket CTF Exchange",
|
||||
version: "2",
|
||||
chain_id: chain_id,
|
||||
verifying_contract: verifying_contract,
|
||||
);
|
||||
|
||||
Self { domain }
|
||||
}
|
||||
}
|
||||
|
||||
/// Get current Unix timestamp in seconds
|
||||
pub fn get_current_unix_time_secs() -> u64 {
|
||||
SystemTime::now()
|
||||
@@ -185,6 +204,16 @@ pub fn sign_order_message(
|
||||
order: SignedOrderMessage,
|
||||
chain_id: u64,
|
||||
verifying_contract: Address,
|
||||
) -> Result<String> {
|
||||
let domain = PreparedOrderDomain::new(chain_id, verifying_contract);
|
||||
sign_order_message_with_domain(signer, order, &domain)
|
||||
}
|
||||
|
||||
/// Sign order message using a prepared EIP-712 domain.
|
||||
pub fn sign_order_message_with_domain(
|
||||
signer: &PrivateKeySigner,
|
||||
order: SignedOrderMessage,
|
||||
domain: &PreparedOrderDomain,
|
||||
) -> Result<String> {
|
||||
let order = Order {
|
||||
salt: order.salt,
|
||||
@@ -200,15 +229,8 @@ pub fn sign_order_message(
|
||||
builder: order.builder,
|
||||
};
|
||||
|
||||
let domain = eip712_domain!(
|
||||
name: "Polymarket CTF Exchange",
|
||||
version: "2",
|
||||
chain_id: chain_id,
|
||||
verifying_contract: verifying_contract,
|
||||
);
|
||||
|
||||
let signature = signer
|
||||
.sign_typed_data_sync(&order, &domain)
|
||||
.sign_typed_data_sync(&order, &domain.domain)
|
||||
.map_err(|e| PolyfillError::crypto(format!("Order signature failed: {}", e)))?;
|
||||
|
||||
Ok(encode_prefixed(signature.as_bytes()))
|
||||
|
||||
+83
-2
@@ -974,6 +974,35 @@ impl ClobClient {
|
||||
self.get_clob_market_info(&market.condition_id).await
|
||||
}
|
||||
|
||||
/// Prepare reusable order-path state for repeated orders on one token.
|
||||
///
|
||||
/// This resolves tick-size/neg-risk once, then caches fixed order-path inputs such as the
|
||||
/// parsed token ID, exchange address, normalized bytes32 values, and EIP-712 domain.
|
||||
pub async fn prepare_order_path(
|
||||
&self,
|
||||
token_id: &str,
|
||||
options: Option<&CreateOrderOptions>,
|
||||
builder_code: Option<&str>,
|
||||
metadata: Option<&str>,
|
||||
) -> Result<crate::orders::PreparedOrderPath> {
|
||||
let order_builder = self
|
||||
.order_builder
|
||||
.as_ref()
|
||||
.ok_or_else(|| PolyfillError::auth("Order builder not initialized"))?;
|
||||
|
||||
let create_order_options = self.get_filled_order_options(token_id, options).await?;
|
||||
let builder_code = builder_code.or(self.builder_code.as_deref());
|
||||
|
||||
order_builder.prepare_order_path(
|
||||
self.chain_id,
|
||||
token_id,
|
||||
create_order_options.tick_size.expect("Should be filled"),
|
||||
create_order_options.neg_risk.expect("Should be filled"),
|
||||
builder_code,
|
||||
metadata,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create an order
|
||||
pub async fn create_order(
|
||||
&self,
|
||||
@@ -2516,8 +2545,9 @@ pub type PolyfillClient = ClobClient;
|
||||
mod tests {
|
||||
use super::{ClobClient, OrderArgs as ClientOrderArgs};
|
||||
use crate::types::{
|
||||
OrderType, PostOrderOptions, PricesHistoryInterval, RfqCreateQuote, RfqCreateRequest,
|
||||
RfqOrderExecutionRequest, RfqQuotesParams, RfqRequestsParams, Side, SignedOrderRequest,
|
||||
CreateOrderOptions, OrderType, PostOrderOptions, PricesHistoryInterval, RfqCreateQuote,
|
||||
RfqCreateRequest, RfqOrderExecutionRequest, RfqQuotesParams, RfqRequestsParams, Side,
|
||||
SignedOrderRequest,
|
||||
};
|
||||
use crate::{ApiCredentials, ClientConfig, PolyfillError};
|
||||
use mockito::{Matcher, Server};
|
||||
@@ -3288,6 +3318,57 @@ mod tests {
|
||||
assert_eq!(default_args.side, Side::BUY);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_prepare_order_path_uses_client_builder_code() {
|
||||
let mut server = Server::new_async().await;
|
||||
let tick_size_mock = server
|
||||
.mock("GET", "/tick-size")
|
||||
.match_query(Matcher::UrlEncoded("token_id".into(), "123456".into()))
|
||||
.with_status(200)
|
||||
.with_header("content-type", "application/json")
|
||||
.with_body(r#"{"minimum_tick_size":"0.01"}"#)
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let client = ClobClient::from_config(ClientConfig {
|
||||
base_url: server.url(),
|
||||
chain: 137,
|
||||
private_key: Some(
|
||||
"0x1234567890123456789012345678901234567890123456789012345678901234".to_string(),
|
||||
),
|
||||
builder_code: Some(
|
||||
"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".to_string(),
|
||||
),
|
||||
..ClientConfig::default()
|
||||
})
|
||||
.expect("test auth client with builder code");
|
||||
let options = CreateOrderOptions {
|
||||
tick_size: Some(Decimal::from_str("0.01").unwrap()),
|
||||
neg_risk: Some(false),
|
||||
};
|
||||
|
||||
let prepared = client
|
||||
.prepare_order_path("123456", Some(&options), None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
let order = prepared
|
||||
.create_limit_order(
|
||||
Side::BUY,
|
||||
Decimal::from_str("0.45").unwrap(),
|
||||
Decimal::from_str("12.34").unwrap(),
|
||||
Some(1_900_000_000),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
tick_size_mock.assert_async().await;
|
||||
assert_eq!(order.token_id, "123456");
|
||||
assert_eq!(
|
||||
order.builder,
|
||||
"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
);
|
||||
assert_eq!(order.metadata, crate::orders::BYTES32_ZERO);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_clob_market_info_success() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
+251
-27
@@ -3,7 +3,9 @@
|
||||
//! This module handles the complex process of creating and signing orders
|
||||
//! for the Polymarket CLOB, including EIP-712 signature generation.
|
||||
|
||||
use crate::auth::{sign_order_message, SignedOrderMessage};
|
||||
use crate::auth::{
|
||||
sign_order_message, sign_order_message_with_domain, PreparedOrderDomain, SignedOrderMessage,
|
||||
};
|
||||
use crate::errors::{PolyfillError, Result};
|
||||
use crate::types::{
|
||||
CreateOrderOptions, MarketOrderArgs, OrderArgs, OrderType, Side, SignedOrderRequest,
|
||||
@@ -33,6 +35,7 @@ pub enum SigType {
|
||||
}
|
||||
|
||||
/// Rounding configuration for different tick sizes
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct RoundConfig {
|
||||
price: u32,
|
||||
size: u32,
|
||||
@@ -47,10 +50,28 @@ pub struct ContractConfig {
|
||||
}
|
||||
|
||||
/// Order builder for creating and signing orders
|
||||
#[derive(Clone)]
|
||||
pub struct OrderBuilder {
|
||||
signer: PrivateKeySigner,
|
||||
signer_address: Address,
|
||||
signer_checksum: String,
|
||||
sig_type: SigType,
|
||||
funder: Address,
|
||||
funder_checksum: String,
|
||||
}
|
||||
|
||||
/// Prepared low-latency order path for a single market/token configuration.
|
||||
#[derive(Clone)]
|
||||
pub struct PreparedOrderPath {
|
||||
builder: OrderBuilder,
|
||||
token_id: String,
|
||||
token_id_u256: U256,
|
||||
round_config: RoundConfig,
|
||||
domain: PreparedOrderDomain,
|
||||
builder_bytes: B256,
|
||||
builder_code: String,
|
||||
metadata_bytes: B256,
|
||||
metadata: String,
|
||||
}
|
||||
|
||||
const POLYGON_PROXY_FACTORY: &str = "0xaB45c5A4B0c941a2F231C04C3f49182e1A254052";
|
||||
@@ -99,6 +120,28 @@ pub fn get_contract_config(chain_id: u64, neg_risk: bool) -> Option<ContractConf
|
||||
}
|
||||
}
|
||||
|
||||
fn exchange_address_for(chain_id: u64, neg_risk: bool) -> Result<Address> {
|
||||
let contract_config = get_contract_config(chain_id, neg_risk).ok_or_else(|| {
|
||||
PolyfillError::config("No contract found with given chain_id and neg_risk")
|
||||
})?;
|
||||
|
||||
Address::from_str(&contract_config.exchange)
|
||||
.map_err(|e| PolyfillError::config(format!("Invalid exchange address: {}", e)))
|
||||
}
|
||||
|
||||
fn parse_token_id(token_id: &str) -> Result<U256> {
|
||||
U256::from_str_radix(token_id, 10)
|
||||
.map_err(|e| PolyfillError::validation(format!("Incorrect tokenId format: {}", e)))
|
||||
}
|
||||
|
||||
fn parse_optional_bytes32(name: &str, value: Option<&str>) -> Result<(B256, String)> {
|
||||
let normalized = normalize_optional_bytes32(name, value)?;
|
||||
let parsed = B256::from_str(&normalized)
|
||||
.map_err(|e| PolyfillError::validation(format!("Invalid {name} bytes32 value: {e}")))?;
|
||||
|
||||
Ok((parsed, normalized))
|
||||
}
|
||||
|
||||
pub fn sig_type_from_u8(signature_type: u8) -> Result<SigType> {
|
||||
match signature_type {
|
||||
0 => Ok(SigType::Eoa),
|
||||
@@ -303,12 +346,18 @@ impl OrderBuilder {
|
||||
funder: Option<Address>,
|
||||
) -> Self {
|
||||
let sig_type = sig_type.unwrap_or(SigType::Eoa);
|
||||
let funder = funder.unwrap_or(signer.address());
|
||||
let signer_address = signer.address();
|
||||
let signer_checksum = signer_address.to_checksum(None);
|
||||
let funder = funder.unwrap_or(signer_address);
|
||||
let funder_checksum = funder.to_checksum(None);
|
||||
|
||||
OrderBuilder {
|
||||
signer,
|
||||
signer_address,
|
||||
signer_checksum,
|
||||
sig_type,
|
||||
funder,
|
||||
funder_checksum,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,6 +366,41 @@ impl OrderBuilder {
|
||||
self.sig_type as u8
|
||||
}
|
||||
|
||||
/// Prepare reusable order-path state for one market/token.
|
||||
///
|
||||
/// This caches tick-size rounding, exchange address parsing, token ID parsing, normalized
|
||||
/// bytes32 fields, and the EIP-712 domain. Use this for latency-sensitive repeated order
|
||||
/// creation on the same token.
|
||||
pub fn prepare_order_path(
|
||||
&self,
|
||||
chain_id: u64,
|
||||
token_id: impl Into<String>,
|
||||
tick_size: Decimal,
|
||||
neg_risk: bool,
|
||||
builder_code: Option<&str>,
|
||||
metadata: Option<&str>,
|
||||
) -> Result<PreparedOrderPath> {
|
||||
let token_id = token_id.into();
|
||||
let token_id_u256 = parse_token_id(&token_id)?;
|
||||
let round_config = *parse_round_config(tick_size)?;
|
||||
let exchange = exchange_address_for(chain_id, neg_risk)?;
|
||||
let domain = PreparedOrderDomain::new(chain_id, exchange);
|
||||
let (builder_bytes, builder_code) = parse_optional_bytes32("builder_code", builder_code)?;
|
||||
let (metadata_bytes, metadata) = parse_optional_bytes32("metadata", metadata)?;
|
||||
|
||||
Ok(PreparedOrderPath {
|
||||
builder: self.clone(),
|
||||
token_id,
|
||||
token_id_u256,
|
||||
round_config,
|
||||
domain,
|
||||
builder_bytes,
|
||||
builder_code,
|
||||
metadata_bytes,
|
||||
metadata,
|
||||
})
|
||||
}
|
||||
|
||||
/// Fix amount rounding according to configuration
|
||||
fn fix_amount_rounding(&self, mut amt: Decimal, round_config: &RoundConfig) -> Decimal {
|
||||
if amt.scale() > round_config.amount {
|
||||
@@ -459,12 +543,7 @@ impl OrderBuilder {
|
||||
.neg_risk
|
||||
.ok_or_else(|| PolyfillError::validation("Cannot create order without neg_risk"))?;
|
||||
|
||||
let contract_config = get_contract_config(chain_id, neg_risk).ok_or_else(|| {
|
||||
PolyfillError::config("No contract found with given chain_id and neg_risk")
|
||||
})?;
|
||||
|
||||
let exchange_address = Address::from_str(&contract_config.exchange)
|
||||
.map_err(|e| PolyfillError::config(format!("Invalid exchange address: {}", e)))?;
|
||||
let exchange_address = exchange_address_for(chain_id, neg_risk)?;
|
||||
|
||||
self.build_signed_order(
|
||||
order_args.token_id.clone(),
|
||||
@@ -502,12 +581,7 @@ impl OrderBuilder {
|
||||
.neg_risk
|
||||
.ok_or_else(|| PolyfillError::validation("Cannot create order without neg_risk"))?;
|
||||
|
||||
let contract_config = get_contract_config(chain_id, neg_risk).ok_or_else(|| {
|
||||
PolyfillError::config("No contract found with given chain_id and neg_risk")
|
||||
})?;
|
||||
|
||||
let exchange_address = Address::from_str(&contract_config.exchange)
|
||||
.map_err(|e| PolyfillError::config(format!("Invalid exchange address: {}", e)))?;
|
||||
let exchange_address = exchange_address_for(chain_id, neg_risk)?;
|
||||
|
||||
self.build_signed_order(
|
||||
order_args.token_id.clone(),
|
||||
@@ -542,35 +616,30 @@ impl OrderBuilder {
|
||||
.expect("Time went backwards")
|
||||
.as_millis();
|
||||
|
||||
let u256_token_id = U256::from_str_radix(&token_id, 10)
|
||||
.map_err(|e| PolyfillError::validation(format!("Incorrect tokenId format: {}", e)))?;
|
||||
let builder = normalize_optional_bytes32("builder_code", builder_code)?;
|
||||
let metadata = normalize_optional_bytes32("metadata", metadata)?;
|
||||
let u256_token_id = parse_token_id(&token_id)?;
|
||||
let (builder_bytes, builder) = parse_optional_bytes32("builder_code", builder_code)?;
|
||||
let (metadata_bytes, metadata) = parse_optional_bytes32("metadata", metadata)?;
|
||||
|
||||
let order = SignedOrderMessage {
|
||||
salt: U256::from(seed),
|
||||
maker: self.funder,
|
||||
signer: self.signer.address(),
|
||||
signer: self.signer_address,
|
||||
token_id: u256_token_id,
|
||||
maker_amount,
|
||||
taker_amount,
|
||||
side: side as u8,
|
||||
signature_type: self.sig_type as u8,
|
||||
timestamp: U256::from(timestamp),
|
||||
metadata: B256::from_str(&metadata).map_err(|e| {
|
||||
PolyfillError::validation(format!("Invalid metadata bytes32 value: {e}"))
|
||||
})?,
|
||||
builder: B256::from_str(&builder).map_err(|e| {
|
||||
PolyfillError::validation(format!("Invalid builder_code bytes32 value: {e}"))
|
||||
})?,
|
||||
metadata: metadata_bytes,
|
||||
builder: builder_bytes,
|
||||
};
|
||||
|
||||
let signature = sign_order_message(&self.signer, order, chain_id, exchange)?;
|
||||
|
||||
Ok(SignedOrderRequest {
|
||||
salt: seed,
|
||||
maker: self.funder.to_checksum(None),
|
||||
signer: self.signer.address().to_checksum(None),
|
||||
maker: self.funder_checksum.clone(),
|
||||
signer: self.signer_checksum.clone(),
|
||||
token_id,
|
||||
maker_amount: maker_amount.to_string(),
|
||||
taker_amount: taker_amount.to_string(),
|
||||
@@ -585,6 +654,90 @@ impl OrderBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
impl PreparedOrderPath {
|
||||
/// Create and sign a limit order using the cached market/token context.
|
||||
pub fn create_limit_order(
|
||||
&self,
|
||||
side: Side,
|
||||
price: Decimal,
|
||||
size: Decimal,
|
||||
expiration: Option<u64>,
|
||||
) -> Result<SignedOrderRequest> {
|
||||
let (maker_amount, taker_amount) =
|
||||
self.builder
|
||||
.get_order_amounts(side, size, price, &self.round_config)?;
|
||||
|
||||
self.build_signed_order(side, maker_amount, taker_amount, expiration.unwrap_or(0))
|
||||
}
|
||||
|
||||
/// Create and sign a market order using the cached market/token context.
|
||||
pub fn create_market_order(
|
||||
&self,
|
||||
side: Side,
|
||||
amount: Decimal,
|
||||
price: Decimal,
|
||||
order_type: OrderType,
|
||||
) -> Result<SignedOrderRequest> {
|
||||
if !matches!(order_type, OrderType::FOK | OrderType::FAK) {
|
||||
return Err(PolyfillError::validation(
|
||||
"Market orders only support FOK and FAK order types",
|
||||
));
|
||||
}
|
||||
|
||||
let (maker_amount, taker_amount) =
|
||||
self.builder
|
||||
.get_market_order_amounts(side, amount, price, &self.round_config)?;
|
||||
|
||||
self.build_signed_order(side, maker_amount, taker_amount, 0)
|
||||
}
|
||||
|
||||
fn build_signed_order(
|
||||
&self,
|
||||
side: Side,
|
||||
maker_amount: U256,
|
||||
taker_amount: U256,
|
||||
expiration: u64,
|
||||
) -> Result<SignedOrderRequest> {
|
||||
let seed = generate_seed();
|
||||
let timestamp = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("Time went backwards")
|
||||
.as_millis();
|
||||
|
||||
let order = SignedOrderMessage {
|
||||
salt: U256::from(seed),
|
||||
maker: self.builder.funder,
|
||||
signer: self.builder.signer_address,
|
||||
token_id: self.token_id_u256,
|
||||
maker_amount,
|
||||
taker_amount,
|
||||
side: side as u8,
|
||||
signature_type: self.builder.sig_type as u8,
|
||||
timestamp: U256::from(timestamp),
|
||||
metadata: self.metadata_bytes,
|
||||
builder: self.builder_bytes,
|
||||
};
|
||||
|
||||
let signature = sign_order_message_with_domain(&self.builder.signer, order, &self.domain)?;
|
||||
|
||||
Ok(SignedOrderRequest {
|
||||
salt: seed,
|
||||
maker: self.builder.funder_checksum.clone(),
|
||||
signer: self.builder.signer_checksum.clone(),
|
||||
token_id: self.token_id.clone(),
|
||||
maker_amount: maker_amount.to_string(),
|
||||
taker_amount: taker_amount.to_string(),
|
||||
expiration: expiration.to_string(),
|
||||
side: side.as_str().to_string(),
|
||||
signature_type: self.builder.sig_type as u8,
|
||||
timestamp: timestamp.to_string(),
|
||||
metadata: self.metadata.clone(),
|
||||
builder: self.builder_code.clone(),
|
||||
signature,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -766,6 +919,77 @@ mod tests {
|
||||
assert_eq!(order.metadata, BYTES32_ZERO);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prepared_order_path_creates_equivalent_limit_order_fields() {
|
||||
let builder = test_builder();
|
||||
let args = OrderArgs {
|
||||
token_id: "123456".to_string(),
|
||||
price: Decimal::from_str("0.45").unwrap(),
|
||||
size: Decimal::from_str("12.34").unwrap(),
|
||||
side: Side::BUY,
|
||||
expiration: Some(1_900_000_000),
|
||||
builder_code: Some(BYTES32_ZERO.to_string()),
|
||||
metadata: None,
|
||||
};
|
||||
let options = CreateOrderOptions {
|
||||
tick_size: Some(Decimal::from_str("0.01").unwrap()),
|
||||
neg_risk: Some(false),
|
||||
};
|
||||
|
||||
let order = builder.create_order(137, &args, &options).unwrap();
|
||||
let prepared = builder
|
||||
.prepare_order_path(
|
||||
137,
|
||||
args.token_id.clone(),
|
||||
options.tick_size.unwrap(),
|
||||
options.neg_risk.unwrap(),
|
||||
args.builder_code.as_deref(),
|
||||
args.metadata.as_deref(),
|
||||
)
|
||||
.unwrap();
|
||||
let prepared_order = prepared
|
||||
.create_limit_order(args.side, args.price, args.size, args.expiration)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(prepared_order.maker, order.maker);
|
||||
assert_eq!(prepared_order.signer, order.signer);
|
||||
assert_eq!(prepared_order.token_id, order.token_id);
|
||||
assert_eq!(prepared_order.maker_amount, order.maker_amount);
|
||||
assert_eq!(prepared_order.taker_amount, order.taker_amount);
|
||||
assert_eq!(prepared_order.expiration, order.expiration);
|
||||
assert_eq!(prepared_order.side, order.side);
|
||||
assert_eq!(prepared_order.signature_type, order.signature_type);
|
||||
assert_eq!(prepared_order.metadata, order.metadata);
|
||||
assert_eq!(prepared_order.builder, order.builder);
|
||||
assert!(prepared_order.signature.starts_with("0x"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prepared_market_order_rejects_unsupported_order_type() {
|
||||
let builder = test_builder();
|
||||
let prepared = builder
|
||||
.prepare_order_path(
|
||||
137,
|
||||
"123456",
|
||||
Decimal::from_str("0.01").unwrap(),
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let err = prepared
|
||||
.create_market_order(
|
||||
Side::BUY,
|
||||
Decimal::from_str("10").unwrap(),
|
||||
Decimal::from_str("0.45").unwrap(),
|
||||
OrderType::GTC,
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, PolyfillError::Validation { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_market_order_supports_fak() {
|
||||
let builder = test_builder();
|
||||
|
||||
Reference in New Issue
Block a user