1#[cfg(feature = "floats")]
2use libm::pow;
3
4#[cfg(feature = "wasm")]
5use riptide_amm_macros::wasm_expose;
6
7use super::error::{CoreError, AMOUNT_EXCEEDS_MAX_U64, ARITHMETIC_OVERFLOW};
8
9use super::U128;
10
11#[cfg(feature = "floats")]
12#[cfg_attr(feature = "wasm", wasm_expose)]
13pub fn amount_to_ui_amount(amount: u64, decimals: u8) -> f64 {
14 let power = pow(10f64, decimals as f64);
15 amount as f64 / power
16}
17
18#[cfg(feature = "floats")]
19#[cfg_attr(feature = "wasm", wasm_expose)]
20pub fn ui_amount_to_amount(amount: f64, decimals: u8) -> u64 {
21 let power = pow(10f64, decimals as f64);
22 (amount * power) as u64
23}
24
25#[cfg_attr(feature = "wasm", wasm_expose)]
35pub fn a_to_b(amount_a: u64, price: U128, round_up: bool) -> Result<u64, CoreError> {
36 let price: u128 = price.into();
37
38 let product = u128::from(amount_a)
39 .checked_mul(price)
40 .ok_or(ARITHMETIC_OVERFLOW)?;
41
42 let quotient = product >> 64;
43 let remainder = product as u64;
44
45 let result = if round_up && remainder > 0 {
46 quotient + 1
47 } else {
48 quotient
49 };
50
51 result.try_into().map_err(|_| AMOUNT_EXCEEDS_MAX_U64)
52}
53
54#[cfg_attr(feature = "wasm", wasm_expose)]
64pub fn b_to_a(amount_b: u64, price: U128, round_up: bool) -> Result<u64, CoreError> {
65 let price: u128 = price.into();
66 if price == 0 {
67 return Ok(0);
68 }
69
70 let numerator = u128::from(amount_b)
71 .checked_shl(64)
72 .ok_or(ARITHMETIC_OVERFLOW)?;
73
74 let quotient = numerator.checked_div(price).ok_or(ARITHMETIC_OVERFLOW)?;
75 let remainder = numerator.checked_rem(price).ok_or(ARITHMETIC_OVERFLOW)?;
76
77 let result = if round_up && remainder > 0 {
78 quotient + 1
79 } else {
80 quotient
81 };
82
83 result.try_into().map_err(|_| AMOUNT_EXCEEDS_MAX_U64)
84}
85
86#[cfg(all(test, feature = "lib"))]
87mod tests {
88 use super::*;
89 use rstest::rstest;
90
91 #[rstest]
92 #[case(1000000000, 9, 1.0)]
93 #[case(1000000000, 6, 1000.0)]
94 #[case(1000000000, 3, 1000000.0)]
95 fn test_amount_to_ui_amount(
96 #[case] amount: u64,
97 #[case] decimals: u8,
98 #[case] expected_ui_amount: f64,
99 ) {
100 let ui_amount = amount_to_ui_amount(amount, decimals);
101 assert_eq!(ui_amount, expected_ui_amount);
102 }
103
104 #[rstest]
105 #[case(1.0, 9, 1000000000)]
106 #[case(1000.0, 6, 1000000000)]
107 #[case(1000000.0, 3, 1000000000)]
108 fn test_ui_amount_to_amount(
109 #[case] ui_amount: f64,
110 #[case] decimals: u8,
111 #[case] expected_amount: u64,
112 ) {
113 let amount = ui_amount_to_amount(ui_amount, decimals);
114 assert_eq!(amount, expected_amount);
115 }
116
117 #[rstest]
118 #[case(100, 1 << 64, true, Ok(100))]
119 #[case(100, 1 << 64, false, Ok(100))]
120 #[case(100, 8 << 64, true, Ok(800))]
121 #[case(100, 8 << 64, false, Ok(800))]
122 #[case(100, (1 << 64) / 8, true, Ok(13))]
123 #[case(100, (1 << 64) / 8, false, Ok(12))]
124 #[case(100, 0, true, Ok(0))]
125 #[case(0, 1 << 64, true, Ok(0))]
126 fn test_a_to_b(
127 #[case] amount_a: u64,
128 #[case] price: U128,
129 #[case] round_up: bool,
130 #[case] expected: Result<u64, CoreError>,
131 ) {
132 let result = a_to_b(amount_a, price, round_up);
133 assert_eq!(result, expected);
134 }
135
136 #[rstest]
137 #[case(100, 1 << 64, true, Ok(100))]
138 #[case(100, 1 << 64, false, Ok(100))]
139 #[case(100, 8 << 64, true, Ok(13))]
140 #[case(100, 8 << 64, false, Ok(12))]
141 #[case(100, (1 << 64) / 8, true, Ok(800))]
142 #[case(100, (1 << 64) / 8, false, Ok(800))]
143 #[case(100, 0, true, Ok(0))]
144 #[case(0, 1 << 64, true, Ok(0))]
145 fn test_b_to_a(
146 #[case] amount_b: u64,
147 #[case] price: U128,
148 #[case] round_up: bool,
149 #[case] expected: Result<u64, CoreError>,
150 ) {
151 let result = b_to_a(amount_b, price, round_up);
152 assert_eq!(result, expected);
153 }
154}