ethers_abi/token/
lenient.rs1use crate::{
10 errors::Error,
11 token::{StrictTokenizer, Tokenizer},
12 Uint,
13};
14use std::borrow::Cow;
15
16use once_cell::sync::Lazy;
17static RE: Lazy<regex::Regex> = Lazy::new(|| {
18 regex::Regex::new(r"^([0-9]+)(\.[0-9]+)?\s*(ether|gwei|nanoether|nano|wei)$")
19 .expect("invalid regex")
20});
21
22pub struct LenientTokenizer;
24
25impl Tokenizer for LenientTokenizer {
26 fn tokenize_address(value: &str) -> Result<[u8; 20], Error> {
27 StrictTokenizer::tokenize_address(value)
28 }
29
30 fn tokenize_string(value: &str) -> Result<String, Error> {
31 StrictTokenizer::tokenize_string(value)
32 }
33
34 fn tokenize_bool(value: &str) -> Result<bool, Error> {
35 StrictTokenizer::tokenize_bool(value)
36 }
37
38 fn tokenize_bytes(value: &str) -> Result<Vec<u8>, Error> {
39 StrictTokenizer::tokenize_bytes(value)
40 }
41
42 fn tokenize_fixed_bytes(value: &str, len: usize) -> Result<Vec<u8>, Error> {
43 StrictTokenizer::tokenize_fixed_bytes(value, len)
44 }
45
46 fn tokenize_uint(value: &str) -> Result<[u8; 32], Error> {
47 let result = StrictTokenizer::tokenize_uint(value);
48 if result.is_ok() {
49 return result;
50 }
51
52 let uint = match Uint::from_dec_str(value) {
56 Ok(_uint) => _uint,
57 Err(dec_error) => {
58 let original_dec_error = dec_error.to_string();
59
60 match RE.captures(value) {
61 Some(captures) => {
62 let integer = captures
63 .get(1)
64 .expect("capture group does not exist")
65 .as_str();
66 let fract = captures
67 .get(2)
68 .map(|c| c.as_str().trim_start_matches('.'))
69 .unwrap_or_else(|| "");
70 let units = captures
71 .get(3)
72 .expect("capture group does not exist")
73 .as_str();
74
75 let units = Uint::from(match units.to_lowercase().as_str() {
76 "ether" => 18,
77 "gwei" | "nano" | "nanoether" => 9,
78 "wei" => 0,
79 _ => return Err(dec_error.into()),
80 });
81
82 let integer =
83 Uint::from_dec_str(integer)?.checked_mul(Uint::from(10u32).pow(units));
84
85 if fract.is_empty() {
86 integer.ok_or(dec_error)?
87 } else {
88 let fract_pow = units
90 .checked_sub(Uint::from(fract.len()))
91 .ok_or(dec_error)?;
92
93 let fract = Uint::from_dec_str(fract)?
94 .checked_mul(Uint::from(10u32).pow(fract_pow))
95 .ok_or_else(|| {
96 Error::Other(Cow::Owned(original_dec_error.clone()))
97 })?;
98
99 integer
100 .and_then(|integer| integer.checked_add(fract))
101 .ok_or(Error::Other(Cow::Owned(original_dec_error)))?
102 }
103 }
104 None => return Err(dec_error.into()),
105 }
106 }
107 };
108
109 Ok(uint.into())
110 }
111
112 fn tokenize_int(value: &str) -> Result<[u8; 32], Error> {
116 let result = StrictTokenizer::tokenize_int(value);
117 if result.is_ok() {
118 return result;
119 }
120
121 let abs = Uint::from_dec_str(value.trim_start_matches('-'))?;
122 let max = Uint::max_value() / 2;
123 let int = if value.starts_with('-') {
124 if abs.is_zero() {
125 return Ok(abs.into());
126 } else if abs > max + 1 {
127 return Err(Error::Other(Cow::Borrowed("int256 parse error: Underflow")));
128 }
129 !abs + 1 } else {
131 if abs > max {
132 return Err(Error::Other(Cow::Borrowed("int256 parse error: Overflow")));
133 }
134 abs
135 };
136 Ok(int.into())
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use ethereum_types::FromDecStrErr;
143
144 use crate::{
145 errors::Error,
146 token::{LenientTokenizer, Token, Tokenizer},
147 ParamType, Uint,
148 };
149
150 #[test]
151 fn tokenize_uint() {
152 assert_eq!(
153 LenientTokenizer::tokenize(
154 &ParamType::Uint(256),
155 "1111111111111111111111111111111111111111111111111111111111111111"
156 )
157 .unwrap(),
158 Token::Uint([0x11u8; 32].into())
159 );
160 }
161
162 #[test]
163 fn tokenize_uint_wei() {
164 assert_eq!(
165 LenientTokenizer::tokenize(&ParamType::Uint(256), "1wei").unwrap(),
166 Token::Uint(Uint::from(1))
167 );
168
169 assert_eq!(
170 LenientTokenizer::tokenize(&ParamType::Uint(256), "1 wei").unwrap(),
171 Token::Uint(Uint::from(1))
172 );
173 }
174
175 #[test]
176 fn tokenize_uint_gwei() {
177 assert_eq!(
178 LenientTokenizer::tokenize(&ParamType::Uint(256), "1nano").unwrap(),
179 Token::Uint(Uint::from_dec_str("1000000000").unwrap())
180 );
181
182 assert_eq!(
183 LenientTokenizer::tokenize(&ParamType::Uint(256), "1nanoether").unwrap(),
184 Token::Uint(Uint::from_dec_str("1000000000").unwrap())
185 );
186
187 assert_eq!(
188 LenientTokenizer::tokenize(&ParamType::Uint(256), "1gwei").unwrap(),
189 Token::Uint(Uint::from_dec_str("1000000000").unwrap())
190 );
191
192 assert_eq!(
193 LenientTokenizer::tokenize(&ParamType::Uint(256), "0.1 gwei").unwrap(),
194 Token::Uint(Uint::from_dec_str("100000000").unwrap())
195 );
196 }
197
198 #[test]
199 fn tokenize_uint_ether() {
200 assert_eq!(
201 LenientTokenizer::tokenize(&ParamType::Uint(256), "10000000000ether").unwrap(),
202 Token::Uint(Uint::from_dec_str("10000000000000000000000000000").unwrap())
203 );
204
205 assert_eq!(
206 LenientTokenizer::tokenize(&ParamType::Uint(256), "1ether").unwrap(),
207 Token::Uint(Uint::from_dec_str("1000000000000000000").unwrap())
208 );
209
210 assert_eq!(
211 LenientTokenizer::tokenize(&ParamType::Uint(256), "0.01 ether").unwrap(),
212 Token::Uint(Uint::from_dec_str("10000000000000000").unwrap())
213 );
214
215 assert_eq!(
216 LenientTokenizer::tokenize(&ParamType::Uint(256), "0.000000000000000001ether").unwrap(),
217 Token::Uint(Uint::from_dec_str("1").unwrap())
218 );
219
220 assert_eq!(
221 LenientTokenizer::tokenize(&ParamType::Uint(256), "0.000000000000000001ether").unwrap(),
222 LenientTokenizer::tokenize(&ParamType::Uint(256), "1wei").unwrap(),
223 );
224 }
225
226 #[test]
227 fn tokenize_uint_array_ether() {
228 assert_eq!(
229 LenientTokenizer::tokenize(
230 &ParamType::Array(Box::new(ParamType::Uint(256))),
231 "[1ether,0.1 ether]"
232 )
233 .unwrap(),
234 Token::Array(vec![
235 Token::Uint(Uint::from_dec_str("1000000000000000000").unwrap()),
236 Token::Uint(Uint::from_dec_str("100000000000000000").unwrap())
237 ])
238 );
239 }
240
241 #[test]
242 fn tokenize_uint_invalid_units() {
243 let _error = Error::from(FromDecStrErr::InvalidCharacter);
244
245 assert!(matches!(
246 LenientTokenizer::tokenize(&ParamType::Uint(256), "0.1 wei"),
247 Err(_error)
248 ));
249
250 assert!(matches!(
252 LenientTokenizer::tokenize(&ParamType::Uint(256), "0.0000000000000000001ether"),
253 Err(_error)
254 ));
255
256 assert!(matches!(
258 LenientTokenizer::tokenize(&ParamType::Uint(256), "1.0000000000000000001ether"),
259 Err(_error)
260 ));
261
262 assert!(matches!(
264 LenientTokenizer::tokenize(
265 &ParamType::Uint(256),
266 "1000000000.0000000000000000001ether"
267 ),
268 Err(_error)
269 ));
270
271 assert!(matches!(
272 LenientTokenizer::tokenize(&ParamType::Uint(256), "0..1 gwei"),
273 Err(_error)
274 ));
275
276 assert!(matches!(
277 LenientTokenizer::tokenize(&ParamType::Uint(256), "..1 gwei"),
278 Err(_error)
279 ));
280
281 assert!(matches!(
282 LenientTokenizer::tokenize(&ParamType::Uint(256), "1. gwei"),
283 Err(_error)
284 ));
285
286 assert!(matches!(
287 LenientTokenizer::tokenize(&ParamType::Uint(256), ".1 gwei"),
288 Err(_error)
289 ));
290
291 assert!(matches!(
292 LenientTokenizer::tokenize(&ParamType::Uint(256), "2.1.1 gwei"),
293 Err(_error)
294 ));
295
296 assert!(matches!(
297 LenientTokenizer::tokenize(&ParamType::Uint(256), ".1.1 gwei"),
298 Err(_error)
299 ));
300
301 assert!(matches!(
302 LenientTokenizer::tokenize(&ParamType::Uint(256), "1abc"),
303 Err(_error)
304 ));
305
306 assert!(matches!(
307 LenientTokenizer::tokenize(&ParamType::Uint(256), "1 gwei "),
308 Err(_error)
309 ));
310
311 assert!(matches!(
312 LenientTokenizer::tokenize(&ParamType::Uint(256), "g 1 gwei"),
313 Err(_error)
314 ));
315
316 assert!(matches!(
317 LenientTokenizer::tokenize(&ParamType::Uint(256), "1gwei 1 gwei"),
318 Err(_error)
319 ));
320 }
321}