Skip to content

Commit aee4b4e

Browse files
committed
fix: highest balance check
1 parent 124a6f5 commit aee4b4e

File tree

3 files changed

+213
-3
lines changed

3 files changed

+213
-3
lines changed

src/handlers/chain_agnostic/mod.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use {
66
tenderly::{AssetChangeType, TokenStandard},
77
SimulationProvider,
88
},
9-
utils::crypto::get_erc20_balance,
9+
utils::{crypto::get_erc20_balance, token_amount::TokenAmount},
1010
Metrics,
1111
},
1212
alloy::primitives::{Address, B256, U256},
@@ -261,7 +261,9 @@ pub async fn check_bridging_for_erc20_transfer(
261261
.await?;
262262
for (account, contract_address, current_balance) in erc20_balances {
263263
// Check if the balance compared to the transfer value is enough, applied to the transfer token decimals
264-
if convert_amount(current_balance, decimals, amount_token_decimals) >= value {
264+
if TokenAmount::new(current_balance, decimals)
265+
>= TokenAmount::new(value, amount_token_decimals)
266+
{
265267
// Use the priority asset if found
266268
if token_symbol == token_symbol_priority {
267269
return Ok(Some(BridgingAsset {
@@ -277,10 +279,13 @@ pub async fn check_bridging_for_erc20_transfer(
277279
// or use the asset with the highest found balance
278280
if let Some(BridgingAsset {
279281
current_balance: existing_balance,
282+
decimals: existing_decimals,
280283
..
281284
}) = &bridging_asset_found
282285
{
283-
if current_balance <= *existing_balance {
286+
if TokenAmount::new(current_balance, decimals)
287+
<= TokenAmount::new(*existing_balance, *existing_decimals)
288+
{
284289
continue;
285290
}
286291
}

src/utils/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod permissions;
77
pub mod rate_limit;
88
pub mod sessions;
99
pub mod simple_request_json;
10+
pub mod token_amount;
1011

1112
pub fn generate_random_string(len: usize) -> String {
1213
let rng = rand::thread_rng();

src/utils/token_amount.rs

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
use alloy::primitives::U256;
2+
use std::cmp::Ordering;
3+
4+
/// Basically a BigInt utility, but restricted to U256 and u8 types
5+
#[derive(Debug, Clone)]
6+
pub struct TokenAmount {
7+
amount: U256,
8+
decimals: u8,
9+
}
10+
11+
impl TokenAmount {
12+
pub fn new(amount: U256, decimals: u8) -> Self {
13+
Self { amount, decimals }
14+
}
15+
}
16+
17+
impl PartialOrd for TokenAmount {
18+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
19+
Some(compare_amounts(
20+
self.amount,
21+
other.amount,
22+
self.decimals,
23+
other.decimals,
24+
))
25+
}
26+
}
27+
28+
impl PartialEq for TokenAmount {
29+
fn eq(&self, other: &Self) -> bool {
30+
self.partial_cmp(other) == Some(Ordering::Equal)
31+
}
32+
}
33+
34+
/// Compare two amounts with different decimals
35+
fn compare_amounts(a_amount: U256, b_amount: U256, a_decimals: u8, b_decimals: u8) -> Ordering {
36+
// Same assertion as alloy's Unit type
37+
assert!(a_decimals <= 77);
38+
assert!(b_decimals <= 77);
39+
40+
let base_10 = U256::from(10);
41+
match a_decimals.cmp(&b_decimals) {
42+
Ordering::Equal => a_amount.cmp(&b_amount),
43+
Ordering::Greater => {
44+
// A has more decimals than B
45+
let diff = a_decimals - b_decimals;
46+
let exp = U256::from(diff as u64);
47+
let factor = base_10.checked_pow(exp);
48+
if let Some(factor) = factor {
49+
let b_amount_adjusted = b_amount.checked_mul(factor);
50+
if let Some(b_amount_adjusted) = b_amount_adjusted {
51+
a_amount.cmp(&b_amount_adjusted)
52+
} else {
53+
Ordering::Less
54+
}
55+
} else {
56+
// Branch should never be reached because 2^256 > 10^77
57+
Ordering::Less
58+
}
59+
}
60+
Ordering::Less => {
61+
// A has less decimals than B
62+
let diff = b_decimals - a_decimals;
63+
let exp = U256::from(diff as u64);
64+
let factor = base_10.checked_pow(exp);
65+
if let Some(factor) = factor {
66+
let a_amount_adjusted = a_amount.checked_mul(factor);
67+
if let Some(a_amount_adjusted) = a_amount_adjusted {
68+
a_amount_adjusted.cmp(&b_amount)
69+
} else {
70+
Ordering::Greater
71+
}
72+
} else {
73+
// Branch should never be reached because 2^256 > 10^77
74+
Ordering::Greater
75+
}
76+
}
77+
}
78+
}
79+
80+
#[cfg(test)]
81+
#[allow(clippy::neg_cmp_op_on_partial_ord)]
82+
mod tests {
83+
use super::*;
84+
85+
#[test]
86+
fn test_equal_amounts_0() {
87+
let a_amount = TokenAmount {
88+
amount: U256::from(0),
89+
decimals: 0,
90+
};
91+
let b_amount = TokenAmount {
92+
amount: U256::from(0),
93+
decimals: 0,
94+
};
95+
assert_eq!(a_amount, b_amount);
96+
assert_eq!(a_amount.partial_cmp(&b_amount), Some(Ordering::Equal));
97+
assert!(a_amount <= b_amount);
98+
assert!(a_amount >= b_amount);
99+
assert!(b_amount <= a_amount);
100+
assert!(b_amount >= a_amount);
101+
assert!(!(a_amount < b_amount));
102+
assert!(!(a_amount > b_amount));
103+
}
104+
105+
#[test]
106+
fn test_equal_amounts_different_decimals() {
107+
let a_amount = TokenAmount {
108+
amount: U256::from(0),
109+
decimals: 10,
110+
};
111+
let b_amount = TokenAmount {
112+
amount: U256::from(0),
113+
decimals: 0,
114+
};
115+
assert_eq!(a_amount, b_amount);
116+
assert_eq!(a_amount.partial_cmp(&b_amount), Some(Ordering::Equal));
117+
assert!(a_amount <= b_amount);
118+
assert!(a_amount >= b_amount);
119+
assert!(b_amount <= a_amount);
120+
assert!(b_amount >= a_amount);
121+
assert!(!(a_amount < b_amount));
122+
assert!(!(a_amount > b_amount));
123+
}
124+
125+
#[test]
126+
fn test_different_amounts() {
127+
let a_amount = TokenAmount {
128+
amount: U256::from(1),
129+
decimals: 0,
130+
};
131+
let b_amount = TokenAmount {
132+
amount: U256::from(0),
133+
decimals: 0,
134+
};
135+
assert!(a_amount > b_amount);
136+
assert!(a_amount >= b_amount);
137+
assert!(!(a_amount < b_amount));
138+
assert!(!(a_amount <= b_amount));
139+
assert!(!(a_amount == b_amount));
140+
assert!(b_amount < a_amount);
141+
assert!(b_amount <= a_amount);
142+
assert!(!(b_amount > a_amount));
143+
assert!(!(b_amount >= a_amount));
144+
assert!(!(b_amount == a_amount));
145+
}
146+
147+
#[test]
148+
fn test_different_decimals() {
149+
let a_amount = TokenAmount {
150+
amount: U256::from(1),
151+
decimals: 0,
152+
};
153+
let b_amount = TokenAmount {
154+
amount: U256::from(1),
155+
decimals: 1,
156+
};
157+
assert!(a_amount > b_amount);
158+
assert!(a_amount >= b_amount);
159+
assert!(!(a_amount < b_amount));
160+
assert!(!(a_amount <= b_amount));
161+
assert!(!(a_amount == b_amount));
162+
assert!(b_amount < a_amount);
163+
assert!(b_amount <= a_amount);
164+
assert!(!(b_amount > a_amount));
165+
assert!(!(b_amount >= a_amount));
166+
assert!(!(b_amount == a_amount));
167+
}
168+
169+
#[test]
170+
fn test_overflow_amount() {
171+
let a_amount_amount = U256::from(1000000);
172+
let a_amount_decimals = 0;
173+
let b_amount_amount = U256::from(1);
174+
let b_amount_decimals = 77;
175+
assert!(U256::from(10)
176+
.checked_pow(U256::from(b_amount_decimals - a_amount_decimals))
177+
.is_some());
178+
assert!(a_amount_amount
179+
.checked_mul(U256::from(
180+
U256::from(10)
181+
.checked_pow(U256::from(b_amount_decimals - a_amount_decimals))
182+
.unwrap()
183+
))
184+
.is_none());
185+
let a_amount = TokenAmount {
186+
amount: a_amount_amount,
187+
decimals: a_amount_decimals,
188+
};
189+
let b_amount = TokenAmount {
190+
amount: b_amount_amount,
191+
decimals: b_amount_decimals,
192+
};
193+
assert!(a_amount > b_amount);
194+
assert!(a_amount >= b_amount);
195+
assert!(!(a_amount < b_amount));
196+
assert!(!(a_amount <= b_amount));
197+
assert!(!(a_amount == b_amount));
198+
assert!(b_amount < a_amount);
199+
assert!(b_amount <= a_amount);
200+
assert!(!(b_amount > a_amount));
201+
assert!(!(b_amount >= a_amount));
202+
assert!(!(b_amount == a_amount));
203+
}
204+
}

0 commit comments

Comments
 (0)