1use std::fmt;
2
3use alloy::primitives::Address;
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7#[derive(Error, Debug, Clone, PartialEq)]
9#[error("invalid tick size: {0}. Valid values are 0.1, 0.01, 0.001, or 0.0001")]
10pub struct ParseTickSizeError(String);
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
13#[serde(rename_all = "UPPERCASE")]
14pub enum OrderSide {
15 Buy,
16 Sell,
17}
18
19impl Serialize for OrderSide {
20 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
21 where
22 S: serde::Serializer,
23 {
24 match self {
25 Self::Buy => serializer.serialize_str("BUY"),
26 Self::Sell => serializer.serialize_str("SELL"),
27 }
28 }
29}
30
31impl fmt::Display for OrderSide {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 match self {
34 Self::Buy => write!(f, "0"),
35 Self::Sell => write!(f, "1"),
36 }
37 }
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
42#[serde(rename_all = "UPPERCASE")]
43pub enum OrderKind {
44 Gtc,
46 Fok,
48 Gtd,
50 Fak,
52}
53
54impl fmt::Display for OrderKind {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 match self {
57 Self::Gtc => write!(f, "GTC"),
58 Self::Fok => write!(f, "FOK"),
59 Self::Gtd => write!(f, "GTD"),
60 Self::Fak => write!(f, "FAK"),
61 }
62 }
63}
64
65#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
68pub enum SignatureType {
69 #[default]
70 Eoa = 0,
71 PolyProxy = 1,
72 PolyGnosisSafe = 2,
73}
74
75impl SignatureType {
76 pub fn is_proxy(&self) -> bool {
78 matches!(self, Self::PolyProxy | Self::PolyGnosisSafe)
79 }
80}
81
82impl Serialize for SignatureType {
83 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
84 where
85 S: serde::Serializer,
86 {
87 serializer.serialize_u8(*self as u8)
88 }
89}
90
91impl<'de> Deserialize<'de> for SignatureType {
92 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
93 where
94 D: serde::Deserializer<'de>,
95 {
96 let v = u8::deserialize(deserializer)?;
97 match v {
98 0 => Ok(Self::Eoa),
99 1 => Ok(Self::PolyProxy),
100 2 => Ok(Self::PolyGnosisSafe),
101 _ => Err(serde::de::Error::custom(format!(
102 "invalid signature type: {}",
103 v
104 ))),
105 }
106 }
107}
108
109impl fmt::Display for SignatureType {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 match self {
112 Self::Eoa => write!(f, "eoa"),
113 Self::PolyProxy => write!(f, "poly-proxy"),
114 Self::PolyGnosisSafe => write!(f, "poly-gnosis-safe"),
115 }
116 }
117}
118
119#[derive(Debug, Clone, Copy, PartialEq)]
121pub enum TickSize {
122 Tenth,
124 Hundredth,
126 Thousandth,
128 TenThousandth,
130}
131
132impl TickSize {
133 pub fn as_f64(&self) -> f64 {
134 match self {
135 Self::Tenth => 0.1,
136 Self::Hundredth => 0.01,
137 Self::Thousandth => 0.001,
138 Self::TenThousandth => 0.0001,
139 }
140 }
141
142 pub fn decimals(&self) -> u32 {
143 match self {
144 Self::Tenth => 1,
145 Self::Hundredth => 2,
146 Self::Thousandth => 3,
147 Self::TenThousandth => 4,
148 }
149 }
150}
151
152#[derive(Debug, Clone, Copy, Default)]
154pub struct PartialCreateOrderOptions {
155 pub tick_size: Option<TickSize>,
156 pub neg_risk: Option<bool>,
157}
158
159impl TryFrom<&str> for TickSize {
160 type Error = ParseTickSizeError;
161
162 fn try_from(s: &str) -> Result<Self, Self::Error> {
163 match s {
164 "0.1" => Ok(Self::Tenth),
165 "0.01" => Ok(Self::Hundredth),
166 "0.001" => Ok(Self::Thousandth),
167 "0.0001" => Ok(Self::TenThousandth),
168 _ => Err(ParseTickSizeError(s.to_string())),
169 }
170 }
171}
172
173impl TryFrom<f64> for TickSize {
174 type Error = ParseTickSizeError;
175
176 fn try_from(n: f64) -> Result<Self, Self::Error> {
177 const EPSILON: f64 = 1e-10;
178 if (n - 0.1).abs() < EPSILON {
179 Ok(Self::Tenth)
180 } else if (n - 0.01).abs() < EPSILON {
181 Ok(Self::Hundredth)
182 } else if (n - 0.001).abs() < EPSILON {
183 Ok(Self::Thousandth)
184 } else if (n - 0.0001).abs() < EPSILON {
185 Ok(Self::TenThousandth)
186 } else {
187 Err(ParseTickSizeError(n.to_string()))
188 }
189 }
190}
191
192impl std::str::FromStr for TickSize {
193 type Err = ParseTickSizeError;
194
195 fn from_str(s: &str) -> Result<Self, Self::Err> {
196 Self::try_from(s)
197 }
198}
199
200fn serialize_salt<S>(salt: &str, serializer: S) -> Result<S::Ok, S::Error>
201where
202 S: serde::Serializer,
203{
204 let val = salt
206 .parse::<u128>()
207 .map_err(|_| serde::ser::Error::custom("invalid salt"))?;
208 serializer.serialize_u128(val)
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
213#[serde(rename_all = "camelCase")]
214pub struct Order {
215 #[serde(serialize_with = "serialize_salt")]
216 pub salt: String,
217 pub maker: Address,
218 pub signer: Address,
219 pub taker: Address,
220 pub token_id: String,
221 pub maker_amount: String,
222 pub taker_amount: String,
223 pub expiration: String,
224 pub nonce: String,
225 pub fee_rate_bps: String,
226 pub side: OrderSide,
227 pub signature_type: SignatureType,
228 #[serde(skip)]
229 pub neg_risk: bool,
230}
231
232#[derive(Debug, Clone)]
234pub struct MarketOrderArgs {
235 pub token_id: String,
236 pub amount: f64,
239 pub side: OrderSide,
240 pub price: Option<f64>,
243 pub fee_rate_bps: Option<u16>,
244 pub nonce: Option<u64>,
245 pub funder: Option<Address>,
246 pub signature_type: Option<SignatureType>,
247 pub order_type: Option<OrderKind>,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
252#[serde(rename_all = "camelCase")]
253pub struct SignedOrder {
254 #[serde(flatten)]
255 pub order: Order,
256 pub signature: String,
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262 use alloy::primitives::Address;
263 use std::str::FromStr;
264
265 #[test]
266 fn test_order_serialization() {
267 let order = Order {
268 salt: "123".to_string(),
269 maker: Address::from_str("0x0000000000000000000000000000000000000001").unwrap(),
270 signer: Address::from_str("0x0000000000000000000000000000000000000002").unwrap(),
271 taker: Address::ZERO,
272 token_id: "456".to_string(),
273 maker_amount: "1000".to_string(),
274 taker_amount: "2000".to_string(),
275 expiration: "0".to_string(),
276 nonce: "789".to_string(),
277 fee_rate_bps: "0".to_string(),
278 side: OrderSide::Buy,
279 signature_type: SignatureType::Eoa,
280 neg_risk: false,
281 };
282
283 let signed_order = SignedOrder {
284 order,
285 signature: "0xabc".to_string(),
286 };
287
288 let json = serde_json::to_value(&signed_order).unwrap();
289
290 assert!(json.get("makerAmount").is_some());
292 assert!(json.get("takerAmount").is_some());
293 assert!(json.get("tokenId").is_some());
294 assert!(json.get("feeRateBps").is_some());
295 assert!(json.get("signatureType").is_some());
296
297 assert!(json.get("signature").is_some());
299 assert!(json.get("salt").is_some());
300
301 assert_eq!(json["makerAmount"], "1000");
303 assert_eq!(json["side"], "BUY");
304 assert_eq!(json["signatureType"], 0);
305 assert_eq!(json["nonce"], "789");
306 }
307}