ethers_abi/token/
token.rs

1// Copyright 2015-2020 Parity Technologies
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Ethereum ABI params.
10
11use core::fmt;
12
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16#[cfg(not(feature = "std"))]
17use crate::no_std_prelude::*;
18use crate::{Address, Bytes, FixedBytes, Int, ParamType, Uint};
19
20/// Ethereum ABI params.
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22#[derive(Debug, PartialEq, Clone)]
23pub enum Token {
24	/// Address.
25	///
26	/// solidity name: address
27	/// Encoded to left padded [0u8; 32].
28	Address(Address),
29	/// Vector of bytes with known size.
30	///
31	/// solidity name eg.: bytes8, bytes32, bytes64, bytes1024
32	/// Encoded to right padded [0u8; ((N + 31) / 32) * 32].
33	FixedBytes(FixedBytes),
34	/// Vector of bytes of unknown size.
35	///
36	/// solidity name: bytes
37	/// Encoded in two parts.
38	/// Init part: offset of 'closing part`.
39	/// Closing part: encoded length followed by encoded right padded bytes.
40	Bytes(Bytes),
41	/// Signed integer.
42	///
43	/// solidity name: int
44	Int(Int),
45	/// Unsigned integer.
46	///
47	/// solidity name: uint
48	Uint(Uint),
49	/// Boolean value.
50	///
51	/// solidity name: bool
52	/// Encoded as left padded [0u8; 32], where last bit represents boolean value.
53	Bool(bool),
54	/// String.
55	///
56	/// solidity name: string
57	/// Encoded in the same way as bytes. Must be utf8 compliant.
58	String(String),
59	/// Array with known size.
60	///
61	/// solidity name eg.: int[3], bool[3], address[][8]
62	/// Encoding of array is equal to encoding of consecutive elements of array.
63	FixedArray(Vec<Token>),
64	/// Array of params with unknown size.
65	///
66	/// solidity name eg. int[], bool[], address[5][]
67	Array(Vec<Token>),
68	/// Tuple of params of variable types.
69	///
70	/// solidity name: tuple
71	Tuple(Vec<Token>),
72}
73
74impl fmt::Display for Token {
75	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76		match *self {
77			Token::Bool(b) => write!(f, "{b}"),
78			Token::String(ref s) => write!(f, "{s}"),
79			Token::Address(ref a) => write!(f, "{a:x}"),
80			Token::Bytes(ref bytes) | Token::FixedBytes(ref bytes) => write!(f, "{}", hex::encode(bytes)),
81			Token::Uint(ref i) | Token::Int(ref i) => write!(f, "{i:x}"),
82			Token::Array(ref arr) | Token::FixedArray(ref arr) => {
83				let s = arr.iter().map(|ref t| format!("{t}")).collect::<Vec<String>>().join(",");
84
85				write!(f, "[{s}]")
86			}
87			Token::Tuple(ref s) => {
88				let s = s.iter().map(|ref t| format!("{t}")).collect::<Vec<String>>().join(",");
89
90				write!(f, "({s})")
91			}
92		}
93	}
94}
95
96impl Token {
97	/// Check whether the type of the token matches the given parameter type.
98	///
99	/// Numeric types (`Int` and `Uint`) type check if the size of the token
100	/// type is of greater or equal size than the provided parameter type.
101	pub fn type_check(&self, param_type: &ParamType) -> bool {
102		match *self {
103			Token::Address(_) => *param_type == ParamType::Address,
104			Token::Bytes(_) => *param_type == ParamType::Bytes,
105			Token::Int(_) => {
106				matches!(*param_type, ParamType::Int(_))
107			}
108			Token::Uint(_) => {
109				matches!(*param_type, ParamType::Uint(_))
110			}
111			Token::Bool(_) => *param_type == ParamType::Bool,
112			Token::String(_) => *param_type == ParamType::String,
113			Token::FixedBytes(ref bytes) => {
114				if let ParamType::FixedBytes(size) = *param_type {
115					size >= bytes.len()
116				} else {
117					false
118				}
119			}
120			Token::Array(ref tokens) => {
121				if let ParamType::Array(ref param_type) = *param_type {
122					tokens.iter().all(|t| t.type_check(param_type))
123				} else {
124					false
125				}
126			}
127			Token::FixedArray(ref tokens) => {
128				if let ParamType::FixedArray(ref param_type, size) = *param_type {
129					size == tokens.len() && tokens.iter().all(|t| t.type_check(param_type))
130				} else {
131					false
132				}
133			}
134			Token::Tuple(ref tokens) => {
135				if let ParamType::Tuple(ref param_type) = *param_type {
136					tokens.iter().enumerate().all(|(i, t)| t.type_check(&param_type[i]))
137				} else {
138					false
139				}
140			}
141		}
142	}
143
144	/// Converts token to...
145	pub fn into_address(self) -> Option<Address> {
146		match self {
147			Token::Address(address) => Some(address),
148			_ => None,
149		}
150	}
151
152	/// Converts token to...
153	pub fn into_fixed_bytes(self) -> Option<Vec<u8>> {
154		match self {
155			Token::FixedBytes(bytes) => Some(bytes),
156			_ => None,
157		}
158	}
159
160	/// Converts token to...
161	pub fn into_bytes(self) -> Option<Vec<u8>> {
162		match self {
163			Token::Bytes(bytes) => Some(bytes),
164			_ => None,
165		}
166	}
167
168	/// Converts token to...
169	pub fn into_int(self) -> Option<Int> {
170		match self {
171			Token::Int(int) => Some(int),
172			_ => None,
173		}
174	}
175
176	/// Converts token to...
177	pub fn into_uint(self) -> Option<Uint> {
178		match self {
179			Token::Uint(uint) => Some(uint),
180			_ => None,
181		}
182	}
183
184	/// Converts token to...
185	pub fn into_bool(self) -> Option<bool> {
186		match self {
187			Token::Bool(b) => Some(b),
188			_ => None,
189		}
190	}
191
192	/// Converts token to...
193	pub fn into_string(self) -> Option<String> {
194		match self {
195			Token::String(s) => Some(s),
196			_ => None,
197		}
198	}
199
200	/// Converts token to...
201	pub fn into_fixed_array(self) -> Option<Vec<Token>> {
202		match self {
203			Token::FixedArray(arr) => Some(arr),
204			_ => None,
205		}
206	}
207
208	/// Converts token to...
209	pub fn into_array(self) -> Option<Vec<Token>> {
210		match self {
211			Token::Array(arr) => Some(arr),
212			_ => None,
213		}
214	}
215
216	/// Converts token to...
217	pub fn into_tuple(self) -> Option<Vec<Token>> {
218		match self {
219			Token::Tuple(tuple) => Some(tuple),
220			_ => None,
221		}
222	}
223
224	/// Check if all the types of the tokens match the given parameter types.
225	pub fn types_check(tokens: &[Token], param_types: &[ParamType]) -> bool {
226		param_types.len() == tokens.len() && {
227			param_types.iter().zip(tokens).all(|(param_type, token)| token.type_check(param_type))
228		}
229	}
230
231	/// Check if the token is a dynamic type resulting in prefixed encoding
232	pub fn is_dynamic(&self) -> bool {
233		match self {
234			Token::Bytes(_) | Token::String(_) | Token::Array(_) => true,
235			Token::FixedArray(tokens) => tokens.iter().any(|token| token.is_dynamic()),
236			Token::Tuple(tokens) => tokens.iter().any(|token| token.is_dynamic()),
237			_ => false,
238		}
239	}
240}
241
242#[cfg(test)]
243mod tests {
244	#[cfg(not(feature = "std"))]
245	use crate::no_std_prelude::*;
246	use crate::{ParamType, Token};
247
248	#[test]
249	fn test_type_check() {
250		fn assert_type_check(tokens: Vec<Token>, param_types: Vec<ParamType>) {
251			assert!(Token::types_check(&tokens, &param_types))
252		}
253
254		fn assert_not_type_check(tokens: Vec<Token>, param_types: Vec<ParamType>) {
255			assert!(!Token::types_check(&tokens, &param_types))
256		}
257
258		assert_type_check(vec![Token::Uint(0.into()), Token::Bool(false)], vec![ParamType::Uint(256), ParamType::Bool]);
259		assert_type_check(vec![Token::Uint(0.into()), Token::Bool(false)], vec![ParamType::Uint(32), ParamType::Bool]);
260
261		assert_not_type_check(vec![Token::Uint(0.into())], vec![ParamType::Uint(32), ParamType::Bool]);
262		assert_not_type_check(vec![Token::Uint(0.into()), Token::Bool(false)], vec![ParamType::Uint(32)]);
263		assert_not_type_check(
264			vec![Token::Bool(false), Token::Uint(0.into())],
265			vec![ParamType::Uint(32), ParamType::Bool],
266		);
267
268		assert_type_check(vec![Token::FixedBytes(vec![0, 0, 0, 0])], vec![ParamType::FixedBytes(4)]);
269		assert_type_check(vec![Token::FixedBytes(vec![0, 0, 0])], vec![ParamType::FixedBytes(4)]);
270		assert_not_type_check(vec![Token::FixedBytes(vec![0, 0, 0, 0])], vec![ParamType::FixedBytes(3)]);
271
272		assert_type_check(
273			vec![Token::Array(vec![Token::Bool(false), Token::Bool(true)])],
274			vec![ParamType::Array(Box::new(ParamType::Bool))],
275		);
276		assert_not_type_check(
277			vec![Token::Array(vec![Token::Bool(false), Token::Uint(0.into())])],
278			vec![ParamType::Array(Box::new(ParamType::Bool))],
279		);
280		assert_not_type_check(
281			vec![Token::Array(vec![Token::Bool(false), Token::Bool(true)])],
282			vec![ParamType::Array(Box::new(ParamType::Address))],
283		);
284
285		assert_type_check(
286			vec![Token::FixedArray(vec![Token::Bool(false), Token::Bool(true)])],
287			vec![ParamType::FixedArray(Box::new(ParamType::Bool), 2)],
288		);
289		assert_not_type_check(
290			vec![Token::FixedArray(vec![Token::Bool(false), Token::Bool(true)])],
291			vec![ParamType::FixedArray(Box::new(ParamType::Bool), 3)],
292		);
293		assert_not_type_check(
294			vec![Token::FixedArray(vec![Token::Bool(false), Token::Uint(0.into())])],
295			vec![ParamType::FixedArray(Box::new(ParamType::Bool), 2)],
296		);
297		assert_not_type_check(
298			vec![Token::FixedArray(vec![Token::Bool(false), Token::Bool(true)])],
299			vec![ParamType::FixedArray(Box::new(ParamType::Address), 2)],
300		);
301	}
302
303	#[test]
304	fn test_is_dynamic() {
305		assert!(!Token::Address("0000000000000000000000000000000000000000".parse().unwrap()).is_dynamic());
306		assert!(Token::Bytes(vec![0, 0, 0, 0]).is_dynamic());
307		assert!(!Token::FixedBytes(vec![0, 0, 0, 0]).is_dynamic());
308		assert!(!Token::Uint(0.into()).is_dynamic());
309		assert!(!Token::Int(0.into()).is_dynamic());
310		assert!(!Token::Bool(false).is_dynamic());
311		assert!(Token::String("".into()).is_dynamic());
312		assert!(Token::Array(vec![Token::Bool(false)]).is_dynamic());
313		assert!(!Token::FixedArray(vec![Token::Uint(0.into())]).is_dynamic());
314		assert!(Token::FixedArray(vec![Token::String("".into())]).is_dynamic());
315		assert!(Token::FixedArray(vec![Token::Array(vec![Token::Bool(false)])]).is_dynamic());
316	}
317}