1use alloy_primitives::U256;
28use cow_primitives::{HUNDRED_THOUSANDS, ONE_HUNDRED_BPS};
29use cow_types::OrderKind;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct QuoteAmounts {
36 pub sell_amount: U256,
38 pub buy_amount: U256,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub struct QuoteNetworkFee {
45 pub amount_in_sell_currency: U256,
47 pub amount_in_buy_currency: U256,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq)]
53pub struct QuoteFeeComponent {
54 pub amount: U256,
56 pub bps: f64,
58}
59
60#[derive(Debug, Clone, Copy, PartialEq)]
62pub struct QuoteCosts {
63 pub network_fee: QuoteNetworkFee,
65 pub partner_fee: QuoteFeeComponent,
67 pub protocol_fee: QuoteFeeComponent,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq)]
76pub struct QuoteAmountsAndCostsResult {
77 pub is_sell: bool,
79 pub costs: QuoteCosts,
81 pub before_all_fees: QuoteAmounts,
83 pub before_network_costs: QuoteAmounts,
85 pub after_protocol_fees: QuoteAmounts,
87 pub after_network_costs: QuoteAmounts,
89 pub after_partner_fees: QuoteAmounts,
91 pub after_slippage: QuoteAmounts,
93 pub amounts_to_sign: QuoteAmounts,
95}
96
97#[derive(Debug, Clone)]
103pub struct QuoteOrderParams {
104 pub kind: OrderKind,
106 pub sell_amount: U256,
108 pub buy_amount: U256,
110 pub fee_amount: U256,
112}
113
114#[derive(Debug, Clone)]
116pub struct QuoteAmountsAndCostsParams {
117 pub order_params: QuoteOrderParams,
119 pub protocol_fee_bps: Option<f64>,
121 pub partner_fee_bps: Option<u32>,
123 pub slippage_percent_bps: u32,
125}
126
127#[derive(Debug, Clone)]
129pub struct ProtocolFeeAmountParams {
130 pub order_params: QuoteOrderParams,
132 pub protocol_fee_bps: f64,
134}
135
136#[must_use]
158pub fn get_protocol_fee_amount(params: &ProtocolFeeAmountParams) -> U256 {
159 let ProtocolFeeAmountParams { order_params, protocol_fee_bps } = params;
160
161 if *protocol_fee_bps <= 0.0 {
162 return U256::ZERO;
163 }
164
165 let is_sell = order_params.kind.is_sell();
166 let sell_amount = order_params.sell_amount;
167 let buy_amount = order_params.buy_amount;
168 let fee_amount = order_params.fee_amount;
169
170 let protocol_fee_scale = U256::from(HUNDRED_THOUSANDS);
171 let protocol_fee_bps_scaled = (*protocol_fee_bps * HUNDRED_THOUSANDS as f64).round() as u64;
173 let protocol_fee_bps_big = U256::from(protocol_fee_bps_scaled);
174
175 if protocol_fee_bps_big.is_zero() {
176 return U256::ZERO;
177 }
178
179 let one_hundred_bps = U256::from(ONE_HUNDRED_BPS);
180
181 if is_sell {
182 let denominator = one_hundred_bps * protocol_fee_scale - protocol_fee_bps_big;
184 buy_amount * protocol_fee_bps_big / denominator
185 } else {
186 let denominator = one_hundred_bps * protocol_fee_scale + protocol_fee_bps_big;
188 (sell_amount + fee_amount) * protocol_fee_bps_big / denominator
189 }
190}
191
192#[derive(Debug, Clone, Copy, PartialEq, Eq)]
196pub struct PartnerFeeResult {
197 pub partner_fee_amount: U256,
199 pub after_partner_fees: QuoteAmounts,
201}
202
203#[must_use]
224pub fn get_quote_amounts_after_partner_fee(
225 after_network_costs: &QuoteAmounts,
226 before_all_fees: &QuoteAmounts,
227 is_sell: bool,
228 partner_fee_bps: u32,
229) -> PartnerFeeResult {
230 let one_hundred_bps = U256::from(ONE_HUNDRED_BPS);
231
232 let surplus_amount =
234 if is_sell { before_all_fees.buy_amount } else { before_all_fees.sell_amount };
235 let partner_fee_amount = if partner_fee_bps > 0 {
236 surplus_amount * U256::from(partner_fee_bps) / one_hundred_bps
237 } else {
238 U256::ZERO
239 };
240
241 let after_partner_fees = if is_sell {
242 QuoteAmounts {
243 sell_amount: after_network_costs.sell_amount,
244 buy_amount: after_network_costs.buy_amount - partner_fee_amount,
245 }
246 } else {
247 QuoteAmounts {
248 sell_amount: after_network_costs.sell_amount + partner_fee_amount,
249 buy_amount: after_network_costs.buy_amount,
250 }
251 };
252
253 PartnerFeeResult { partner_fee_amount, after_partner_fees }
254}
255
256#[must_use]
276pub fn get_quote_amounts_after_slippage(
277 after_partner_fees: &QuoteAmounts,
278 is_sell: bool,
279 slippage_bps: u32,
280) -> QuoteAmounts {
281 let one_hundred_bps = U256::from(ONE_HUNDRED_BPS);
282 let slippage_bps_big = U256::from(slippage_bps);
283
284 let slippage = |amount: U256| -> U256 { amount * slippage_bps_big / one_hundred_bps };
285
286 if is_sell {
287 QuoteAmounts {
288 sell_amount: after_partner_fees.sell_amount,
289 buy_amount: after_partner_fees.buy_amount - slippage(after_partner_fees.buy_amount),
290 }
291 } else {
292 QuoteAmounts {
293 sell_amount: after_partner_fees.sell_amount + slippage(after_partner_fees.sell_amount),
294 buy_amount: after_partner_fees.buy_amount,
295 }
296 }
297}
298
299#[must_use]
326pub fn get_quote_amounts_and_costs(
327 params: &QuoteAmountsAndCostsParams,
328) -> QuoteAmountsAndCostsResult {
329 let QuoteAmountsAndCostsParams {
330 order_params,
331 protocol_fee_bps,
332 partner_fee_bps,
333 slippage_percent_bps,
334 } = params;
335
336 let partner_fee = partner_fee_bps.map_or(0, |v| v);
337 let protocol_fee = protocol_fee_bps.map_or(0.0, |v| v);
338 let is_sell = order_params.kind.is_sell();
339
340 let sell_amount = order_params.sell_amount;
341 let buy_amount = order_params.buy_amount;
342 let network_cost_amount = order_params.fee_amount;
343
344 let network_cost_in_buy = if sell_amount.is_zero() {
346 U256::ZERO
347 } else {
348 buy_amount * network_cost_amount / sell_amount
349 };
350
351 let protocol_fee_amount = get_protocol_fee_amount(&ProtocolFeeAmountParams {
353 order_params: order_params.clone(),
354 protocol_fee_bps: protocol_fee,
355 });
356
357 let before_all_fees = if is_sell {
359 QuoteAmounts {
360 sell_amount: sell_amount + network_cost_amount,
361 buy_amount: buy_amount + network_cost_in_buy + protocol_fee_amount,
362 }
363 } else {
364 QuoteAmounts { sell_amount: sell_amount - protocol_fee_amount, buy_amount }
365 };
366
367 let after_protocol_fees = if is_sell {
369 QuoteAmounts {
370 sell_amount: before_all_fees.sell_amount,
371 buy_amount: before_all_fees.buy_amount - protocol_fee_amount,
372 }
373 } else {
374 QuoteAmounts { sell_amount, buy_amount: before_all_fees.buy_amount }
375 };
376
377 let after_network_costs = if is_sell {
379 QuoteAmounts { sell_amount, buy_amount }
380 } else {
381 QuoteAmounts {
382 sell_amount: sell_amount + network_cost_amount,
383 buy_amount: after_protocol_fees.buy_amount,
384 }
385 };
386
387 let PartnerFeeResult { partner_fee_amount, after_partner_fees } =
389 get_quote_amounts_after_partner_fee(
390 &after_network_costs,
391 &before_all_fees,
392 is_sell,
393 partner_fee,
394 );
395
396 let after_slippage =
398 get_quote_amounts_after_slippage(&after_partner_fees, is_sell, *slippage_percent_bps);
399
400 let amounts_to_sign = if is_sell {
402 QuoteAmounts {
403 sell_amount: before_all_fees.sell_amount,
404 buy_amount: after_slippage.buy_amount,
405 }
406 } else {
407 QuoteAmounts {
408 sell_amount: after_slippage.sell_amount,
409 buy_amount: before_all_fees.buy_amount,
410 }
411 };
412
413 QuoteAmountsAndCostsResult {
414 is_sell,
415 costs: QuoteCosts {
416 network_fee: QuoteNetworkFee {
417 amount_in_sell_currency: network_cost_amount,
418 amount_in_buy_currency: network_cost_in_buy,
419 },
420 partner_fee: QuoteFeeComponent { amount: partner_fee_amount, bps: partner_fee as f64 },
421 protocol_fee: QuoteFeeComponent { amount: protocol_fee_amount, bps: protocol_fee },
422 },
423 before_all_fees,
424 before_network_costs: after_protocol_fees,
425 after_protocol_fees,
426 after_network_costs,
427 after_partner_fees,
428 after_slippage,
429 amounts_to_sign,
430 }
431}
432
433#[must_use]
456pub fn transform_order(mut order: super::Order) -> super::Order {
457 let executed_fee_amount: U256 = order.executed_fee_amount.parse().map_or(U256::ZERO, |v| v);
459 let executed_fee: U256 =
460 order.executed_fee.as_deref().and_then(|s| s.parse().ok()).map_or(U256::ZERO, |v| v);
461 order.total_fee = Some((executed_fee_amount + executed_fee).to_string());
462
463 if let Some(ref ethflow_data) = order.ethflow_data {
465 order.valid_to = ethflow_data.user_valid_to;
466 if let Some(user) = order.onchain_user {
467 order.owner = user;
468 }
469 order.sell_token = cow_chains::NATIVE_CURRENCY_ADDRESS;
470 }
471
472 order
473}
474
475#[cfg(test)]
478mod tests {
479 use super::*;
480
481 fn sell_order() -> QuoteOrderParams {
483 QuoteOrderParams {
484 kind: OrderKind::Sell,
485 sell_amount: U256::from_str_radix("156144455961718918", 10).unwrap(),
486 fee_amount: U256::from_str_radix("3855544038281082", 10).unwrap(),
487 buy_amount: U256::from_str_radix("18632013982", 10).unwrap(),
488 }
489 }
490
491 fn buy_order() -> QuoteOrderParams {
492 QuoteOrderParams {
493 kind: OrderKind::Buy,
494 sell_amount: U256::from_str_radix("168970833896526983", 10).unwrap(),
495 fee_amount: U256::from_str_radix("2947344072902629", 10).unwrap(),
496 buy_amount: U256::from_str_radix("2000000000", 10).unwrap(),
497 }
498 }
499
500 #[test]
503 fn sell_after_network_costs_equals_api_sell_amount() {
504 let order_params = sell_order();
505 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
506 order_params: order_params.clone(),
507 slippage_percent_bps: 0,
508 partner_fee_bps: None,
509 protocol_fee_bps: None,
510 });
511 assert_eq!(result.after_network_costs.sell_amount, order_params.sell_amount);
512 }
513
514 #[test]
515 fn sell_before_network_costs_is_sell_plus_fee() {
516 let order_params = sell_order();
517 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
518 order_params: order_params.clone(),
519 slippage_percent_bps: 0,
520 partner_fee_bps: None,
521 protocol_fee_bps: None,
522 });
523 assert_eq!(
524 result.before_network_costs.sell_amount,
525 order_params.sell_amount + order_params.fee_amount
526 );
527 }
528
529 #[test]
530 fn buy_after_network_costs_is_sell_plus_fee() {
531 let order_params = buy_order();
532 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
533 order_params: order_params.clone(),
534 slippage_percent_bps: 0,
535 partner_fee_bps: None,
536 protocol_fee_bps: None,
537 });
538 assert_eq!(
539 result.after_network_costs.sell_amount,
540 order_params.sell_amount + order_params.fee_amount
541 );
542 }
543
544 #[test]
545 fn buy_before_network_costs_is_raw_sell() {
546 let order_params = buy_order();
547 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
548 order_params: order_params.clone(),
549 slippage_percent_bps: 0,
550 partner_fee_bps: None,
551 protocol_fee_bps: None,
552 });
553 assert_eq!(result.before_network_costs.sell_amount, order_params.sell_amount);
554 }
555
556 #[test]
557 fn sell_buy_amount_includes_network_cost_in_buy_currency() {
558 let order_params = sell_order();
559 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
560 order_params: order_params.clone(),
561 slippage_percent_bps: 0,
562 partner_fee_bps: None,
563 protocol_fee_bps: None,
564 });
565 let network_cost_in_buy =
566 order_params.buy_amount * order_params.fee_amount / order_params.sell_amount;
567 assert_eq!(
568 result.before_network_costs.buy_amount,
569 order_params.buy_amount + network_cost_in_buy
570 );
571 assert_eq!(result.after_network_costs.buy_amount, order_params.buy_amount);
572 }
573
574 #[test]
575 fn buy_buy_amount_unchanged_by_network_costs() {
576 let order_params = buy_order();
577 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
578 order_params,
579 slippage_percent_bps: 0,
580 partner_fee_bps: None,
581 protocol_fee_bps: None,
582 });
583 assert_eq!(result.after_network_costs.buy_amount, result.before_network_costs.buy_amount);
584 }
585
586 #[test]
589 fn sell_partner_fee_subtracted_from_buy() {
590 let order_params = sell_order();
591 let partner_fee_bps = 100u32;
592 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
593 order_params: order_params.clone(),
594 slippage_percent_bps: 0,
595 partner_fee_bps: Some(partner_fee_bps),
596 protocol_fee_bps: None,
597 });
598 let network_cost_in_buy =
599 order_params.buy_amount * order_params.fee_amount / order_params.sell_amount;
600 let buy_before_all_fees = order_params.buy_amount + network_cost_in_buy;
601 let expected =
602 buy_before_all_fees * U256::from(partner_fee_bps) / U256::from(ONE_HUNDRED_BPS);
603 assert_eq!(result.costs.partner_fee.amount, expected);
604 }
605
606 #[test]
607 fn buy_partner_fee_added_to_sell() {
608 let order_params = buy_order();
609 let partner_fee_bps = 100u32;
610 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
611 order_params: order_params.clone(),
612 slippage_percent_bps: 0,
613 partner_fee_bps: Some(partner_fee_bps),
614 protocol_fee_bps: None,
615 });
616 let expected =
617 order_params.sell_amount * U256::from(partner_fee_bps) / U256::from(ONE_HUNDRED_BPS);
618 assert_eq!(result.costs.partner_fee.amount, expected);
619 }
620
621 #[test]
624 fn sell_slippage_subtracted_from_buy() {
625 let order_params = sell_order();
626 let slippage_bps = 200u32;
627 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
628 order_params: order_params.clone(),
629 slippage_percent_bps: slippage_bps,
630 partner_fee_bps: None,
631 protocol_fee_bps: None,
632 });
633 let buy_after_network = order_params.buy_amount;
634 let slippage = buy_after_network * U256::from(slippage_bps) / U256::from(ONE_HUNDRED_BPS);
635 assert_eq!(result.after_slippage.buy_amount, buy_after_network - slippage);
636 }
637
638 #[test]
639 fn buy_slippage_added_to_sell() {
640 let order_params = buy_order();
641 let slippage_bps = 200u32;
642 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
643 order_params: order_params.clone(),
644 slippage_percent_bps: slippage_bps,
645 partner_fee_bps: None,
646 protocol_fee_bps: None,
647 });
648 let sell_after_network = order_params.sell_amount + order_params.fee_amount;
649 let slippage = sell_after_network * U256::from(slippage_bps) / U256::from(ONE_HUNDRED_BPS);
650 assert_eq!(result.after_slippage.sell_amount, sell_after_network + slippage);
651 }
652
653 #[test]
656 fn sell_protocol_fee_calculated_correctly() {
657 let order_params = sell_order();
658 let protocol_fee_bps = 20.0;
659 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
660 order_params: order_params.clone(),
661 slippage_percent_bps: 0,
662 partner_fee_bps: None,
663 protocol_fee_bps: Some(protocol_fee_bps),
664 });
665 let bps = U256::from(protocol_fee_bps as u64);
666 let denominator = U256::from(ONE_HUNDRED_BPS) - bps;
667 let expected = order_params.buy_amount * bps / denominator;
668 assert_eq!(result.costs.protocol_fee.amount, expected);
669 }
670
671 #[test]
672 fn buy_protocol_fee_calculated_correctly() {
673 let order_params = buy_order();
674 let protocol_fee_bps = 20.0;
675 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
676 order_params: order_params.clone(),
677 slippage_percent_bps: 0,
678 partner_fee_bps: None,
679 protocol_fee_bps: Some(protocol_fee_bps),
680 });
681 let sell_after_network = order_params.sell_amount + order_params.fee_amount;
682 let bps = U256::from(protocol_fee_bps as u64);
683 let denominator = U256::from(ONE_HUNDRED_BPS) + bps;
684 let expected = sell_after_network * bps / denominator;
685 assert_eq!(result.costs.protocol_fee.amount, expected);
686 }
687
688 #[test]
689 fn sell_before_all_fees_includes_protocol_fee_once() {
690 let order_params = sell_order();
691 let protocol_fee_bps = 20.0;
692 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
693 order_params,
694 slippage_percent_bps: 0,
695 partner_fee_bps: None,
696 protocol_fee_bps: Some(protocol_fee_bps),
697 });
698 assert_eq!(
699 result.before_all_fees.buy_amount,
700 result.before_network_costs.buy_amount + result.costs.protocol_fee.amount
701 );
702 }
703
704 #[test]
705 fn buy_before_all_fees_includes_protocol_fee_once() {
706 let order_params = buy_order();
707 let protocol_fee_bps = 20.0;
708 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
709 order_params,
710 slippage_percent_bps: 0,
711 partner_fee_bps: None,
712 protocol_fee_bps: Some(protocol_fee_bps),
713 });
714 assert_eq!(
715 result.before_all_fees.sell_amount,
716 result.before_network_costs.sell_amount - result.costs.protocol_fee.amount
717 );
718 }
719
720 #[test]
721 fn sell_fractional_protocol_fee_bps() {
722 let order_params = sell_order();
723 let protocol_fee_bps: f64 = 0.003;
724 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
725 order_params: order_params.clone(),
726 slippage_percent_bps: 0,
727 partner_fee_bps: None,
728 protocol_fee_bps: Some(protocol_fee_bps),
729 });
730 let bps = U256::from((protocol_fee_bps * HUNDRED_THOUSANDS as f64) as u64);
732 let denominator = U256::from(ONE_HUNDRED_BPS) * U256::from(HUNDRED_THOUSANDS) - bps;
733 let expected = order_params.buy_amount * bps / denominator;
734 assert_eq!(result.costs.protocol_fee.amount, expected);
735 assert_eq!(result.costs.protocol_fee.amount, U256::from(5589u64));
737 }
738
739 #[test]
740 fn decimal_protocol_fee_bps_from_string_like_ts_test() {
741 let order_params = sell_order();
745 let protocol_fee_bps: f64 = "0.3".parse().expect("parse fee bps");
746 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
747 order_params: order_params.clone(),
748 slippage_percent_bps: 0,
749 partner_fee_bps: None,
750 protocol_fee_bps: Some(protocol_fee_bps),
751 });
752 let bps = U256::from((protocol_fee_bps * HUNDRED_THOUSANDS as f64).round() as u64);
753 assert_eq!(bps, U256::from(30_000u64));
754 let denominator = U256::from(ONE_HUNDRED_BPS) * U256::from(HUNDRED_THOUSANDS) - bps;
755 let expected = order_params.buy_amount * bps / denominator;
756 assert_eq!(result.costs.protocol_fee.amount, expected);
757 }
758
759 #[test]
760 fn zero_or_negative_protocol_fee_bps_returns_zero() {
761 let order_params = sell_order();
762 for bps in [0.0, -0.1, -5.0] {
763 let fee = get_protocol_fee_amount(&ProtocolFeeAmountParams {
764 order_params: order_params.clone(),
765 protocol_fee_bps: bps,
766 });
767 assert_eq!(fee, U256::ZERO, "expected 0 for bps={bps}");
768 }
769 }
770
771 #[test]
772 fn buy_fractional_protocol_fee_bps() {
773 let order_params = buy_order();
774 let protocol_fee_bps: f64 = 0.00071;
775 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
776 order_params,
777 slippage_percent_bps: 0,
778 partner_fee_bps: None,
779 protocol_fee_bps: Some(protocol_fee_bps),
780 });
781 assert_eq!(result.costs.protocol_fee.amount, U256::from(12_206_189_769u64));
783 }
784
785 #[test]
786 fn sell_partner_fee_with_protocol_fee() {
787 let order_params = sell_order();
788 let protocol_fee_bps = 20.0;
789 let partner_fee_bps = 100u32;
790 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
791 order_params: order_params.clone(),
792 slippage_percent_bps: 0,
793 partner_fee_bps: Some(partner_fee_bps),
794 protocol_fee_bps: Some(protocol_fee_bps),
795 });
796
797 let buy_after = order_params.buy_amount;
798 let protocol_bps = U256::from(protocol_fee_bps as u64);
799 let protocol_denom = U256::from(ONE_HUNDRED_BPS) - protocol_bps;
800 let protocol_fee = buy_after * protocol_bps / protocol_denom;
801
802 let network_cost_in_buy = buy_after * order_params.fee_amount / order_params.sell_amount;
803 let buy_before_all_fees = buy_after + network_cost_in_buy + protocol_fee;
804 let expected_partner =
805 buy_before_all_fees * U256::from(partner_fee_bps) / U256::from(ONE_HUNDRED_BPS);
806 assert_eq!(result.costs.partner_fee.amount, expected_partner);
807 assert_eq!(result.after_partner_fees.buy_amount, buy_after - expected_partner);
808 }
809
810 #[test]
811 fn buy_partner_fee_with_protocol_fee() {
812 let order_params = buy_order();
813 let protocol_fee_bps = 20.0;
814 let partner_fee_bps = 100u32;
815 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
816 order_params: order_params.clone(),
817 slippage_percent_bps: 0,
818 partner_fee_bps: Some(partner_fee_bps),
819 protocol_fee_bps: Some(protocol_fee_bps),
820 });
821
822 let sell_amount = order_params.sell_amount;
823 let fee_amount = order_params.fee_amount;
824 let sell_after_network = sell_amount + fee_amount;
825
826 let protocol_bps = U256::from(protocol_fee_bps as u64);
827 let protocol_denom = U256::from(ONE_HUNDRED_BPS) + protocol_bps;
828 let protocol_fee = sell_after_network * protocol_bps / protocol_denom;
829
830 let sell_before_all_fees = sell_amount - protocol_fee;
831 let expected_partner =
832 sell_before_all_fees * U256::from(partner_fee_bps) / U256::from(ONE_HUNDRED_BPS);
833 assert_eq!(result.costs.partner_fee.amount, expected_partner);
834 assert_eq!(result.after_partner_fees.sell_amount, sell_after_network + expected_partner);
835 }
836
837 #[test]
840 fn sell_protocol_fee_zero_bps() {
841 let order_params = sell_order();
842 let amount = get_protocol_fee_amount(&ProtocolFeeAmountParams {
843 order_params,
844 protocol_fee_bps: 0.0,
845 });
846 assert_eq!(amount, U256::ZERO);
847 }
848
849 #[test]
850 fn sell_protocol_fee_negative_bps() {
851 let order_params = sell_order();
852 let amount = get_protocol_fee_amount(&ProtocolFeeAmountParams {
853 order_params,
854 protocol_fee_bps: -1.0,
855 });
856 assert_eq!(amount, U256::ZERO);
857 }
858
859 #[test]
862 fn sell_order_zero_sell_amount() {
863 let order_params = QuoteOrderParams {
864 kind: OrderKind::Sell,
865 sell_amount: U256::ZERO,
866 buy_amount: U256::from(1000u64),
867 fee_amount: U256::from(100u64),
868 };
869 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
870 order_params,
871 slippage_percent_bps: 50,
872 partner_fee_bps: None,
873 protocol_fee_bps: None,
874 });
875 assert_eq!(result.costs.network_fee.amount_in_buy_currency, U256::ZERO);
876 }
877
878 #[test]
881 fn partner_fee_zero_bps() {
882 let amounts =
883 QuoteAmounts { sell_amount: U256::from(1000u64), buy_amount: U256::from(500u64) };
884 let result = get_quote_amounts_after_partner_fee(&amounts, &amounts, true, 0);
885 assert_eq!(result.partner_fee_amount, U256::ZERO);
886 assert_eq!(result.after_partner_fees.buy_amount, U256::from(500u64));
887 }
888
889 #[test]
892 fn sell_slippage_reduces_buy_amount() {
893 let amounts =
894 QuoteAmounts { sell_amount: U256::from(1000u64), buy_amount: U256::from(10_000u64) };
895 let result = get_quote_amounts_after_slippage(&amounts, true, 100); assert_eq!(result.sell_amount, U256::from(1000u64));
897 assert!(result.buy_amount < U256::from(10_000u64));
898 }
899
900 #[test]
901 fn buy_slippage_increases_sell_amount() {
902 let amounts =
903 QuoteAmounts { sell_amount: U256::from(10_000u64), buy_amount: U256::from(1000u64) };
904 let result = get_quote_amounts_after_slippage(&amounts, false, 100); assert!(result.sell_amount > U256::from(10_000u64));
906 assert_eq!(result.buy_amount, U256::from(1000u64));
907 }
908
909 #[test]
912 fn buy_order_amounts_to_sign_correct() {
913 let order_params = buy_order();
914 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
915 order_params,
916 slippage_percent_bps: 50,
917 partner_fee_bps: Some(50),
918 protocol_fee_bps: Some(10.0),
919 });
920 assert_eq!(result.amounts_to_sign.sell_amount, result.after_slippage.sell_amount);
922 assert_eq!(result.amounts_to_sign.buy_amount, result.before_all_fees.buy_amount);
924 assert!(!result.is_sell);
925 }
926
927 #[test]
928 fn sell_order_amounts_to_sign_correct() {
929 let order_params = sell_order();
930 let result = get_quote_amounts_and_costs(&QuoteAmountsAndCostsParams {
931 order_params,
932 slippage_percent_bps: 50,
933 partner_fee_bps: Some(50),
934 protocol_fee_bps: Some(10.0),
935 });
936 assert_eq!(result.amounts_to_sign.sell_amount, result.before_all_fees.sell_amount);
938 assert_eq!(result.amounts_to_sign.buy_amount, result.after_slippage.buy_amount);
940 assert!(result.is_sell);
941 }
942
943 #[test]
946 fn protocol_fee_bps_below_scaling_resolution_returns_zero() {
947 let result = get_protocol_fee_amount(&ProtocolFeeAmountParams {
952 order_params: sell_order(),
953 protocol_fee_bps: 1e-6,
954 });
955 assert_eq!(result, U256::ZERO);
956 }
957
958 #[test]
959 fn transform_order_with_ethflow_data_overrides_owner_and_sell_token() {
960 let onchain_user = "0x2222222222222222222222222222222222222222"
966 .parse::<alloy_primitives::Address>()
967 .unwrap();
968 let json = serde_json::json!({
969 "uid": "0xmockuid",
970 "sellToken": "0xfff9976782d46cc05630d1f6ebab18b2324d6b14",
971 "buyToken": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238",
972 "receiver": "0x0000000000000000000000000000000000000000",
973 "sellAmount": "1000",
974 "buyAmount": "500",
975 "validTo": 1_700_000_000u32,
976 "appData": "0x0000000000000000000000000000000000000000000000000000000000000000",
977 "feeAmount": "10",
978 "kind": "sell",
979 "partiallyFillable": false,
980 "creationDate": "2024-01-01T00:00:00Z",
981 "owner": "0x0000000000000000000000000000000000000000",
982 "executedSellAmount": "100",
983 "executedSellAmountBeforeFees": "100",
984 "executedBuyAmount": "50",
985 "executedFeeAmount": "5",
986 "invalidated": false,
987 "status": "open",
988 "signingScheme": "eip712",
989 "signature": "0x",
990 "ethflowData": { "userValidTo": 1_999_999_999u32, "isRefundClaimed": false },
991 "onchainUser": format!("{onchain_user:#x}"),
992 });
993 let order: super::super::Order = serde_json::from_value(json).unwrap();
994 let enriched = transform_order(order);
995
996 assert_eq!(enriched.valid_to, 1_999_999_999);
998 assert_eq!(enriched.owner, onchain_user);
1000 assert_eq!(enriched.sell_token, cow_chains::NATIVE_CURRENCY_ADDRESS);
1003 }
1004
1005 #[test]
1006 fn transform_order_with_ethflow_data_without_onchain_user_keeps_owner() {
1007 let original_owner = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
1011 .parse::<alloy_primitives::Address>()
1012 .unwrap();
1013 let json = serde_json::json!({
1014 "uid": "0xmockuid",
1015 "sellToken": "0xfff9976782d46cc05630d1f6ebab18b2324d6b14",
1016 "buyToken": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238",
1017 "receiver": "0x0000000000000000000000000000000000000000",
1018 "sellAmount": "1000",
1019 "buyAmount": "500",
1020 "validTo": 1_700_000_000u32,
1021 "appData": "0x0000000000000000000000000000000000000000000000000000000000000000",
1022 "feeAmount": "10",
1023 "kind": "sell",
1024 "partiallyFillable": false,
1025 "creationDate": "2024-01-01T00:00:00Z",
1026 "owner": format!("{original_owner:#x}"),
1027 "executedSellAmount": "100",
1028 "executedSellAmountBeforeFees": "100",
1029 "executedBuyAmount": "50",
1030 "executedFeeAmount": "5",
1031 "invalidated": false,
1032 "status": "open",
1033 "signingScheme": "eip712",
1034 "signature": "0x",
1035 "ethflowData": { "userValidTo": 42, "isRefundClaimed": true },
1036 });
1037 let order: super::super::Order = serde_json::from_value(json).unwrap();
1038 let enriched = transform_order(order);
1039 assert_eq!(enriched.valid_to, 42);
1040 assert_eq!(enriched.owner, original_owner); assert_eq!(enriched.sell_token, cow_chains::NATIVE_CURRENCY_ADDRESS);
1042 }
1043
1044 #[test]
1045 fn transform_order_sets_total_fee() {
1046 let json = serde_json::json!({
1047 "uid": "0xmockuid",
1048 "sellToken": "0xfff9976782d46cc05630d1f6ebab18b2324d6b14",
1049 "buyToken": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238",
1050 "receiver": "0x0000000000000000000000000000000000000000",
1051 "sellAmount": "1000",
1052 "buyAmount": "500",
1053 "validTo": 1700000000u32,
1054 "appData": "0x0000000000000000000000000000000000000000000000000000000000000000",
1055 "feeAmount": "10",
1056 "kind": "sell",
1057 "partiallyFillable": false,
1058 "creationDate": "2024-01-01T00:00:00Z",
1059 "owner": "0x0000000000000000000000000000000000000000",
1060 "executedSellAmount": "100",
1061 "executedSellAmountBeforeFees": "100",
1062 "executedBuyAmount": "50",
1063 "executedFeeAmount": "5",
1064 "invalidated": false,
1065 "status": "open",
1066 "signingScheme": "eip712",
1067 "signature": "0x",
1068 });
1069 let order: super::super::Order = serde_json::from_value(json).unwrap();
1070 let enriched = transform_order(order);
1071 assert!(enriched.total_fee.is_some());
1072 assert_eq!(enriched.total_fee.unwrap(), "5");
1073 }
1074}