1use std::fmt;
4
5use alloy_primitives::{Address, U256};
6use cow_orderbook::types::OrderQuoteResponse;
7use cow_signing::types::{OrderTypedData, UnsignedOrder};
8use cow_types::OrderKind;
9
10#[derive(Debug, Clone, Copy, Default)]
12pub struct Amounts {
13 pub sell_amount: U256,
15 pub buy_amount: U256,
17}
18
19impl Amounts {
20 #[must_use]
31 pub const fn new(sell_amount: U256, buy_amount: U256) -> Self {
32 Self { sell_amount, buy_amount }
33 }
34
35 #[must_use]
41 pub fn is_zero(&self) -> bool {
42 self.sell_amount.is_zero() && self.buy_amount.is_zero()
43 }
44
45 #[must_use]
55 pub const fn total(&self) -> U256 {
56 self.sell_amount.saturating_add(self.buy_amount)
57 }
58}
59
60impl fmt::Display for Amounts {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 write!(f, "sell {} → buy {}", self.sell_amount, self.buy_amount)
63 }
64}
65
66#[derive(Debug, Clone, Copy, Default)]
68pub struct NetworkFee {
69 pub amount_in_sell_currency: U256,
71 pub amount_in_buy_currency: U256,
73}
74
75impl NetworkFee {
76 #[must_use]
87 pub const fn new(amount_in_sell_currency: U256, amount_in_buy_currency: U256) -> Self {
88 Self { amount_in_sell_currency, amount_in_buy_currency }
89 }
90
91 #[must_use]
97 pub fn is_zero(&self) -> bool {
98 self.amount_in_sell_currency.is_zero() && self.amount_in_buy_currency.is_zero()
99 }
100
101 #[must_use]
107 pub const fn total_atoms(&self) -> U256 {
108 self.amount_in_sell_currency.saturating_add(self.amount_in_buy_currency)
109 }
110}
111
112impl fmt::Display for NetworkFee {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 write!(
115 f,
116 "network-fee sell={} buy={}",
117 self.amount_in_sell_currency, self.amount_in_buy_currency,
118 )
119 }
120}
121
122#[derive(Debug, Clone, Copy, Default)]
126pub struct PartnerFeeCost {
127 pub amount: U256,
129 pub bps: u32,
131}
132
133impl PartnerFeeCost {
134 #[must_use]
145 pub const fn new(amount: U256, bps: u32) -> Self {
146 Self { amount, bps }
147 }
148
149 #[must_use]
155 pub fn is_zero(&self) -> bool {
156 self.amount.is_zero() && self.bps == 0
157 }
158
159 #[must_use]
165 pub const fn has_bps(&self) -> bool {
166 self.bps > 0
167 }
168}
169
170impl fmt::Display for PartnerFeeCost {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 write!(f, "partner-fee {}bps {}", self.bps, self.amount)
173 }
174}
175
176#[derive(Debug, Clone, Copy, Default)]
180pub struct ProtocolFeeCost {
181 pub amount: U256,
183 pub bps: u32,
185}
186
187impl ProtocolFeeCost {
188 #[must_use]
199 pub const fn new(amount: U256, bps: u32) -> Self {
200 Self { amount, bps }
201 }
202
203 #[must_use]
209 pub fn is_zero(&self) -> bool {
210 self.amount.is_zero() && self.bps == 0
211 }
212
213 #[must_use]
219 pub const fn has_bps(&self) -> bool {
220 self.bps > 0
221 }
222}
223
224impl fmt::Display for ProtocolFeeCost {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 write!(f, "protocol-fee {}bps {}", self.bps, self.amount)
227 }
228}
229
230#[derive(Debug, Clone, Copy)]
233pub struct QuoteAmountsAndCosts {
234 pub is_sell: bool,
236 pub before_all_fees: Amounts,
238 pub before_network_costs: Amounts,
240 pub after_network_costs: Amounts,
242 pub after_partner_fees: Amounts,
244 pub after_slippage: Amounts,
246 pub network_fee: NetworkFee,
248 pub partner_fee: PartnerFeeCost,
250 pub protocol_fee: ProtocolFeeCost,
252}
253
254#[derive(Debug, Clone)]
258pub struct TradingAppDataInfo {
259 pub full_app_data: String,
261 pub app_data_keccak256: String,
263}
264
265impl TradingAppDataInfo {
266 #[must_use]
277 pub fn new(full_app_data: impl Into<String>, app_data_keccak256: impl Into<String>) -> Self {
278 Self { full_app_data: full_app_data.into(), app_data_keccak256: app_data_keccak256.into() }
279 }
280
281 #[must_use]
287 pub const fn has_full_app_data(&self) -> bool {
288 !self.full_app_data.is_empty()
289 }
290
291 #[must_use]
304 pub fn full_app_data_ref(&self) -> &str {
305 &self.full_app_data
306 }
307
308 #[must_use]
321 pub fn keccak256_ref(&self) -> &str {
322 &self.app_data_keccak256
323 }
324}
325
326impl fmt::Display for TradingAppDataInfo {
327 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328 write!(f, "app-data({})", self.app_data_keccak256)
329 }
330}
331
332#[derive(Debug, Clone)]
338pub struct TradingTransactionParams {
339 pub data: Vec<u8>,
341 pub to: Address,
343 pub gas_limit: u64,
345 pub value: U256,
347}
348
349impl TradingTransactionParams {
350 #[must_use]
363 pub const fn new(data: Vec<u8>, to: Address, gas_limit: u64, value: U256) -> Self {
364 Self { data, to, gas_limit, value }
365 }
366
367 #[must_use]
377 pub fn with_data(mut self, data: Vec<u8>) -> Self {
378 self.data = data;
379 self
380 }
381
382 #[must_use]
392 pub const fn with_to(mut self, to: Address) -> Self {
393 self.to = to;
394 self
395 }
396
397 #[must_use]
407 pub const fn with_gas_limit(mut self, gas_limit: u64) -> Self {
408 self.gas_limit = gas_limit;
409 self
410 }
411
412 #[must_use]
422 pub const fn with_value(mut self, value: U256) -> Self {
423 self.value = value;
424 self
425 }
426
427 #[must_use]
433 pub const fn data_len(&self) -> usize {
434 self.data.len()
435 }
436
437 #[must_use]
443 pub fn has_value(&self) -> bool {
444 !self.value.is_zero()
445 }
446}
447
448impl fmt::Display for TradingTransactionParams {
449 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
450 write!(f, "tx to={:#x} gas={}", self.to, self.gas_limit)
451 }
452}
453
454#[derive(Debug, Clone, Default)]
459pub struct PostTradeAdditionalParams {
460 pub signing_scheme: Option<cow_types::SigningScheme>,
467 pub network_costs_amount: Option<String>,
472 pub apply_costs_slippage_and_fees: Option<bool>,
477 pub protocol_fee_bps: Option<f64>,
486}
487
488impl PostTradeAdditionalParams {
489 #[must_use]
499 pub const fn with_signing_scheme(mut self, scheme: cow_types::SigningScheme) -> Self {
500 self.signing_scheme = Some(scheme);
501 self
502 }
503
504 #[must_use]
514 pub fn with_network_costs_amount(mut self, amount: impl Into<String>) -> Self {
515 self.network_costs_amount = Some(amount.into());
516 self
517 }
518
519 #[must_use]
529 pub const fn with_apply_costs_slippage_and_fees(mut self, apply: bool) -> Self {
530 self.apply_costs_slippage_and_fees = Some(apply);
531 self
532 }
533
534 #[must_use]
540 pub const fn has_signing_scheme(&self) -> bool {
541 self.signing_scheme.is_some()
542 }
543
544 #[must_use]
550 pub const fn has_network_costs(&self) -> bool {
551 self.network_costs_amount.is_some()
552 }
553
554 #[must_use]
560 pub const fn should_apply_costs(&self) -> bool {
561 matches!(self.apply_costs_slippage_and_fees, Some(true))
562 }
563
564 #[must_use]
574 pub const fn with_protocol_fee_bps(mut self, bps: f64) -> Self {
575 self.protocol_fee_bps = Some(bps);
576 self
577 }
578
579 #[must_use]
581 pub const fn has_protocol_fee_bps(&self) -> bool {
582 self.protocol_fee_bps.is_some()
583 }
584}
585
586impl fmt::Display for PostTradeAdditionalParams {
587 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
588 f.write_str("post-trade-params")
589 }
590}
591
592#[derive(Debug, Clone, Default)]
596pub struct SwapAdvancedSettings {
597 pub app_data: Option<serde_json::Value>,
599 pub slippage_bps: Option<u32>,
604 pub partner_fee: Option<cow_app_data::types::PartnerFee>,
609}
610
611impl SwapAdvancedSettings {
612 #[must_use]
622 pub fn with_app_data(mut self, app_data: serde_json::Value) -> Self {
623 self.app_data = Some(app_data);
624 self
625 }
626
627 #[must_use]
637 pub const fn with_slippage_bps(mut self, bps: u32) -> Self {
638 self.slippage_bps = Some(bps);
639 self
640 }
641
642 #[must_use]
652 pub fn with_partner_fee(mut self, fee: cow_app_data::types::PartnerFee) -> Self {
653 self.partner_fee = Some(fee);
654 self
655 }
656
657 #[must_use]
663 pub const fn has_app_data(&self) -> bool {
664 self.app_data.is_some()
665 }
666
667 #[must_use]
673 pub const fn has_slippage_bps(&self) -> bool {
674 self.slippage_bps.is_some()
675 }
676
677 #[must_use]
683 pub const fn has_partner_fee(&self) -> bool {
684 self.partner_fee.is_some()
685 }
686}
687
688impl fmt::Display for SwapAdvancedSettings {
689 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
690 f.write_str("swap-settings")
691 }
692}
693
694#[derive(Debug, Clone, Default)]
701pub struct LimitOrderAdvancedSettings {
702 pub receiver: Option<Address>,
704 pub valid_to: Option<u32>,
706 pub partner_fee: Option<cow_app_data::types::PartnerFee>,
708 pub partially_fillable: Option<bool>,
710 pub app_data: Option<String>,
712}
713
714impl LimitOrderAdvancedSettings {
715 #[must_use]
725 pub const fn with_receiver(mut self, receiver: Address) -> Self {
726 self.receiver = Some(receiver);
727 self
728 }
729
730 #[must_use]
740 pub const fn with_valid_to(mut self, valid_to: u32) -> Self {
741 self.valid_to = Some(valid_to);
742 self
743 }
744
745 #[must_use]
755 pub fn with_partner_fee(mut self, fee: cow_app_data::types::PartnerFee) -> Self {
756 self.partner_fee = Some(fee);
757 self
758 }
759
760 #[must_use]
770 pub const fn with_partially_fillable(mut self, partially_fillable: bool) -> Self {
771 self.partially_fillable = Some(partially_fillable);
772 self
773 }
774
775 #[must_use]
785 pub fn with_app_data(mut self, app_data: impl Into<String>) -> Self {
786 self.app_data = Some(app_data.into());
787 self
788 }
789
790 #[must_use]
796 pub const fn has_receiver(&self) -> bool {
797 self.receiver.is_some()
798 }
799
800 #[must_use]
806 pub const fn has_valid_to(&self) -> bool {
807 self.valid_to.is_some()
808 }
809
810 #[must_use]
816 pub const fn has_partner_fee(&self) -> bool {
817 self.partner_fee.is_some()
818 }
819
820 #[must_use]
826 pub const fn has_partially_fillable(&self) -> bool {
827 self.partially_fillable.is_some()
828 }
829
830 #[must_use]
836 pub const fn has_app_data(&self) -> bool {
837 self.app_data.is_some()
838 }
839}
840
841impl fmt::Display for LimitOrderAdvancedSettings {
842 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
843 f.write_str("limit-settings")
844 }
845}
846
847#[must_use]
897pub fn apply_settings_to_limit_trade_parameters(
898 mut params: LimitTradeParameters,
899 settings: Option<&LimitOrderAdvancedSettings>,
900) -> LimitTradeParameters {
901 let Some(s) = settings else {
902 return params;
903 };
904 if s.receiver.is_some() {
905 params.receiver = s.receiver;
906 }
907 if s.valid_to.is_some() {
908 params.valid_to = s.valid_to;
909 }
910 if s.partner_fee.is_some() {
911 params.partner_fee = s.partner_fee.clone();
912 }
913 if let Some(pf) = s.partially_fillable {
914 params.partially_fillable = pf;
915 }
916 if s.app_data.is_some() {
917 params.app_data = s.app_data.clone();
918 }
919 params
920}
921
922#[derive(Debug, Clone)]
928pub struct LimitTradeParametersFromQuote {
929 pub sell_token: Address,
931 pub buy_token: Address,
933 pub sell_amount: U256,
935 pub buy_amount: U256,
937 pub quote_id: Option<i64>,
939}
940
941impl LimitTradeParametersFromQuote {
942 #[must_use]
955 pub const fn new(
956 sell_token: Address,
957 buy_token: Address,
958 sell_amount: U256,
959 buy_amount: U256,
960 ) -> Self {
961 Self { sell_token, buy_token, sell_amount, buy_amount, quote_id: None }
962 }
963
964 #[must_use]
974 pub const fn with_quote_id(mut self, quote_id: i64) -> Self {
975 self.quote_id = Some(quote_id);
976 self
977 }
978
979 #[must_use]
985 pub const fn has_quote_id(&self) -> bool {
986 self.quote_id.is_some()
987 }
988}
989
990impl fmt::Display for LimitTradeParametersFromQuote {
991 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
992 write!(
993 f,
994 "limit-from-quote {:#x} sell={} buy\u{2265}{}",
995 self.sell_token, self.sell_amount, self.buy_amount
996 )
997 }
998}
999
1000#[derive(Debug, Clone)]
1002pub struct TradeParameters {
1003 pub kind: OrderKind,
1005 pub sell_token: Address,
1007 pub sell_token_decimals: u8,
1009 pub buy_token: Address,
1011 pub buy_token_decimals: u8,
1013 pub amount: U256,
1015 pub slippage_bps: Option<u32>,
1017 pub receiver: Option<Address>,
1019 pub valid_for: Option<u32>,
1023 pub valid_to: Option<u32>,
1027 pub partially_fillable: Option<bool>,
1031 pub partner_fee: Option<cow_app_data::types::PartnerFee>,
1036}
1037
1038impl TradeParameters {
1039 #[must_use]
1053 pub const fn sell(
1054 sell_token: Address,
1055 sell_token_decimals: u8,
1056 buy_token: Address,
1057 buy_token_decimals: u8,
1058 amount: U256,
1059 ) -> Self {
1060 Self {
1061 kind: OrderKind::Sell,
1062 sell_token,
1063 sell_token_decimals,
1064 buy_token,
1065 buy_token_decimals,
1066 amount,
1067 slippage_bps: None,
1068 receiver: None,
1069 valid_for: None,
1070 valid_to: None,
1071 partially_fillable: None,
1072 partner_fee: None,
1073 }
1074 }
1075
1076 #[must_use]
1090 pub const fn buy(
1091 sell_token: Address,
1092 sell_token_decimals: u8,
1093 buy_token: Address,
1094 buy_token_decimals: u8,
1095 amount: U256,
1096 ) -> Self {
1097 Self {
1098 kind: OrderKind::Buy,
1099 sell_token,
1100 sell_token_decimals,
1101 buy_token,
1102 buy_token_decimals,
1103 amount,
1104 slippage_bps: None,
1105 receiver: None,
1106 valid_for: None,
1107 valid_to: None,
1108 partially_fillable: None,
1109 partner_fee: None,
1110 }
1111 }
1112
1113 #[must_use]
1123 pub const fn with_slippage_bps(mut self, bps: u32) -> Self {
1124 self.slippage_bps = Some(bps);
1125 self
1126 }
1127
1128 #[must_use]
1138 pub const fn with_receiver(mut self, receiver: Address) -> Self {
1139 self.receiver = Some(receiver);
1140 self
1141 }
1142
1143 #[must_use]
1153 pub const fn with_valid_for(mut self, secs: u32) -> Self {
1154 self.valid_for = Some(secs);
1155 self
1156 }
1157
1158 #[must_use]
1168 pub const fn with_valid_to(mut self, ts: u32) -> Self {
1169 self.valid_to = Some(ts);
1170 self
1171 }
1172
1173 #[must_use]
1179 pub const fn with_partially_fillable(mut self) -> Self {
1180 self.partially_fillable = Some(true);
1181 self
1182 }
1183
1184 #[must_use]
1190 pub const fn is_sell(&self) -> bool {
1191 self.kind.is_sell()
1192 }
1193
1194 #[must_use]
1200 pub const fn is_buy(&self) -> bool {
1201 self.kind.is_buy()
1202 }
1203
1204 #[must_use]
1210 pub const fn has_slippage_bps(&self) -> bool {
1211 self.slippage_bps.is_some()
1212 }
1213
1214 #[must_use]
1220 pub const fn has_receiver(&self) -> bool {
1221 self.receiver.is_some()
1222 }
1223
1224 #[must_use]
1230 pub const fn has_partner_fee(&self) -> bool {
1231 self.partner_fee.is_some()
1232 }
1233}
1234
1235impl fmt::Display for TradeParameters {
1236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1237 write!(
1238 f,
1239 "{} {:#x} \u{2192} {:#x} amt={}",
1240 self.kind, self.sell_token, self.buy_token, self.amount
1241 )
1242 }
1243}
1244
1245impl LimitTradeParameters {
1246 #[must_use]
1259 pub const fn sell(
1260 sell_token: Address,
1261 buy_token: Address,
1262 sell_amount: U256,
1263 buy_amount: U256,
1264 ) -> Self {
1265 Self {
1266 kind: cow_types::OrderKind::Sell,
1267 sell_token,
1268 buy_token,
1269 sell_amount,
1270 buy_amount,
1271 receiver: None,
1272 valid_for: None,
1273 valid_to: None,
1274 partially_fillable: false,
1275 app_data: None,
1276 partner_fee: None,
1277 }
1278 }
1279
1280 #[must_use]
1293 pub const fn buy(
1294 sell_token: Address,
1295 buy_token: Address,
1296 sell_amount: U256,
1297 buy_amount: U256,
1298 ) -> Self {
1299 Self {
1300 kind: cow_types::OrderKind::Buy,
1301 sell_token,
1302 buy_token,
1303 sell_amount,
1304 buy_amount,
1305 receiver: None,
1306 valid_for: None,
1307 valid_to: None,
1308 partially_fillable: false,
1309 app_data: None,
1310 partner_fee: None,
1311 }
1312 }
1313
1314 #[must_use]
1324 pub const fn with_receiver(mut self, receiver: Address) -> Self {
1325 self.receiver = Some(receiver);
1326 self
1327 }
1328
1329 #[must_use]
1339 pub const fn with_valid_for(mut self, secs: u32) -> Self {
1340 self.valid_for = Some(secs);
1341 self
1342 }
1343
1344 #[must_use]
1354 pub const fn with_valid_to(mut self, ts: u32) -> Self {
1355 self.valid_to = Some(ts);
1356 self
1357 }
1358
1359 #[must_use]
1365 pub const fn with_partially_fillable(mut self) -> Self {
1366 self.partially_fillable = true;
1367 self
1368 }
1369
1370 #[must_use]
1376 pub const fn is_sell(&self) -> bool {
1377 self.kind.is_sell()
1378 }
1379
1380 #[must_use]
1386 pub const fn is_buy(&self) -> bool {
1387 self.kind.is_buy()
1388 }
1389
1390 #[must_use]
1396 pub const fn has_receiver(&self) -> bool {
1397 self.receiver.is_some()
1398 }
1399
1400 #[must_use]
1406 pub const fn has_valid_to(&self) -> bool {
1407 self.valid_to.is_some()
1408 }
1409
1410 #[must_use]
1416 pub const fn has_valid_for(&self) -> bool {
1417 self.valid_for.is_some()
1418 }
1419
1420 #[must_use]
1426 pub const fn has_app_data(&self) -> bool {
1427 self.app_data.is_some()
1428 }
1429
1430 #[must_use]
1436 pub const fn has_partner_fee(&self) -> bool {
1437 self.partner_fee.is_some()
1438 }
1439}
1440
1441impl fmt::Display for LimitTradeParameters {
1442 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1443 write!(
1444 f,
1445 "limit {} {:#x} sell={} buy\u{2265}{}",
1446 self.kind, self.sell_token, self.sell_amount, self.buy_amount
1447 )
1448 }
1449}
1450
1451impl QuoteAmountsAndCosts {
1452 #[must_use]
1460 pub const fn is_buy(&self) -> bool {
1461 !self.is_sell
1462 }
1463
1464 #[must_use]
1475 pub const fn max_slippage_atoms(&self) -> U256 {
1476 self.after_partner_fees.buy_amount.saturating_sub(self.after_slippage.buy_amount)
1477 }
1478
1479 #[must_use]
1485 pub const fn total_fees_atoms(&self) -> U256 {
1486 self.network_fee
1487 .amount_in_sell_currency
1488 .saturating_add(self.partner_fee.amount)
1489 .saturating_add(self.protocol_fee.amount)
1490 }
1491
1492 #[must_use]
1498 pub fn has_network_fee(&self) -> bool {
1499 !self.network_fee.is_zero()
1500 }
1501
1502 #[must_use]
1508 pub fn has_partner_fee(&self) -> bool {
1509 !self.partner_fee.is_zero()
1510 }
1511
1512 #[must_use]
1518 pub fn has_protocol_fee(&self) -> bool {
1519 !self.protocol_fee.is_zero()
1520 }
1521}
1522
1523impl fmt::Display for QuoteAmountsAndCosts {
1524 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1525 let dir = if self.is_sell { "sell" } else { "buy" };
1526 write!(
1527 f,
1528 "{dir} gross={} after-slippage={} [{} / {} / {}]",
1529 self.before_all_fees,
1530 self.after_slippage,
1531 self.network_fee,
1532 self.partner_fee,
1533 self.protocol_fee,
1534 )
1535 }
1536}
1537
1538#[must_use]
1593pub fn map_quote_amounts_and_costs<F>(
1594 costs: &QuoteAmountsAndCosts,
1595 mut f: F,
1596) -> QuoteAmountsAndCosts
1597where
1598 F: FnMut(U256) -> U256,
1599{
1600 QuoteAmountsAndCosts {
1601 is_sell: costs.is_sell,
1602 before_all_fees: Amounts {
1603 sell_amount: f(costs.before_all_fees.sell_amount),
1604 buy_amount: f(costs.before_all_fees.buy_amount),
1605 },
1606 before_network_costs: Amounts {
1607 sell_amount: f(costs.before_network_costs.sell_amount),
1608 buy_amount: f(costs.before_network_costs.buy_amount),
1609 },
1610 after_network_costs: Amounts {
1611 sell_amount: f(costs.after_network_costs.sell_amount),
1612 buy_amount: f(costs.after_network_costs.buy_amount),
1613 },
1614 after_partner_fees: Amounts {
1615 sell_amount: f(costs.after_partner_fees.sell_amount),
1616 buy_amount: f(costs.after_partner_fees.buy_amount),
1617 },
1618 after_slippage: Amounts {
1619 sell_amount: f(costs.after_slippage.sell_amount),
1620 buy_amount: f(costs.after_slippage.buy_amount),
1621 },
1622 network_fee: NetworkFee {
1623 amount_in_sell_currency: f(costs.network_fee.amount_in_sell_currency),
1624 amount_in_buy_currency: f(costs.network_fee.amount_in_buy_currency),
1625 },
1626 partner_fee: PartnerFeeCost {
1627 amount: f(costs.partner_fee.amount),
1628 bps: costs.partner_fee.bps,
1629 },
1630 protocol_fee: ProtocolFeeCost {
1631 amount: f(costs.protocol_fee.amount),
1632 bps: costs.protocol_fee.bps,
1633 },
1634 }
1635}
1636
1637#[derive(Debug, Clone)]
1643pub struct LimitTradeParameters {
1644 pub kind: cow_types::OrderKind,
1646 pub sell_token: Address,
1648 pub buy_token: Address,
1650 pub sell_amount: U256,
1652 pub buy_amount: U256,
1654 pub receiver: Option<Address>,
1656 pub valid_for: Option<u32>,
1660 pub valid_to: Option<u32>,
1664 pub partially_fillable: bool,
1666 pub app_data: Option<String>,
1669 pub partner_fee: Option<cow_app_data::types::PartnerFee>,
1673}
1674
1675#[derive(Debug, Clone)]
1680pub struct OrderPostingResult {
1681 pub order_id: String,
1683 pub signing_scheme: cow_types::SigningScheme,
1685 pub signature: String,
1687 pub order_to_sign: UnsignedOrder,
1689}
1690
1691impl OrderPostingResult {
1692 #[must_use]
1705 pub fn new(
1706 order_id: impl Into<String>,
1707 signing_scheme: cow_types::SigningScheme,
1708 signature: impl Into<String>,
1709 order_to_sign: UnsignedOrder,
1710 ) -> Self {
1711 Self {
1712 order_id: order_id.into(),
1713 signing_scheme,
1714 signature: signature.into(),
1715 order_to_sign,
1716 }
1717 }
1718
1719 #[must_use]
1726 pub const fn is_eip712(&self) -> bool {
1727 matches!(self.signing_scheme, cow_types::SigningScheme::Eip712)
1728 }
1729
1730 #[must_use]
1737 pub const fn is_eth_sign(&self) -> bool {
1738 matches!(self.signing_scheme, cow_types::SigningScheme::EthSign)
1739 }
1740
1741 #[must_use]
1748 pub const fn is_eip1271(&self) -> bool {
1749 matches!(self.signing_scheme, cow_types::SigningScheme::Eip1271)
1750 }
1751
1752 #[must_use]
1759 pub const fn is_presign(&self) -> bool {
1760 matches!(self.signing_scheme, cow_types::SigningScheme::PreSign)
1761 }
1762
1763 #[must_use]
1769 pub fn order_id_ref(&self) -> &str {
1770 &self.order_id
1771 }
1772
1773 #[must_use]
1779 pub fn signature_ref(&self) -> &str {
1780 &self.signature
1781 }
1782}
1783
1784impl fmt::Display for OrderPostingResult {
1785 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1786 write!(f, "order({})", self.order_id)
1787 }
1788}
1789
1790impl fmt::Display for QuoteResults {
1791 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1792 write!(f, "quote slippage={}bps {}", self.suggested_slippage_bps, self.amounts_and_costs)
1793 }
1794}
1795
1796#[derive(Debug, Clone)]
1798pub struct QuoteResults {
1799 pub order_to_sign: UnsignedOrder,
1801 pub order_typed_data: OrderTypedData,
1807 pub quote_response: OrderQuoteResponse,
1809 pub amounts_and_costs: QuoteAmountsAndCosts,
1811 pub suggested_slippage_bps: u32,
1813 pub app_data_info: TradingAppDataInfo,
1815}
1816
1817impl QuoteResults {
1818 #[must_use]
1824 pub const fn order_ref(&self) -> &UnsignedOrder {
1825 &self.order_to_sign
1826 }
1827
1828 #[must_use]
1834 pub const fn quote_ref(&self) -> &OrderQuoteResponse {
1835 &self.quote_response
1836 }
1837}
1838
1839#[derive(Debug, Clone)]
1845pub struct BuildAppDataParams {
1846 pub app_code: String,
1848 pub slippage_bps: u32,
1850 pub order_class: cow_app_data::types::OrderClassKind,
1852 pub partner_fee: Option<cow_app_data::types::PartnerFee>,
1854}
1855
1856impl BuildAppDataParams {
1857 #[must_use]
1869 pub fn new(
1870 app_code: impl Into<String>,
1871 slippage_bps: u32,
1872 order_class: cow_app_data::types::OrderClassKind,
1873 ) -> Self {
1874 Self { app_code: app_code.into(), slippage_bps, order_class, partner_fee: None }
1875 }
1876
1877 #[must_use]
1887 pub fn with_partner_fee(mut self, fee: cow_app_data::types::PartnerFee) -> Self {
1888 self.partner_fee = Some(fee);
1889 self
1890 }
1891
1892 #[must_use]
1898 pub const fn has_partner_fee(&self) -> bool {
1899 self.partner_fee.is_some()
1900 }
1901}
1902
1903impl fmt::Display for BuildAppDataParams {
1904 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1905 write!(
1906 f,
1907 "build-app-data({}, {}bps, {})",
1908 self.app_code, self.slippage_bps, self.order_class
1909 )
1910 }
1911}
1912
1913#[derive(Debug, Clone)]
1919pub struct SlippageToleranceRequest {
1920 pub chain_id: u64,
1922 pub sell_token: Address,
1924 pub buy_token: Address,
1926 pub sell_amount: Option<U256>,
1928 pub buy_amount: Option<U256>,
1930}
1931
1932impl SlippageToleranceRequest {
1933 #[must_use]
1945 pub const fn new(chain_id: u64, sell_token: Address, buy_token: Address) -> Self {
1946 Self { chain_id, sell_token, buy_token, sell_amount: None, buy_amount: None }
1947 }
1948
1949 #[must_use]
1959 pub const fn with_sell_amount(mut self, amount: U256) -> Self {
1960 self.sell_amount = Some(amount);
1961 self
1962 }
1963
1964 #[must_use]
1974 pub const fn with_buy_amount(mut self, amount: U256) -> Self {
1975 self.buy_amount = Some(amount);
1976 self
1977 }
1978}
1979
1980impl fmt::Display for SlippageToleranceRequest {
1981 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1982 write!(
1983 f,
1984 "slippage-req(chain={}, {:#x} -> {:#x})",
1985 self.chain_id, self.sell_token, self.buy_token
1986 )
1987 }
1988}
1989
1990#[derive(Debug, Clone)]
1994pub struct SlippageToleranceResponse {
1995 pub slippage_bps: Option<u32>,
1997}
1998
1999impl SlippageToleranceResponse {
2000 #[must_use]
2010 pub const fn new(slippage_bps: Option<u32>) -> Self {
2011 Self { slippage_bps }
2012 }
2013
2014 #[must_use]
2020 pub const fn has_suggestion(&self) -> bool {
2021 self.slippage_bps.is_some()
2022 }
2023}
2024
2025impl fmt::Display for SlippageToleranceResponse {
2026 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2027 match self.slippage_bps {
2028 Some(bps) => write!(f, "slippage-resp({bps}bps)"),
2029 None => f.write_str("slippage-resp(none)"),
2030 }
2031 }
2032}
2033
2034#[cfg(test)]
2035mod tests {
2036 use super::*;
2037
2038 use alloy_primitives::B256;
2039
2040 use cow_app_data::types::{OrderClassKind, PartnerFee, PartnerFeeEntry};
2041 use cow_types::{SigningScheme, TokenBalance};
2042
2043 #[test]
2046 fn amounts_new_stores_fields() {
2047 let a = Amounts::new(U256::from(100u32), U256::from(200u32));
2048 assert_eq!(a.sell_amount, U256::from(100u32));
2049 assert_eq!(a.buy_amount, U256::from(200u32));
2050 }
2051
2052 #[test]
2053 fn amounts_is_zero() {
2054 assert!(Amounts::default().is_zero());
2055 assert!(Amounts::new(U256::ZERO, U256::ZERO).is_zero());
2056 assert!(!Amounts::new(U256::from(1u32), U256::ZERO).is_zero());
2057 assert!(!Amounts::new(U256::ZERO, U256::from(1u32)).is_zero());
2058 }
2059
2060 #[test]
2061 fn amounts_total() {
2062 let a = Amounts::new(U256::from(100u32), U256::from(90u32));
2063 assert_eq!(a.total(), U256::from(190u32));
2064 }
2065
2066 #[test]
2067 fn amounts_total_saturates() {
2068 let a = Amounts::new(U256::MAX, U256::from(1u32));
2069 assert_eq!(a.total(), U256::MAX);
2070 }
2071
2072 #[test]
2073 fn amounts_display() {
2074 let a = Amounts::new(U256::from(42u32), U256::from(7u32));
2075 let s = format!("{a}");
2076 assert!(s.contains("sell"));
2077 assert!(s.contains("buy"));
2078 assert!(s.contains("42"));
2079 assert!(s.contains('7'));
2080 }
2081
2082 #[test]
2083 fn amounts_default() {
2084 let a = Amounts::default();
2085 assert_eq!(a.sell_amount, U256::ZERO);
2086 assert_eq!(a.buy_amount, U256::ZERO);
2087 }
2088
2089 #[test]
2092 fn network_fee_new_stores_fields() {
2093 let nf = NetworkFee::new(U256::from(10u32), U256::from(20u32));
2094 assert_eq!(nf.amount_in_sell_currency, U256::from(10u32));
2095 assert_eq!(nf.amount_in_buy_currency, U256::from(20u32));
2096 }
2097
2098 #[test]
2099 fn network_fee_is_zero() {
2100 assert!(NetworkFee::default().is_zero());
2101 assert!(!NetworkFee::new(U256::from(1u32), U256::ZERO).is_zero());
2102 assert!(!NetworkFee::new(U256::ZERO, U256::from(1u32)).is_zero());
2103 }
2104
2105 #[test]
2106 fn network_fee_total_atoms() {
2107 let nf = NetworkFee::new(U256::from(5u32), U256::from(3u32));
2108 assert_eq!(nf.total_atoms(), U256::from(8u32));
2109 }
2110
2111 #[test]
2112 fn network_fee_total_atoms_saturates() {
2113 let nf = NetworkFee::new(U256::MAX, U256::from(1u32));
2114 assert_eq!(nf.total_atoms(), U256::MAX);
2115 }
2116
2117 #[test]
2118 fn network_fee_display() {
2119 let nf = NetworkFee::new(U256::from(10u32), U256::from(20u32));
2120 let s = format!("{nf}");
2121 assert!(s.contains("network-fee"));
2122 assert!(s.contains("10"));
2123 assert!(s.contains("20"));
2124 }
2125
2126 #[test]
2127 fn network_fee_default() {
2128 let nf = NetworkFee::default();
2129 assert_eq!(nf.amount_in_sell_currency, U256::ZERO);
2130 assert_eq!(nf.amount_in_buy_currency, U256::ZERO);
2131 }
2132
2133 #[test]
2136 fn partner_fee_cost_new() {
2137 let pf = PartnerFeeCost::new(U256::from(500u32), 50);
2138 assert_eq!(pf.amount, U256::from(500u32));
2139 assert_eq!(pf.bps, 50);
2140 }
2141
2142 #[test]
2143 fn partner_fee_cost_is_zero() {
2144 assert!(PartnerFeeCost::default().is_zero());
2145 assert!(!PartnerFeeCost::new(U256::from(1u32), 0).is_zero());
2146 assert!(!PartnerFeeCost::new(U256::ZERO, 1).is_zero());
2147 }
2148
2149 #[test]
2150 fn partner_fee_cost_has_bps() {
2151 assert!(!PartnerFeeCost::default().has_bps());
2152 assert!(PartnerFeeCost::new(U256::ZERO, 10).has_bps());
2153 }
2154
2155 #[test]
2156 fn partner_fee_cost_display() {
2157 let pf = PartnerFeeCost::new(U256::from(99u32), 25);
2158 let s = format!("{pf}");
2159 assert!(s.contains("partner-fee"));
2160 assert!(s.contains("25bps"));
2161 }
2162
2163 #[test]
2164 fn partner_fee_cost_default() {
2165 let pf = PartnerFeeCost::default();
2166 assert_eq!(pf.amount, U256::ZERO);
2167 assert_eq!(pf.bps, 0);
2168 }
2169
2170 #[test]
2173 fn protocol_fee_cost_new() {
2174 let pf = ProtocolFeeCost::new(U256::from(300u32), 15);
2175 assert_eq!(pf.amount, U256::from(300u32));
2176 assert_eq!(pf.bps, 15);
2177 }
2178
2179 #[test]
2180 fn protocol_fee_cost_is_zero() {
2181 assert!(ProtocolFeeCost::default().is_zero());
2182 assert!(!ProtocolFeeCost::new(U256::from(1u32), 0).is_zero());
2183 assert!(!ProtocolFeeCost::new(U256::ZERO, 1).is_zero());
2184 }
2185
2186 #[test]
2187 fn protocol_fee_cost_has_bps() {
2188 assert!(!ProtocolFeeCost::default().has_bps());
2189 assert!(ProtocolFeeCost::new(U256::ZERO, 5).has_bps());
2190 }
2191
2192 #[test]
2193 fn protocol_fee_cost_display() {
2194 let pf = ProtocolFeeCost::new(U256::from(77u32), 10);
2195 let s = format!("{pf}");
2196 assert!(s.contains("protocol-fee"));
2197 assert!(s.contains("10bps"));
2198 }
2199
2200 #[test]
2201 fn protocol_fee_cost_default() {
2202 let pf = ProtocolFeeCost::default();
2203 assert_eq!(pf.amount, U256::ZERO);
2204 assert_eq!(pf.bps, 0);
2205 }
2206
2207 #[test]
2210 fn trading_app_data_info_new() {
2211 let info = TradingAppDataInfo::new("{\"v\":1}", "0xabc");
2212 assert_eq!(info.full_app_data, "{\"v\":1}");
2213 assert_eq!(info.app_data_keccak256, "0xabc");
2214 }
2215
2216 #[test]
2217 fn trading_app_data_info_has_full_app_data() {
2218 let with = TradingAppDataInfo::new("{}", "0x1");
2219 assert!(with.has_full_app_data());
2220
2221 let without = TradingAppDataInfo::new("", "0x1");
2222 assert!(!without.has_full_app_data());
2223 }
2224
2225 #[test]
2226 fn trading_app_data_info_refs() {
2227 let info = TradingAppDataInfo::new("doc", "0xhash");
2228 assert_eq!(info.full_app_data_ref(), "doc");
2229 assert_eq!(info.keccak256_ref(), "0xhash");
2230 }
2231
2232 #[test]
2233 fn trading_app_data_info_display() {
2234 let info = TradingAppDataInfo::new("{}", "0xdeadbeef");
2235 let s = format!("{info}");
2236 assert!(s.contains("app-data"));
2237 assert!(s.contains("0xdeadbeef"));
2238 }
2239
2240 #[test]
2243 fn trading_tx_params_new() {
2244 let data = vec![0xAA, 0xBB];
2245 let to = Address::ZERO;
2246 let tx = TradingTransactionParams::new(data.clone(), to, 21_000, U256::from(1u32));
2247 assert_eq!(tx.data, data);
2248 assert_eq!(tx.to, to);
2249 assert_eq!(tx.gas_limit, 21_000);
2250 assert_eq!(tx.value, U256::from(1u32));
2251 }
2252
2253 #[test]
2254 fn trading_tx_params_builders() {
2255 let tx = TradingTransactionParams::new(vec![], Address::ZERO, 0, U256::ZERO)
2256 .with_data(vec![1, 2, 3])
2257 .with_to(Address::with_last_byte(0x01))
2258 .with_gas_limit(50_000)
2259 .with_value(U256::from(999u32));
2260
2261 assert_eq!(tx.data, vec![1, 2, 3]);
2262 assert_eq!(tx.to, Address::with_last_byte(0x01));
2263 assert_eq!(tx.gas_limit, 50_000);
2264 assert_eq!(tx.value, U256::from(999u32));
2265 }
2266
2267 #[test]
2268 fn trading_tx_params_data_len() {
2269 let tx = TradingTransactionParams::new(vec![0; 64], Address::ZERO, 0, U256::ZERO);
2270 assert_eq!(tx.data_len(), 64);
2271 }
2272
2273 #[test]
2274 fn trading_tx_params_has_value() {
2275 let no_val = TradingTransactionParams::new(vec![], Address::ZERO, 0, U256::ZERO);
2276 assert!(!no_val.has_value());
2277
2278 let with_val = no_val.with_value(U256::from(1u32));
2279 assert!(with_val.has_value());
2280 }
2281
2282 #[test]
2283 fn trading_tx_params_display() {
2284 let tx = TradingTransactionParams::new(vec![], Address::ZERO, 21_000, U256::ZERO);
2285 let s = format!("{tx}");
2286 assert!(s.contains("tx"));
2287 assert!(s.contains("21000"));
2288 }
2289
2290 #[test]
2293 fn post_trade_default() {
2294 let p = PostTradeAdditionalParams::default();
2295 assert!(!p.has_signing_scheme());
2296 assert!(!p.has_network_costs());
2297 assert!(!p.should_apply_costs());
2298 }
2299
2300 #[test]
2301 fn post_trade_with_signing_scheme() {
2302 let p = PostTradeAdditionalParams::default().with_signing_scheme(SigningScheme::PreSign);
2303 assert!(p.has_signing_scheme());
2304 assert!(matches!(p.signing_scheme, Some(SigningScheme::PreSign)));
2305 }
2306
2307 #[test]
2308 fn post_trade_with_network_costs_amount() {
2309 let p = PostTradeAdditionalParams::default().with_network_costs_amount("12345");
2310 assert!(p.has_network_costs());
2311 assert_eq!(p.network_costs_amount.as_deref(), Some("12345"));
2312 }
2313
2314 #[test]
2315 fn post_trade_with_apply_costs() {
2316 let p = PostTradeAdditionalParams::default().with_apply_costs_slippage_and_fees(true);
2317 assert!(p.should_apply_costs());
2318
2319 let p2 = PostTradeAdditionalParams::default().with_apply_costs_slippage_and_fees(false);
2320 assert!(!p2.should_apply_costs());
2321 }
2322
2323 #[test]
2324 fn post_trade_display() {
2325 let p = PostTradeAdditionalParams::default();
2326 assert_eq!(format!("{p}"), "post-trade-params");
2327 }
2328
2329 #[test]
2332 fn swap_settings_default() {
2333 let s = SwapAdvancedSettings::default();
2334 assert!(!s.has_app_data());
2335 assert!(!s.has_slippage_bps());
2336 assert!(!s.has_partner_fee());
2337 }
2338
2339 #[test]
2340 fn swap_settings_with_app_data() {
2341 let s = SwapAdvancedSettings::default().with_app_data(serde_json::json!({"k": "v"}));
2342 assert!(s.has_app_data());
2343 }
2344
2345 #[test]
2346 fn swap_settings_with_slippage_bps() {
2347 let s = SwapAdvancedSettings::default().with_slippage_bps(100);
2348 assert!(s.has_slippage_bps());
2349 assert_eq!(s.slippage_bps, Some(100));
2350 }
2351
2352 #[test]
2353 fn swap_settings_with_partner_fee() {
2354 let fee = PartnerFee::single(PartnerFeeEntry::volume(50, "0xRecipient"));
2355 let s = SwapAdvancedSettings::default().with_partner_fee(fee);
2356 assert!(s.has_partner_fee());
2357 }
2358
2359 #[test]
2360 fn swap_settings_display() {
2361 let s = SwapAdvancedSettings::default();
2362 assert_eq!(format!("{s}"), "swap-settings");
2363 }
2364
2365 #[test]
2368 fn limit_settings_default() {
2369 let s = LimitOrderAdvancedSettings::default();
2370 assert!(!s.has_receiver());
2371 assert!(!s.has_valid_to());
2372 assert!(!s.has_partner_fee());
2373 assert!(!s.has_partially_fillable());
2374 assert!(!s.has_app_data());
2375 }
2376
2377 #[test]
2378 fn limit_settings_with_receiver() {
2379 let addr = Address::with_last_byte(0x42);
2380 let s = LimitOrderAdvancedSettings::default().with_receiver(addr);
2381 assert!(s.has_receiver());
2382 assert_eq!(s.receiver, Some(addr));
2383 }
2384
2385 #[test]
2386 fn limit_settings_with_valid_to() {
2387 let s = LimitOrderAdvancedSettings::default().with_valid_to(1_700_000_000);
2388 assert!(s.has_valid_to());
2389 assert_eq!(s.valid_to, Some(1_700_000_000));
2390 }
2391
2392 #[test]
2393 fn limit_settings_with_partner_fee() {
2394 let fee = PartnerFee::single(PartnerFeeEntry::volume(25, "0xAddr"));
2395 let s = LimitOrderAdvancedSettings::default().with_partner_fee(fee);
2396 assert!(s.has_partner_fee());
2397 }
2398
2399 #[test]
2400 fn limit_settings_with_partially_fillable() {
2401 let s = LimitOrderAdvancedSettings::default().with_partially_fillable(true);
2402 assert!(s.has_partially_fillable());
2403 assert_eq!(s.partially_fillable, Some(true));
2404 }
2405
2406 #[test]
2407 fn limit_settings_with_app_data() {
2408 let s = LimitOrderAdvancedSettings::default().with_app_data("0xabc123");
2409 assert!(s.has_app_data());
2410 assert_eq!(s.app_data.as_deref(), Some("0xabc123"));
2411 }
2412
2413 #[test]
2414 fn limit_settings_display() {
2415 let s = LimitOrderAdvancedSettings::default();
2416 assert_eq!(format!("{s}"), "limit-settings");
2417 }
2418
2419 #[test]
2422 fn apply_settings_none_returns_unchanged() {
2423 let params = LimitTradeParameters::sell(
2424 Address::ZERO,
2425 Address::ZERO,
2426 U256::from(1000u32),
2427 U256::from(900u32),
2428 );
2429 let result = apply_settings_to_limit_trade_parameters(params, None);
2430 assert_eq!(result.sell_amount, U256::from(1000u32));
2431 assert!(!result.partially_fillable);
2432 }
2433
2434 #[test]
2435 fn apply_settings_overrides_fields() {
2436 let params = LimitTradeParameters::sell(
2437 Address::ZERO,
2438 Address::ZERO,
2439 U256::from(1000u32),
2440 U256::from(900u32),
2441 );
2442 let settings = LimitOrderAdvancedSettings::default()
2443 .with_receiver(Address::with_last_byte(0x01))
2444 .with_valid_to(9999)
2445 .with_partially_fillable(true)
2446 .with_app_data("0xbeef");
2447
2448 let result = apply_settings_to_limit_trade_parameters(params, Some(&settings));
2449 assert_eq!(result.receiver, Some(Address::with_last_byte(0x01)));
2450 assert_eq!(result.valid_to, Some(9999));
2451 assert!(result.partially_fillable);
2452 assert_eq!(result.app_data.as_deref(), Some("0xbeef"));
2453 }
2454
2455 #[test]
2458 fn limit_from_quote_new() {
2459 let p = LimitTradeParametersFromQuote::new(
2460 Address::ZERO,
2461 Address::with_last_byte(1),
2462 U256::from(100u32),
2463 U256::from(90u32),
2464 );
2465 assert_eq!(p.sell_token, Address::ZERO);
2466 assert_eq!(p.buy_token, Address::with_last_byte(1));
2467 assert_eq!(p.sell_amount, U256::from(100u32));
2468 assert_eq!(p.buy_amount, U256::from(90u32));
2469 assert!(!p.has_quote_id());
2470 }
2471
2472 #[test]
2473 fn limit_from_quote_with_quote_id() {
2474 let p = LimitTradeParametersFromQuote::new(
2475 Address::ZERO,
2476 Address::ZERO,
2477 U256::from(1u32),
2478 U256::from(1u32),
2479 )
2480 .with_quote_id(42);
2481 assert!(p.has_quote_id());
2482 assert_eq!(p.quote_id, Some(42));
2483 }
2484
2485 #[test]
2486 fn limit_from_quote_display() {
2487 let p = LimitTradeParametersFromQuote::new(
2488 Address::ZERO,
2489 Address::ZERO,
2490 U256::from(100u32),
2491 U256::from(90u32),
2492 );
2493 let s = format!("{p}");
2494 assert!(s.contains("limit-from-quote"));
2495 }
2496
2497 #[test]
2500 fn trade_params_sell() {
2501 let p = TradeParameters::sell(
2502 Address::ZERO,
2503 18,
2504 Address::with_last_byte(1),
2505 6,
2506 U256::from(1000u32),
2507 );
2508 assert!(p.is_sell());
2509 assert!(!p.is_buy());
2510 assert_eq!(p.sell_token_decimals, 18);
2511 assert_eq!(p.buy_token_decimals, 6);
2512 assert_eq!(p.amount, U256::from(1000u32));
2513 assert!(!p.has_slippage_bps());
2514 assert!(!p.has_receiver());
2515 assert!(!p.has_partner_fee());
2516 }
2517
2518 #[test]
2519 fn trade_params_buy() {
2520 let p = TradeParameters::buy(
2521 Address::ZERO,
2522 18,
2523 Address::with_last_byte(1),
2524 6,
2525 U256::from(500u32),
2526 );
2527 assert!(p.is_buy());
2528 assert!(!p.is_sell());
2529 }
2530
2531 #[test]
2532 fn trade_params_builders() {
2533 let recv = Address::with_last_byte(0x99);
2534 let p = TradeParameters::sell(Address::ZERO, 18, Address::ZERO, 18, U256::from(1u32))
2535 .with_slippage_bps(50)
2536 .with_receiver(recv)
2537 .with_valid_for(600)
2538 .with_valid_to(1_700_000_000)
2539 .with_partially_fillable();
2540
2541 assert!(p.has_slippage_bps());
2542 assert_eq!(p.slippage_bps, Some(50));
2543 assert!(p.has_receiver());
2544 assert_eq!(p.receiver, Some(recv));
2545 assert_eq!(p.valid_for, Some(600));
2546 assert_eq!(p.valid_to, Some(1_700_000_000));
2547 assert_eq!(p.partially_fillable, Some(true));
2548 }
2549
2550 #[test]
2551 fn trade_params_display() {
2552 let p = TradeParameters::sell(Address::ZERO, 18, Address::ZERO, 18, U256::from(1u32));
2553 let s = format!("{p}");
2554 assert!(s.contains("sell"));
2555 }
2556
2557 #[test]
2560 fn limit_trade_params_sell_and_buy() {
2561 let sell = LimitTradeParameters::sell(
2562 Address::ZERO,
2563 Address::with_last_byte(1),
2564 U256::from(100u32),
2565 U256::from(90u32),
2566 );
2567 assert!(sell.is_sell());
2568 assert!(!sell.is_buy());
2569 assert!(!sell.partially_fillable);
2570
2571 let buy = LimitTradeParameters::buy(
2572 Address::ZERO,
2573 Address::with_last_byte(1),
2574 U256::from(100u32),
2575 U256::from(90u32),
2576 );
2577 assert!(buy.is_buy());
2578 assert!(!buy.is_sell());
2579 }
2580
2581 #[test]
2582 fn limit_trade_params_builders() {
2583 let recv = Address::with_last_byte(0x42);
2584 let p = LimitTradeParameters::sell(
2585 Address::ZERO,
2586 Address::ZERO,
2587 U256::from(1u32),
2588 U256::from(1u32),
2589 )
2590 .with_receiver(recv)
2591 .with_valid_for(300)
2592 .with_valid_to(9999)
2593 .with_partially_fillable();
2594
2595 assert!(p.has_receiver());
2596 assert_eq!(p.receiver, Some(recv));
2597 assert!(p.has_valid_for());
2598 assert_eq!(p.valid_for, Some(300));
2599 assert!(p.has_valid_to());
2600 assert_eq!(p.valid_to, Some(9999));
2601 assert!(p.partially_fillable);
2602 }
2603
2604 #[test]
2605 fn limit_trade_params_has_app_data_and_partner_fee() {
2606 let p = LimitTradeParameters::sell(
2607 Address::ZERO,
2608 Address::ZERO,
2609 U256::from(1u32),
2610 U256::from(1u32),
2611 );
2612 assert!(!p.has_app_data());
2613 assert!(!p.has_partner_fee());
2614 }
2615
2616 #[test]
2617 fn limit_trade_params_display() {
2618 let p = LimitTradeParameters::sell(
2619 Address::ZERO,
2620 Address::ZERO,
2621 U256::from(100u32),
2622 U256::from(90u32),
2623 );
2624 let s = format!("{p}");
2625 assert!(s.contains("limit"));
2626 assert!(s.contains("sell"));
2627 }
2628
2629 fn sample_quote_costs() -> QuoteAmountsAndCosts {
2632 QuoteAmountsAndCosts {
2633 is_sell: true,
2634 before_all_fees: Amounts::new(U256::from(200u32), U256::from(110u32)),
2635 before_network_costs: Amounts::new(U256::from(200u32), U256::from(100u32)),
2636 after_network_costs: Amounts::new(U256::from(190u32), U256::from(100u32)),
2637 after_partner_fees: Amounts::new(U256::from(190u32), U256::from(95u32)),
2638 after_slippage: Amounts::new(U256::from(190u32), U256::from(90u32)),
2639 network_fee: NetworkFee::new(U256::from(10u32), U256::ZERO),
2640 partner_fee: PartnerFeeCost::new(U256::from(5u32), 50),
2641 protocol_fee: ProtocolFeeCost::new(U256::from(3u32), 30),
2642 }
2643 }
2644
2645 #[test]
2646 fn quote_costs_is_buy() {
2647 let sell = sample_quote_costs();
2648 assert!(!sell.is_buy());
2649
2650 let mut buy = sample_quote_costs();
2651 buy.is_sell = false;
2652 assert!(buy.is_buy());
2653 }
2654
2655 #[test]
2656 fn quote_costs_max_slippage_atoms() {
2657 let q = sample_quote_costs();
2658 assert_eq!(q.max_slippage_atoms(), U256::from(5u32));
2660 }
2661
2662 #[test]
2663 fn quote_costs_total_fees_atoms() {
2664 let q = sample_quote_costs();
2665 assert_eq!(q.total_fees_atoms(), U256::from(18u32));
2667 }
2668
2669 #[test]
2670 fn quote_costs_has_fees() {
2671 let q = sample_quote_costs();
2672 assert!(q.has_network_fee());
2673 assert!(q.has_partner_fee());
2674 assert!(q.has_protocol_fee());
2675
2676 let zero_q = QuoteAmountsAndCosts {
2677 is_sell: true,
2678 before_all_fees: Amounts::default(),
2679 before_network_costs: Amounts::default(),
2680 after_network_costs: Amounts::default(),
2681 after_partner_fees: Amounts::default(),
2682 after_slippage: Amounts::default(),
2683 network_fee: NetworkFee::default(),
2684 partner_fee: PartnerFeeCost::default(),
2685 protocol_fee: ProtocolFeeCost::default(),
2686 };
2687 assert!(!zero_q.has_network_fee());
2688 assert!(!zero_q.has_partner_fee());
2689 assert!(!zero_q.has_protocol_fee());
2690 }
2691
2692 #[test]
2693 fn quote_costs_display() {
2694 let q = sample_quote_costs();
2695 let s = format!("{q}");
2696 assert!(s.contains("sell"));
2697 assert!(s.contains("network-fee"));
2698 assert!(s.contains("partner-fee"));
2699 assert!(s.contains("protocol-fee"));
2700 }
2701
2702 #[test]
2705 fn map_doubles_amounts_preserves_bps() {
2706 let q = sample_quote_costs();
2707 let doubled = map_quote_amounts_and_costs(&q, |a| a * U256::from(2u32));
2708
2709 assert_eq!(doubled.before_all_fees.sell_amount, U256::from(400u32));
2710 assert_eq!(doubled.network_fee.amount_in_sell_currency, U256::from(20u32));
2711 assert_eq!(doubled.partner_fee.amount, U256::from(10u32));
2712 assert_eq!(doubled.protocol_fee.amount, U256::from(6u32));
2713 assert_eq!(doubled.partner_fee.bps, 50);
2715 assert_eq!(doubled.protocol_fee.bps, 30);
2716 assert!(doubled.is_sell);
2717 }
2718
2719 fn sample_unsigned_order() -> UnsignedOrder {
2722 UnsignedOrder {
2723 sell_token: Address::ZERO,
2724 buy_token: Address::ZERO,
2725 receiver: Address::ZERO,
2726 sell_amount: U256::from(100u32),
2727 buy_amount: U256::from(90u32),
2728 valid_to: 0,
2729 app_data: B256::ZERO,
2730 fee_amount: U256::ZERO,
2731 kind: OrderKind::Sell,
2732 partially_fillable: false,
2733 sell_token_balance: TokenBalance::Erc20,
2734 buy_token_balance: TokenBalance::Erc20,
2735 }
2736 }
2737
2738 #[test]
2739 fn order_posting_result_new() {
2740 let r = OrderPostingResult::new(
2741 "uid123",
2742 SigningScheme::Eip712,
2743 "0xsig",
2744 sample_unsigned_order(),
2745 );
2746 assert_eq!(r.order_id_ref(), "uid123");
2747 assert_eq!(r.signature_ref(), "0xsig");
2748 }
2749
2750 #[test]
2751 fn order_posting_result_signing_scheme_predicates() {
2752 let eip712 =
2753 OrderPostingResult::new("a", SigningScheme::Eip712, "", sample_unsigned_order());
2754 assert!(eip712.is_eip712());
2755 assert!(!eip712.is_eth_sign());
2756 assert!(!eip712.is_eip1271());
2757 assert!(!eip712.is_presign());
2758
2759 let eth_sign =
2760 OrderPostingResult::new("b", SigningScheme::EthSign, "", sample_unsigned_order());
2761 assert!(eth_sign.is_eth_sign());
2762
2763 let eip1271 =
2764 OrderPostingResult::new("c", SigningScheme::Eip1271, "", sample_unsigned_order());
2765 assert!(eip1271.is_eip1271());
2766
2767 let presign =
2768 OrderPostingResult::new("d", SigningScheme::PreSign, "", sample_unsigned_order());
2769 assert!(presign.is_presign());
2770 }
2771
2772 #[test]
2773 fn order_posting_result_display() {
2774 let r =
2775 OrderPostingResult::new("uid-xyz", SigningScheme::Eip712, "", sample_unsigned_order());
2776 let s = format!("{r}");
2777 assert!(s.contains("order"));
2778 assert!(s.contains("uid-xyz"));
2779 }
2780
2781 #[test]
2784 fn build_app_data_params_new() {
2785 let p = BuildAppDataParams::new("CoW Swap", 50, OrderClassKind::Market);
2786 assert_eq!(p.app_code, "CoW Swap");
2787 assert_eq!(p.slippage_bps, 50);
2788 assert!(!p.has_partner_fee());
2789 }
2790
2791 #[test]
2792 fn build_app_data_params_with_partner_fee() {
2793 let fee = PartnerFee::single(PartnerFeeEntry::volume(50, "0xRecip"));
2794 let p = BuildAppDataParams::new("App", 25, OrderClassKind::Limit).with_partner_fee(fee);
2795 assert!(p.has_partner_fee());
2796 }
2797
2798 #[test]
2799 fn build_app_data_params_display() {
2800 let p = BuildAppDataParams::new("MyApp", 100, OrderClassKind::Market);
2801 let s = format!("{p}");
2802 assert!(s.contains("build-app-data"));
2803 assert!(s.contains("MyApp"));
2804 assert!(s.contains("100bps"));
2805 }
2806
2807 #[test]
2810 fn slippage_request_new() {
2811 let r = SlippageToleranceRequest::new(1, Address::ZERO, Address::with_last_byte(1));
2812 assert_eq!(r.chain_id, 1);
2813 assert_eq!(r.sell_token, Address::ZERO);
2814 assert_eq!(r.buy_token, Address::with_last_byte(1));
2815 assert!(r.sell_amount.is_none());
2816 assert!(r.buy_amount.is_none());
2817 }
2818
2819 #[test]
2820 fn slippage_request_with_amounts() {
2821 let r = SlippageToleranceRequest::new(1, Address::ZERO, Address::ZERO)
2822 .with_sell_amount(U256::from(100u32))
2823 .with_buy_amount(U256::from(90u32));
2824 assert_eq!(r.sell_amount, Some(U256::from(100u32)));
2825 assert_eq!(r.buy_amount, Some(U256::from(90u32)));
2826 }
2827
2828 #[test]
2829 fn slippage_request_display() {
2830 let r = SlippageToleranceRequest::new(1, Address::ZERO, Address::ZERO);
2831 let s = format!("{r}");
2832 assert!(s.contains("slippage-req"));
2833 assert!(s.contains("chain=1"));
2834 }
2835
2836 #[test]
2839 fn slippage_response_new() {
2840 let with = SlippageToleranceResponse::new(Some(50));
2841 assert!(with.has_suggestion());
2842 assert_eq!(with.slippage_bps, Some(50));
2843
2844 let without = SlippageToleranceResponse::new(None);
2845 assert!(!without.has_suggestion());
2846 }
2847
2848 #[test]
2849 fn slippage_response_display() {
2850 let with = SlippageToleranceResponse::new(Some(100));
2851 assert_eq!(format!("{with}"), "slippage-resp(100bps)");
2852
2853 let without = SlippageToleranceResponse::new(None);
2854 assert_eq!(format!("{without}"), "slippage-resp(none)");
2855 }
2856}