feat(clob): support all order types for order posting

This commit is contained in:
Andrey Kuznetsov
2026-02-12 23:23:00 +00:00
parent d9627d8f6c
commit 6190a24e0c
2 changed files with 108 additions and 36 deletions
+90 -36
View File
@@ -1079,14 +1079,28 @@ impl ClobClient {
Ok(response.json::<Value>().await?)
}
/// Create and post an order in one call
pub async fn create_and_post_order(&self, order_args: &OrderArgs) -> Result<Value> {
/// Create and post an order in one call with an explicit order type
pub async fn create_and_post_order_with_type(
&self,
order_args: &OrderArgs,
order_type: OrderType,
) -> Result<Value> {
let order = self.create_order(order_args, None, None, None).await?;
self.post_order(order, OrderType::GTC).await
self.post_order(order, order_type).await
}
/// Create and post multiple orders in one call
pub async fn create_and_post_orders(&self, order_args: &[OrderArgs]) -> Result<Value> {
/// Create and post an order in one call (defaults to GTC)
pub async fn create_and_post_order(&self, order_args: &OrderArgs) -> Result<Value> {
self.create_and_post_order_with_type(order_args, OrderType::GTC)
.await
}
/// Create and post multiple orders in one call with an explicit order type
pub async fn create_and_post_orders_with_type(
&self,
order_args: &[OrderArgs],
order_type: OrderType,
) -> Result<Value> {
if order_args.is_empty() {
return Err(PolyfillError::validation("order_args cannot be empty"));
}
@@ -1096,7 +1110,13 @@ impl ClobClient {
orders.push(self.create_order(args, None, None, None).await?);
}
self.post_orders(orders, OrderType::GTC).await
self.post_orders(orders, order_type).await
}
/// Create and post multiple orders in one call (defaults to GTC)
pub async fn create_and_post_orders(&self, order_args: &[OrderArgs]) -> Result<Value> {
self.create_and_post_orders_with_type(order_args, OrderType::GTC)
.await
}
/// Cancel an order
@@ -2394,6 +2414,24 @@ mod tests {
)
}
fn sample_signed_order() -> crate::types::SignedOrderRequest {
crate::types::SignedOrderRequest {
salt: 1,
maker: "0x0000000000000000000000000000000000000000".to_string(),
signer: "0x0000000000000000000000000000000000000000".to_string(),
taker: "0x0000000000000000000000000000000000000000".to_string(),
token_id: "123".to_string(),
maker_amount: "100".to_string(),
taker_amount: "50".to_string(),
expiration: "0".to_string(),
nonce: "0".to_string(),
fee_rate_bps: "0".to_string(),
side: "BUY".to_string(),
signature_type: 0,
signature: "0xdeadbeef".to_string(),
}
}
#[tokio::test(flavor = "multi_thread")]
async fn test_client_creation() {
let client = create_test_client("https://test.example.com");
@@ -2947,21 +2985,7 @@ mod tests {
.await;
let client = create_test_client_with_l2_auth(&server.url());
let signed_order = crate::types::SignedOrderRequest {
salt: 1,
maker: "0x0000000000000000000000000000000000000000".to_string(),
signer: "0x0000000000000000000000000000000000000000".to_string(),
taker: "0x0000000000000000000000000000000000000000".to_string(),
token_id: "123".to_string(),
maker_amount: "100".to_string(),
taker_amount: "50".to_string(),
expiration: "0".to_string(),
nonce: "0".to_string(),
fee_rate_bps: "0".to_string(),
side: "BUY".to_string(),
signature_type: 0,
signature: "0xdeadbeef".to_string(),
};
let signed_order = sample_signed_order();
let result = client
.post_orders(
@@ -2986,27 +3010,57 @@ mod tests {
async fn test_post_orders_batch_too_many_validation() {
let client = create_test_client_with_l2_auth("https://test.example.com");
let orders = (0..16)
.map(|_| crate::types::SignedOrderRequest {
salt: 1,
maker: "0x0000000000000000000000000000000000000000".to_string(),
signer: "0x0000000000000000000000000000000000000000".to_string(),
taker: "0x0000000000000000000000000000000000000000".to_string(),
token_id: "123".to_string(),
maker_amount: "100".to_string(),
taker_amount: "50".to_string(),
expiration: "0".to_string(),
nonce: "0".to_string(),
fee_rate_bps: "0".to_string(),
side: "BUY".to_string(),
signature_type: 0,
signature: "0xdeadbeef".to_string(),
})
.map(|_| sample_signed_order())
.collect::<Vec<_>>();
let result = client.post_orders(orders, crate::types::OrderType::GTC).await;
assert!(matches!(result, Err(PolyfillError::Validation { .. })));
}
#[tokio::test(flavor = "multi_thread")]
async fn test_post_order_supports_fak() {
let mut server = Server::new_async().await;
let mock = server
.mock("POST", "/order")
.match_body(Matcher::Regex(r#""orderType":"FAK""#.to_string()))
.with_status(200)
.with_header("content-type", "application/json")
.with_body(r#"{"success":true,"orderID":"a"}"#)
.create_async()
.await;
let client = create_test_client_with_l2_auth(&server.url());
let result = client
.post_order(sample_signed_order(), crate::types::OrderType::FAK)
.await;
mock.assert_async().await;
assert!(result.is_ok());
assert_eq!(result.unwrap()["success"], true);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_post_orders_supports_fak() {
let mut server = Server::new_async().await;
let mock = server
.mock("POST", "/orders")
.match_body(Matcher::Regex(r#""orderType":"FAK""#.to_string()))
.with_status(200)
.with_header("content-type", "application/json")
.with_body(r#"[{"success":true,"orderID":"a"}]"#)
.create_async()
.await;
let client = create_test_client_with_l2_auth(&server.url());
let result = client
.post_orders(vec![sample_signed_order()], crate::types::OrderType::FAK)
.await;
mock.assert_async().await;
assert!(result.is_ok());
assert_eq!(result.unwrap()[0]["success"], true);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_create_or_derive_api_key() {
let mut server = Server::new_async().await;
+18
View File
@@ -214,6 +214,7 @@ pub enum OrderType {
#[default]
GTC,
FOK,
FAK,
GTD,
}
@@ -222,6 +223,7 @@ impl OrderType {
match self {
OrderType::GTC => "GTC",
OrderType::FOK => "FOK",
OrderType::FAK => "FAK",
OrderType::GTD => "GTD",
}
}
@@ -1657,3 +1659,19 @@ pub type Result<T> = std::result::Result<T, crate::errors::PolyfillError>;
pub type ApiCreds = ApiCredentials;
pub type CreateOrderOptions = OrderOptions;
pub type OrderArgs = OrderRequest;
#[cfg(test)]
mod tests {
use super::OrderType;
#[test]
fn test_order_type_fak_serde_and_string() {
assert_eq!(OrderType::FAK.as_str(), "FAK");
let json = serde_json::to_string(&OrderType::FAK).unwrap();
assert_eq!(json, "\"FAK\"");
let parsed: OrderType = serde_json::from_str("\"FAK\"").unwrap();
assert_eq!(parsed, OrderType::FAK);
}
}