cow_types/
unsigned_order.rs1use std::fmt;
13
14use alloy_primitives::{Address, B256, U256};
15
16use crate::{OrderKind, TokenBalance};
17
18#[derive(Debug, Clone)]
20pub struct UnsignedOrder {
21 pub sell_token: Address,
23 pub buy_token: Address,
25 pub receiver: Address,
27 pub sell_amount: U256,
29 pub buy_amount: U256,
31 pub valid_to: u32,
33 pub app_data: B256,
35 pub fee_amount: U256,
37 pub kind: OrderKind,
39 pub partially_fillable: bool,
41 pub sell_token_balance: TokenBalance,
43 pub buy_token_balance: TokenBalance,
45}
46
47impl UnsignedOrder {
48 #[must_use]
64 pub const fn sell(
65 sell_token: Address,
66 buy_token: Address,
67 sell_amount: U256,
68 buy_amount: U256,
69 ) -> Self {
70 Self {
71 sell_token,
72 buy_token,
73 receiver: Address::ZERO,
74 sell_amount,
75 buy_amount,
76 valid_to: 0,
77 app_data: B256::ZERO,
78 fee_amount: U256::ZERO,
79 kind: OrderKind::Sell,
80 partially_fillable: false,
81 sell_token_balance: TokenBalance::Erc20,
82 buy_token_balance: TokenBalance::Erc20,
83 }
84 }
85
86 #[must_use]
100 pub const fn buy(
101 sell_token: Address,
102 buy_token: Address,
103 sell_amount: U256,
104 buy_amount: U256,
105 ) -> Self {
106 Self {
107 sell_token,
108 buy_token,
109 receiver: Address::ZERO,
110 sell_amount,
111 buy_amount,
112 valid_to: 0,
113 app_data: B256::ZERO,
114 fee_amount: U256::ZERO,
115 kind: OrderKind::Buy,
116 partially_fillable: false,
117 sell_token_balance: TokenBalance::Erc20,
118 buy_token_balance: TokenBalance::Erc20,
119 }
120 }
121
122 #[must_use]
124 pub const fn with_receiver(mut self, receiver: Address) -> Self {
125 self.receiver = receiver;
126 self
127 }
128
129 #[must_use]
131 pub const fn with_valid_to(mut self, valid_to: u32) -> Self {
132 self.valid_to = valid_to;
133 self
134 }
135
136 #[must_use]
138 pub const fn with_app_data(mut self, app_data: B256) -> Self {
139 self.app_data = app_data;
140 self
141 }
142
143 #[must_use]
145 pub const fn with_fee_amount(mut self, fee_amount: U256) -> Self {
146 self.fee_amount = fee_amount;
147 self
148 }
149
150 #[must_use]
152 pub const fn with_partially_fillable(mut self) -> Self {
153 self.partially_fillable = true;
154 self
155 }
156
157 #[must_use]
159 pub const fn with_sell_token_balance(mut self, balance: TokenBalance) -> Self {
160 self.sell_token_balance = balance;
161 self
162 }
163
164 #[must_use]
166 pub const fn with_buy_token_balance(mut self, balance: TokenBalance) -> Self {
167 self.buy_token_balance = balance;
168 self
169 }
170
171 #[must_use]
186 pub const fn is_expired(&self, timestamp: u64) -> bool {
187 timestamp > self.valid_to as u64
188 }
189
190 #[must_use]
192 pub const fn is_sell(&self) -> bool {
193 self.kind.is_sell()
194 }
195
196 #[must_use]
198 pub const fn is_buy(&self) -> bool {
199 self.kind.is_buy()
200 }
201
202 #[must_use]
204 pub fn has_custom_receiver(&self) -> bool {
205 !self.receiver.is_zero()
206 }
207
208 #[must_use]
210 pub fn has_app_data(&self) -> bool {
211 !self.app_data.is_zero()
212 }
213
214 #[must_use]
216 pub fn has_fee(&self) -> bool {
217 !self.fee_amount.is_zero()
218 }
219
220 #[must_use]
222 pub const fn is_partially_fillable(&self) -> bool {
223 self.partially_fillable
224 }
225
226 #[must_use]
230 pub const fn total_amount(&self) -> U256 {
231 self.sell_amount.saturating_add(self.buy_amount)
232 }
233}
234
235impl fmt::Display for UnsignedOrder {
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 write!(f, "{} {:#x} → {:#x}", self.kind, self.sell_token, self.buy_token)
238 }
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244
245 fn base() -> UnsignedOrder {
246 UnsignedOrder::sell(Address::ZERO, Address::ZERO, U256::from(1_000u64), U256::from(500u64))
247 }
248
249 #[test]
250 fn with_partially_fillable_sets_flag() {
251 let order = base().with_partially_fillable();
252 assert!(order.is_partially_fillable());
253 }
254
255 #[test]
256 fn with_sell_token_balance_overrides_default() {
257 let order = base().with_sell_token_balance(TokenBalance::External);
258 assert_eq!(order.sell_token_balance, TokenBalance::External);
259 }
260
261 #[test]
262 fn with_buy_token_balance_overrides_default() {
263 let order = base().with_buy_token_balance(TokenBalance::Internal);
264 assert_eq!(order.buy_token_balance, TokenBalance::Internal);
265 }
266
267 #[test]
268 fn total_amount_saturates_on_overflow() {
269 let order = UnsignedOrder::sell(Address::ZERO, Address::ZERO, U256::MAX, U256::from(1u64));
271 assert_eq!(order.total_amount(), U256::MAX);
272 }
273
274 #[test]
275 fn total_amount_sums_sell_and_buy() {
276 let order =
277 UnsignedOrder::sell(Address::ZERO, Address::ZERO, U256::from(7u64), U256::from(11u64));
278 assert_eq!(order.total_amount(), U256::from(18u64));
279 }
280
281 #[test]
282 fn display_renders_kind_and_token_addresses() {
283 let order = base();
284 let rendered = format!("{order}");
285 assert!(rendered.contains("sell"));
286 assert!(rendered.contains("0x0000000000000000000000000000000000000000"));
287 }
288}