-- file: ethabi/Cargo.toml --
[package]
name = "ethabi"
version = "18.0.0"
authors = [
"Parity Technologies <[email protected]>",
"Artem Vorotnikov <[email protected]>",
"Nicholas Rodrigues Lordello <[email protected]>",
]
homepage = "https://github.com/rust-ethereum/ethabi"
license = "Apache-2.0"
keywords = ["ethereum", "eth", "abi", "solidity"]
description = "Easy to use conversion of ethereum contract calls to bytecode."
edition = "2021"
[dependencies]
hex = { version = "0.4", default-features = false, features = ["alloc"] }
serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] }
serde_json = { version = "1.0", optional = true }
sha3 = { version = "0.10", default-features = false }
ethereum-types = { version = "0.14.0", default-features = false }
thiserror = { version = "1", optional = true }
uint = { version = "0.9.0", default-features = false, optional = true }
regex = { version = "1.5.4", optional = true }
once_cell = { version = "1.9.0", optional = true }
[dev-dependencies]
hex-literal = "0.3"
paste = "1"
serde_json = "1.0"
[features]
default = [
"std",
"full-serde",
"rlp",
]
std = [
"hex/std",
"sha3/std",
"ethereum-types/std",
"thiserror",
"uint?/std",
"serde?/std",
]
serde = [
"dep:serde",
"ethereum-types/serialize",
"uint",
]
# To enable custom `Reader`/`Tokenizer` and `serde` features support
full-serde = [
"std",
"serde",
"serde_json",
"regex",
"once_cell"
]
rlp = [
"ethereum-types/rlp",
]
parity-codec = [
"ethereum-types/codec"
]
-- file: ethabi/src/token/strict.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{errors::Error, token::Tokenizer};
/// Tries to parse string as a token. Require string to clearly represent the value.
pub struct StrictTokenizer;
impl Tokenizer for StrictTokenizer {
fn tokenize_address(value: &str) -> Result<[u8; 20], Error> {
let hex: Vec<u8> = hex::decode(value)?;
match hex.len() == 20 {
false => Err(Error::InvalidData),
true => {
let mut address = [0u8; 20];
address.copy_from_slice(&hex);
Ok(address)
}
}
}
fn tokenize_string(value: &str) -> Result<String, Error> {
Ok(value.to_owned())
}
fn tokenize_bool(value: &str) -> Result<bool, Error> {
match value {
"true" | "1" => Ok(true),
"false" | "0" => Ok(false),
_ => Err(Error::InvalidData),
}
}
fn tokenize_bytes(value: &str) -> Result<Vec<u8>, Error> {
hex::decode(value).map_err(Into::into)
}
fn tokenize_fixed_bytes(value: &str, len: usize) -> Result<Vec<u8>, Error> {
let hex: Vec<u8> = hex::decode(value)?;
match hex.len() == len {
true => Ok(hex),
false => Err(Error::InvalidData),
}
}
fn tokenize_uint(value: &str) -> Result<[u8; 32], Error> {
let hex: Vec<u8> = hex::decode(value)?;
match hex.len() == 32 {
true => {
let mut uint = [0u8; 32];
uint.copy_from_slice(&hex);
Ok(uint)
}
false => Err(Error::InvalidData),
}
}
fn tokenize_int(value: &str) -> Result<[u8; 32], Error> {
Self::tokenize_uint(value)
}
}
#[cfg(test)]
mod tests {
use crate::{
token::{StrictTokenizer, Token, Tokenizer},
ParamType,
};
#[test]
fn tokenize_address() {
assert_eq!(
StrictTokenizer::tokenize(&ParamType::Address, "1111111111111111111111111111111111111111").unwrap(),
Token::Address([0x11u8; 20].into())
);
assert_eq!(
StrictTokenizer::tokenize(&ParamType::Address, "2222222222222222222222222222222222222222").unwrap(),
Token::Address([0x22u8; 20].into())
);
}
#[test]
fn tokenize_string() {
assert_eq!(
StrictTokenizer::tokenize(&ParamType::String, "gavofyork").unwrap(),
Token::String("gavofyork".to_owned())
);
assert_eq!(StrictTokenizer::tokenize(&ParamType::String, "hello").unwrap(), Token::String("hello".to_owned()));
}
#[test]
fn tokenize_bool() {
assert_eq!(StrictTokenizer::tokenize(&ParamType::Bool, "true").unwrap(), Token::Bool(true));
assert_eq!(StrictTokenizer::tokenize(&ParamType::Bool, "1").unwrap(), Token::Bool(true));
assert_eq!(StrictTokenizer::tokenize(&ParamType::Bool, "false").unwrap(), Token::Bool(false));
assert_eq!(StrictTokenizer::tokenize(&ParamType::Bool, "0").unwrap(), Token::Bool(false));
}
#[test]
fn tokenize_bytes() {
assert_eq!(
StrictTokenizer::tokenize(&ParamType::Bytes, "123456").unwrap(),
Token::Bytes(vec![0x12, 0x34, 0x56])
);
assert_eq!(StrictTokenizer::tokenize(&ParamType::Bytes, "0017").unwrap(), Token::Bytes(vec![0x00, 0x17]));
}
#[test]
fn tokenize_fixed_bytes() {
assert_eq!(
StrictTokenizer::tokenize(&ParamType::FixedBytes(3), "123456").unwrap(),
Token::FixedBytes(vec![0x12, 0x34, 0x56])
);
assert_eq!(
StrictTokenizer::tokenize(&ParamType::FixedBytes(2), "0017").unwrap(),
Token::FixedBytes(vec![0x00, 0x17])
);
}
#[test]
fn tokenize_uint() {
assert_eq!(
StrictTokenizer::tokenize(
&ParamType::Uint(256),
"1111111111111111111111111111111111111111111111111111111111111111"
)
.unwrap(),
Token::Uint([0x11u8; 32].into())
);
assert_eq!(
StrictTokenizer::tokenize(
&ParamType::Uint(256),
"2222222222222222222222222222222222222222222222222222222222222222"
)
.unwrap(),
Token::Uint([0x22u8; 32].into())
);
}
#[test]
fn tokenize_int() {
assert_eq!(
StrictTokenizer::tokenize(
&ParamType::Int(256),
"1111111111111111111111111111111111111111111111111111111111111111"
)
.unwrap(),
Token::Int([0x11u8; 32].into())
);
assert_eq!(
StrictTokenizer::tokenize(
&ParamType::Int(256),
"2222222222222222222222222222222222222222222222222222222222222222"
)
.unwrap(),
Token::Int([0x22u8; 32].into())
);
}
#[test]
fn tokenize_empty_array() {
assert_eq!(
StrictTokenizer::tokenize(&ParamType::Array(Box::new(ParamType::Bool)), "[]").unwrap(),
Token::Array(vec![])
);
}
#[test]
fn tokenize_bool_array() {
assert_eq!(
StrictTokenizer::tokenize(&ParamType::Array(Box::new(ParamType::Bool)), "[true,1,0,false]").unwrap(),
Token::Array(vec![Token::Bool(true), Token::Bool(true), Token::Bool(false), Token::Bool(false)])
);
}
#[test]
fn tokenize_bool_array_of_arrays() {
assert_eq!(
StrictTokenizer::tokenize(
&ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Bool)))),
"[[true,1,0],[false]]"
)
.unwrap(),
Token::Array(vec![
Token::Array(vec![Token::Bool(true), Token::Bool(true), Token::Bool(false)]),
Token::Array(vec![Token::Bool(false)])
])
);
}
}
-- file: ethabi/src/token/token.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Ethereum ABI params.
use core::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{Address, Bytes, FixedBytes, Int, ParamType, Uint};
/// Ethereum ABI params.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Clone)]
pub enum Token {
/// Address.
///
/// solidity name: address
/// Encoded to left padded [0u8; 32].
Address(Address),
/// Vector of bytes with known size.
///
/// solidity name eg.: bytes8, bytes32, bytes64, bytes1024
/// Encoded to right padded [0u8; ((N + 31) / 32) * 32].
FixedBytes(FixedBytes),
/// Vector of bytes of unknown size.
///
/// solidity name: bytes
/// Encoded in two parts.
/// Init part: offset of 'closing part`.
/// Closing part: encoded length followed by encoded right padded bytes.
Bytes(Bytes),
/// Signed integer.
///
/// solidity name: int
Int(Int),
/// Unsigned integer.
///
/// solidity name: uint
Uint(Uint),
/// Boolean value.
///
/// solidity name: bool
/// Encoded as left padded [0u8; 32], where last bit represents boolean value.
Bool(bool),
/// String.
///
/// solidity name: string
/// Encoded in the same way as bytes. Must be utf8 compliant.
String(String),
/// Array with known size.
///
/// solidity name eg.: int[3], bool[3], address[][8]
/// Encoding of array is equal to encoding of consecutive elements of array.
FixedArray(Vec<Token>),
/// Array of params with unknown size.
///
/// solidity name eg. int[], bool[], address[5][]
Array(Vec<Token>),
/// Tuple of params of variable types.
///
/// solidity name: tuple
Tuple(Vec<Token>),
}
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Token::Bool(b) => write!(f, "{b}"),
Token::String(ref s) => write!(f, "{s}"),
Token::Address(ref a) => write!(f, "{a:x}"),
Token::Bytes(ref bytes) | Token::FixedBytes(ref bytes) => write!(f, "{}", hex::encode(bytes)),
Token::Uint(ref i) | Token::Int(ref i) => write!(f, "{i:x}"),
Token::Array(ref arr) | Token::FixedArray(ref arr) => {
let s = arr.iter().map(|ref t| format!("{t}")).collect::<Vec<String>>().join(",");
write!(f, "[{s}]")
}
Token::Tuple(ref s) => {
let s = s.iter().map(|ref t| format!("{t}")).collect::<Vec<String>>().join(",");
write!(f, "({s})")
}
}
}
}
impl Token {
/// Check whether the type of the token matches the given parameter type.
///
/// Numeric types (`Int` and `Uint`) type check if the size of the token
/// type is of greater or equal size than the provided parameter type.
pub fn type_check(&self, param_type: &ParamType) -> bool {
match *self {
Token::Address(_) => *param_type == ParamType::Address,
Token::Bytes(_) => *param_type == ParamType::Bytes,
Token::Int(_) => {
matches!(*param_type, ParamType::Int(_))
}
Token::Uint(_) => {
matches!(*param_type, ParamType::Uint(_))
}
Token::Bool(_) => *param_type == ParamType::Bool,
Token::String(_) => *param_type == ParamType::String,
Token::FixedBytes(ref bytes) => {
if let ParamType::FixedBytes(size) = *param_type {
size >= bytes.len()
} else {
false
}
}
Token::Array(ref tokens) => {
if let ParamType::Array(ref param_type) = *param_type {
tokens.iter().all(|t| t.type_check(param_type))
} else {
false
}
}
Token::FixedArray(ref tokens) => {
if let ParamType::FixedArray(ref param_type, size) = *param_type {
size == tokens.len() && tokens.iter().all(|t| t.type_check(param_type))
} else {
false
}
}
Token::Tuple(ref tokens) => {
if let ParamType::Tuple(ref param_type) = *param_type {
tokens.iter().enumerate().all(|(i, t)| t.type_check(¶m_type[i]))
} else {
false
}
}
}
}
/// Converts token to...
pub fn into_address(self) -> Option<Address> {
match self {
Token::Address(address) => Some(address),
_ => None,
}
}
/// Converts token to...
pub fn into_fixed_bytes(self) -> Option<Vec<u8>> {
match self {
Token::FixedBytes(bytes) => Some(bytes),
_ => None,
}
}
/// Converts token to...
pub fn into_bytes(self) -> Option<Vec<u8>> {
match self {
Token::Bytes(bytes) => Some(bytes),
_ => None,
}
}
/// Converts token to...
pub fn into_int(self) -> Option<Int> {
match self {
Token::Int(int) => Some(int),
_ => None,
}
}
/// Converts token to...
pub fn into_uint(self) -> Option<Uint> {
match self {
Token::Uint(uint) => Some(uint),
_ => None,
}
}
/// Converts token to...
pub fn into_bool(self) -> Option<bool> {
match self {
Token::Bool(b) => Some(b),
_ => None,
}
}
/// Converts token to...
pub fn into_string(self) -> Option<String> {
match self {
Token::String(s) => Some(s),
_ => None,
}
}
/// Converts token to...
pub fn into_fixed_array(self) -> Option<Vec<Token>> {
match self {
Token::FixedArray(arr) => Some(arr),
_ => None,
}
}
/// Converts token to...
pub fn into_array(self) -> Option<Vec<Token>> {
match self {
Token::Array(arr) => Some(arr),
_ => None,
}
}
/// Converts token to...
pub fn into_tuple(self) -> Option<Vec<Token>> {
match self {
Token::Tuple(tuple) => Some(tuple),
_ => None,
}
}
/// Check if all the types of the tokens match the given parameter types.
pub fn types_check(tokens: &[Token], param_types: &[ParamType]) -> bool {
param_types.len() == tokens.len() && {
param_types.iter().zip(tokens).all(|(param_type, token)| token.type_check(param_type))
}
}
/// Check if the token is a dynamic type resulting in prefixed encoding
pub fn is_dynamic(&self) -> bool {
match self {
Token::Bytes(_) | Token::String(_) | Token::Array(_) => true,
Token::FixedArray(tokens) => tokens.iter().any(|token| token.is_dynamic()),
Token::Tuple(tokens) => tokens.iter().any(|token| token.is_dynamic()),
_ => false,
}
}
}
#[cfg(test)]
mod tests {
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{ParamType, Token};
#[test]
fn test_type_check() {
fn assert_type_check(tokens: Vec<Token>, param_types: Vec<ParamType>) {
assert!(Token::types_check(&tokens, ¶m_types))
}
fn assert_not_type_check(tokens: Vec<Token>, param_types: Vec<ParamType>) {
assert!(!Token::types_check(&tokens, ¶m_types))
}
assert_type_check(vec![Token::Uint(0.into()), Token::Bool(false)], vec![ParamType::Uint(256), ParamType::Bool]);
assert_type_check(vec![Token::Uint(0.into()), Token::Bool(false)], vec![ParamType::Uint(32), ParamType::Bool]);
assert_not_type_check(vec![Token::Uint(0.into())], vec![ParamType::Uint(32), ParamType::Bool]);
assert_not_type_check(vec![Token::Uint(0.into()), Token::Bool(false)], vec![ParamType::Uint(32)]);
assert_not_type_check(
vec![Token::Bool(false), Token::Uint(0.into())],
vec![ParamType::Uint(32), ParamType::Bool],
);
assert_type_check(vec![Token::FixedBytes(vec![0, 0, 0, 0])], vec![ParamType::FixedBytes(4)]);
assert_type_check(vec![Token::FixedBytes(vec![0, 0, 0])], vec![ParamType::FixedBytes(4)]);
assert_not_type_check(vec![Token::FixedBytes(vec![0, 0, 0, 0])], vec![ParamType::FixedBytes(3)]);
assert_type_check(
vec![Token::Array(vec![Token::Bool(false), Token::Bool(true)])],
vec![ParamType::Array(Box::new(ParamType::Bool))],
);
assert_not_type_check(
vec![Token::Array(vec![Token::Bool(false), Token::Uint(0.into())])],
vec![ParamType::Array(Box::new(ParamType::Bool))],
);
assert_not_type_check(
vec![Token::Array(vec![Token::Bool(false), Token::Bool(true)])],
vec![ParamType::Array(Box::new(ParamType::Address))],
);
assert_type_check(
vec![Token::FixedArray(vec![Token::Bool(false), Token::Bool(true)])],
vec![ParamType::FixedArray(Box::new(ParamType::Bool), 2)],
);
assert_not_type_check(
vec![Token::FixedArray(vec![Token::Bool(false), Token::Bool(true)])],
vec![ParamType::FixedArray(Box::new(ParamType::Bool), 3)],
);
assert_not_type_check(
vec![Token::FixedArray(vec![Token::Bool(false), Token::Uint(0.into())])],
vec![ParamType::FixedArray(Box::new(ParamType::Bool), 2)],
);
assert_not_type_check(
vec![Token::FixedArray(vec![Token::Bool(false), Token::Bool(true)])],
vec![ParamType::FixedArray(Box::new(ParamType::Address), 2)],
);
}
#[test]
fn test_is_dynamic() {
assert!(!Token::Address("0000000000000000000000000000000000000000".parse().unwrap()).is_dynamic());
assert!(Token::Bytes(vec![0, 0, 0, 0]).is_dynamic());
assert!(!Token::FixedBytes(vec![0, 0, 0, 0]).is_dynamic());
assert!(!Token::Uint(0.into()).is_dynamic());
assert!(!Token::Int(0.into()).is_dynamic());
assert!(!Token::Bool(false).is_dynamic());
assert!(Token::String("".into()).is_dynamic());
assert!(Token::Array(vec![Token::Bool(false)]).is_dynamic());
assert!(!Token::FixedArray(vec![Token::Uint(0.into())]).is_dynamic());
assert!(Token::FixedArray(vec![Token::String("".into())]).is_dynamic());
assert!(Token::FixedArray(vec![Token::Array(vec![Token::Bool(false)])]).is_dynamic());
}
}
-- file: ethabi/src/token/lenient.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::{
errors::Error,
token::{StrictTokenizer, Tokenizer},
Uint,
};
use std::borrow::Cow;
use once_cell::sync::Lazy;
static RE: Lazy<regex::Regex> =
Lazy::new(|| regex::Regex::new(r"^([0-9]+)(\.[0-9]+)?\s*(ether|gwei|nanoether|nano|wei)$").expect("invalid regex"));
/// Tries to parse string as a token. Does not require string to clearly represent the value.
pub struct LenientTokenizer;
impl Tokenizer for LenientTokenizer {
fn tokenize_address(value: &str) -> Result<[u8; 20], Error> {
StrictTokenizer::tokenize_address(value)
}
fn tokenize_string(value: &str) -> Result<String, Error> {
StrictTokenizer::tokenize_string(value)
}
fn tokenize_bool(value: &str) -> Result<bool, Error> {
StrictTokenizer::tokenize_bool(value)
}
fn tokenize_bytes(value: &str) -> Result<Vec<u8>, Error> {
StrictTokenizer::tokenize_bytes(value)
}
fn tokenize_fixed_bytes(value: &str, len: usize) -> Result<Vec<u8>, Error> {
StrictTokenizer::tokenize_fixed_bytes(value, len)
}
fn tokenize_uint(value: &str) -> Result<[u8; 32], Error> {
let result = StrictTokenizer::tokenize_uint(value);
if result.is_ok() {
return result;
}
// Tries to parse it as is first. If it fails, tries to check for
// expectable units with the following format: 'Number[Spaces]Unit'.
// If regex fails, then the original FromDecStrErr should take priority
let uint = match Uint::from_dec_str(value) {
Ok(_uint) => _uint,
Err(dec_error) => {
let original_dec_error = dec_error.to_string();
match RE.captures(value) {
Some(captures) => {
let integer = captures.get(1).expect("capture group does not exist").as_str();
let fract = captures.get(2).map(|c| c.as_str().trim_start_matches('.')).unwrap_or_else(|| "");
let units = captures.get(3).expect("capture group does not exist").as_str();
let units = Uint::from(match units.to_lowercase().as_str() {
"ether" => 18,
"gwei" | "nano" | "nanoether" => 9,
"wei" => 0,
_ => return Err(dec_error.into()),
});
let integer = Uint::from_dec_str(integer)?.checked_mul(Uint::from(10u32).pow(units));
if fract.is_empty() {
integer.ok_or(dec_error)?
} else {
// makes sure we don't go beyond 18 decimals
let fract_pow = units.checked_sub(Uint::from(fract.len())).ok_or(dec_error)?;
let fract = Uint::from_dec_str(fract)?
.checked_mul(Uint::from(10u32).pow(fract_pow))
.ok_or_else(|| Error::Other(Cow::Owned(original_dec_error.clone())))?;
integer
.and_then(|integer| integer.checked_add(fract))
.ok_or(Error::Other(Cow::Owned(original_dec_error)))?
}
}
None => return Err(dec_error.into()),
}
}
};
Ok(uint.into())
}
// We don't have a proper signed int 256-bit long type, so here we're cheating. We build a U256
// out of it and check that it's within the lower/upper bound of a hypothetical I256 type: half
// the `U256::max_value().
fn tokenize_int(value: &str) -> Result<[u8; 32], Error> {
let result = StrictTokenizer::tokenize_int(value);
if result.is_ok() {
return result;
}
let abs = Uint::from_dec_str(value.trim_start_matches('-'))?;
let max = Uint::max_value() / 2;
let int = if value.starts_with('-') {
if abs.is_zero() {
return Ok(abs.into());
} else if abs > max + 1 {
return Err(Error::Other(Cow::Borrowed("int256 parse error: Underflow")));
}
!abs + 1 // two's complement
} else {
if abs > max {
return Err(Error::Other(Cow::Borrowed("int256 parse error: Overflow")));
}
abs
};
Ok(int.into())
}
}
#[cfg(test)]
mod tests {
use ethereum_types::FromDecStrErr;
use crate::{
errors::Error,
token::{LenientTokenizer, Token, Tokenizer},
ParamType, Uint,
};
#[test]
fn tokenize_uint() {
assert_eq!(
LenientTokenizer::tokenize(
&ParamType::Uint(256),
"1111111111111111111111111111111111111111111111111111111111111111"
)
.unwrap(),
Token::Uint([0x11u8; 32].into())
);
}
#[test]
fn tokenize_uint_wei() {
assert_eq!(LenientTokenizer::tokenize(&ParamType::Uint(256), "1wei").unwrap(), Token::Uint(Uint::from(1)));
assert_eq!(LenientTokenizer::tokenize(&ParamType::Uint(256), "1 wei").unwrap(), Token::Uint(Uint::from(1)));
}
#[test]
fn tokenize_uint_gwei() {
assert_eq!(
LenientTokenizer::tokenize(&ParamType::Uint(256), "1nano").unwrap(),
Token::Uint(Uint::from_dec_str("1000000000").unwrap())
);
assert_eq!(
LenientTokenizer::tokenize(&ParamType::Uint(256), "1nanoether").unwrap(),
Token::Uint(Uint::from_dec_str("1000000000").unwrap())
);
assert_eq!(
LenientTokenizer::tokenize(&ParamType::Uint(256), "1gwei").unwrap(),
Token::Uint(Uint::from_dec_str("1000000000").unwrap())
);
assert_eq!(
LenientTokenizer::tokenize(&ParamType::Uint(256), "0.1 gwei").unwrap(),
Token::Uint(Uint::from_dec_str("100000000").unwrap())
);
}
#[test]
fn tokenize_uint_ether() {
assert_eq!(
LenientTokenizer::tokenize(&ParamType::Uint(256), "10000000000ether").unwrap(),
Token::Uint(Uint::from_dec_str("10000000000000000000000000000").unwrap())
);
assert_eq!(
LenientTokenizer::tokenize(&ParamType::Uint(256), "1ether").unwrap(),
Token::Uint(Uint::from_dec_str("1000000000000000000").unwrap())
);
assert_eq!(
LenientTokenizer::tokenize(&ParamType::Uint(256), "0.01 ether").unwrap(),
Token::Uint(Uint::from_dec_str("10000000000000000").unwrap())
);
assert_eq!(
LenientTokenizer::tokenize(&ParamType::Uint(256), "0.000000000000000001ether").unwrap(),
Token::Uint(Uint::from_dec_str("1").unwrap())
);
assert_eq!(
LenientTokenizer::tokenize(&ParamType::Uint(256), "0.000000000000000001ether").unwrap(),
LenientTokenizer::tokenize(&ParamType::Uint(256), "1wei").unwrap(),
);
}
#[test]
fn tokenize_uint_array_ether() {
assert_eq!(
LenientTokenizer::tokenize(&ParamType::Array(Box::new(ParamType::Uint(256))), "[1ether,0.1 ether]")
.unwrap(),
Token::Array(vec![
Token::Uint(Uint::from_dec_str("1000000000000000000").unwrap()),
Token::Uint(Uint::from_dec_str("100000000000000000").unwrap())
])
);
}
#[test]
fn tokenize_uint_invalid_units() {
let _error = Error::from(FromDecStrErr::InvalidCharacter);
assert!(matches!(LenientTokenizer::tokenize(&ParamType::Uint(256), "0.1 wei"), Err(_error)));
// 0.1 wei
assert!(matches!(LenientTokenizer::tokenize(&ParamType::Uint(256), "0.0000000000000000001ether"), Err(_error)));
// 1 ether + 0.1 wei
assert!(matches!(LenientTokenizer::tokenize(&ParamType::Uint(256), "1.0000000000000000001ether"), Err(_error)));
// 1_000_000_000 ether + 0.1 wei
assert!(matches!(
LenientTokenizer::tokenize(&ParamType::Uint(256), "1000000000.0000000000000000001ether"),
Err(_error)
));
assert!(matches!(LenientTokenizer::tokenize(&ParamType::Uint(256), "0..1 gwei"), Err(_error)));
assert!(matches!(LenientTokenizer::tokenize(&ParamType::Uint(256), "..1 gwei"), Err(_error)));
assert!(matches!(LenientTokenizer::tokenize(&ParamType::Uint(256), "1. gwei"), Err(_error)));
assert!(matches!(LenientTokenizer::tokenize(&ParamType::Uint(256), ".1 gwei"), Err(_error)));
assert!(matches!(LenientTokenizer::tokenize(&ParamType::Uint(256), "2.1.1 gwei"), Err(_error)));
assert!(matches!(LenientTokenizer::tokenize(&ParamType::Uint(256), ".1.1 gwei"), Err(_error)));
assert!(matches!(LenientTokenizer::tokenize(&ParamType::Uint(256), "1abc"), Err(_error)));
assert!(matches!(LenientTokenizer::tokenize(&ParamType::Uint(256), "1 gwei "), Err(_error)));
assert!(matches!(LenientTokenizer::tokenize(&ParamType::Uint(256), "g 1 gwei"), Err(_error)));
assert!(matches!(LenientTokenizer::tokenize(&ParamType::Uint(256), "1gwei 1 gwei"), Err(_error)));
}
}
-- file: ethabi/src/token/mod.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! ABI param and parsing for it.
#[cfg(feature = "full-serde")]
mod lenient;
#[cfg(feature = "full-serde")]
pub use lenient::LenientTokenizer;
#[cfg(feature = "full-serde")]
mod strict;
#[cfg(feature = "full-serde")]
pub use strict::StrictTokenizer;
mod token;
pub use token::Token;
#[cfg(all(feature = "serde", not(feature = "std")))]
use crate::no_std_prelude::*;
#[cfg(feature = "serde")]
use core::cmp::Ordering::{Equal, Less};
#[cfg(feature = "serde")]
use crate::{Error, ParamType};
/// This trait should be used to parse string values as tokens.
#[cfg(feature = "serde")]
pub trait Tokenizer {
/// Tries to parse a string as a token of given type.
fn tokenize(param: &ParamType, value: &str) -> Result<Token, Error> {
match *param {
ParamType::Address => {
Self::tokenize_address(value.strip_prefix("0x").unwrap_or(value)).map(|a| Token::Address(a.into()))
}
ParamType::String => Self::tokenize_string(value).map(Token::String),
ParamType::Bool => Self::tokenize_bool(value).map(Token::Bool),
ParamType::Bytes => Self::tokenize_bytes(value.strip_prefix("0x").unwrap_or(value)).map(Token::Bytes),
ParamType::FixedBytes(len) => {
Self::tokenize_fixed_bytes(value.strip_prefix("0x").unwrap_or(value), len).map(Token::FixedBytes)
}
ParamType::Uint(_) => Self::tokenize_uint(value).map(Into::into).map(Token::Uint),
ParamType::Int(_) => Self::tokenize_int(value).map(Into::into).map(Token::Int),
ParamType::Array(ref p) => Self::tokenize_array(value, p).map(Token::Array),
ParamType::FixedArray(ref p, len) => Self::tokenize_fixed_array(value, p, len).map(Token::FixedArray),
ParamType::Tuple(ref p) => Self::tokenize_struct(value, p).map(Token::Tuple),
}
}
/// Tries to parse a value as a vector of tokens of fixed size.
fn tokenize_fixed_array(value: &str, param: &ParamType, len: usize) -> Result<Vec<Token>, Error> {
let result = Self::tokenize_array(value, param)?;
match result.len() == len {
true => Ok(result),
false => Err(Error::InvalidData),
}
}
/// Tried to parse a struct as a vector of tokens
fn tokenize_struct(value: &str, param: &[ParamType]) -> Result<Vec<Token>, Error> {
if !value.starts_with('(') || !value.ends_with(')') {
return Err(Error::InvalidData);
}
if value.chars().count() == 2 {
return Ok(vec![]);
}
let mut result = vec![];
let mut nested = 0isize;
let mut ignore = false;
let mut last_item = 1;
let mut array_nested = 0isize;
let mut array_item_start = 1;
let mut last_is_array = false;
let mut params = param.iter();
for (pos, ch) in value.chars().enumerate() {
match ch {
'[' if !ignore => {
if array_nested == 0 {
array_item_start = pos;
}
array_nested += 1;
}
']' if !ignore => {
array_nested -= 1;
if nested > 0 {
// still in nested tuple
continue;
}
match array_nested.cmp(&0) {
Less => {
return Err(Error::InvalidData);
}
Equal => {
let sub = &value[array_item_start..pos + 1];
let token = Self::tokenize(params.next().ok_or(Error::InvalidData)?, sub)?;
result.push(token);
last_is_array = !last_is_array;
}
_ => {}
}
}
_ if array_nested != 0 => continue,
'(' if !ignore => {
nested += 1;
}
')' if !ignore && last_is_array => {
nested -= 1;
last_is_array = !last_is_array;
}
')' if !ignore => {
nested -= 1;
match nested.cmp(&0) {
Less => {
return Err(Error::InvalidData);
}
Equal => {
if last_is_array {
last_is_array = !last_is_array;
} else {
let sub = &value[last_item..pos];
let token = Self::tokenize(params.next().ok_or(Error::InvalidData)?, sub)?;
result.push(token);
last_item = pos + 1;
}
}
_ => {}
}
}
'"' => {
ignore = !ignore;
}
',' if array_nested == 0 && nested == 1 && !ignore && last_is_array => {
last_is_array = !last_is_array;
}
',' if nested == 1 && !ignore => {
let sub = &value[last_item..pos];
let token = Self::tokenize(params.next().ok_or(Error::InvalidData)?, sub)?;
result.push(token);
last_item = pos + 1;
}
_ => (),
}
}
if ignore {
return Err(Error::InvalidData);
}
Ok(result)
}
/// Tries to parse a value as a vector of tokens.
fn tokenize_array(value: &str, param: &ParamType) -> Result<Vec<Token>, Error> {
if !value.starts_with('[') || !value.ends_with(']') {
return Err(Error::InvalidData);
}
if value.chars().count() == 2 {
return Ok(vec![]);
}
let mut result = vec![];
let mut nested = 0isize;
let mut ignore = false;
let mut last_item = 1;
let mut tuple_nested = 0isize;
let mut tuple_item_start = 1;
let mut last_is_tuple = false;
for (i, ch) in value.chars().enumerate() {
match ch {
'(' if !ignore => {
if tuple_nested == 0 {
tuple_item_start = i;
}
tuple_nested += 1;
}
')' if !ignore => {
tuple_nested -= 1;
match tuple_nested.cmp(&0) {
Less => {
return Err(Error::InvalidData);
}
Equal => {
let sub = &value[tuple_item_start..i + 1];
let token = Self::tokenize(param, sub)?;
result.push(token);
last_is_tuple = !last_is_tuple;
}
_ => {}
}
}
_ if tuple_nested != 0 => continue,
'[' if !ignore => {
nested += 1;
}
']' if !ignore && last_is_tuple => {
nested -= 1;
last_is_tuple = !last_is_tuple;
}
']' if !ignore => {
nested -= 1;
match nested.cmp(&0) {
Less => {
return Err(Error::InvalidData);
}
Equal => {
if last_is_tuple {
last_is_tuple = !last_is_tuple;
} else {
let sub = &value[last_item..i];
let token = Self::tokenize(param, sub)?;
result.push(token);
last_item = i + 1;
}
}
_ => {}
}
}
'"' => {
ignore = !ignore;
}
',' if tuple_nested == 0 && nested == 1 && !ignore && last_is_tuple => {
last_is_tuple = !last_is_tuple;
}
',' if tuple_nested == 0 && nested == 1 && !ignore => {
let sub = &value[last_item..i];
let token = Self::tokenize(param, sub)?;
result.push(token);
last_item = i + 1;
}
_ => (),
}
}
if ignore {
return Err(Error::InvalidData);
}
Ok(result)
}
/// Tries to parse a value as an address.
fn tokenize_address(value: &str) -> Result<[u8; 20], Error>;
/// Tries to parse a value as a string.
fn tokenize_string(value: &str) -> Result<String, Error>;
/// Tries to parse a value as a bool.
fn tokenize_bool(value: &str) -> Result<bool, Error>;
/// Tries to parse a value as bytes.
fn tokenize_bytes(value: &str) -> Result<Vec<u8>, Error>;
/// Tries to parse a value as bytes.
fn tokenize_fixed_bytes(value: &str, len: usize) -> Result<Vec<u8>, Error>;
/// Tries to parse a value as unsigned integer.
fn tokenize_uint(value: &str) -> Result<[u8; 32], Error>;
/// Tries to parse a value as signed integer.
fn tokenize_int(value: &str) -> Result<[u8; 32], Error>;
}
#[cfg(all(test, feature = "full-serde"))]
mod test {
use super::{LenientTokenizer, ParamType, Tokenizer};
use crate::Token;
#[test]
fn single_quoted_in_array_must_error() {
assert!(LenientTokenizer::tokenize_array("[1,\"0,false]", &ParamType::Bool).is_err());
assert!(LenientTokenizer::tokenize_array("[false\"]", &ParamType::Bool).is_err());
assert!(LenientTokenizer::tokenize_array("[1,false\"]", &ParamType::Bool).is_err());
assert!(LenientTokenizer::tokenize_array("[1,\"0\",false]", &ParamType::Bool).is_err());
assert!(LenientTokenizer::tokenize_array("[1,0]", &ParamType::Bool).is_ok());
}
#[test]
fn tuples_arrays_mixed() {
assert_eq!(
LenientTokenizer::tokenize_array(
"[([(true)],[(false,true)])]",
&ParamType::Tuple(vec![
ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Bool]))),
ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Bool, ParamType::Bool]))),
]),
)
.unwrap(),
vec![Token::Tuple(vec![
Token::Array(vec![Token::Tuple(vec![Token::Bool(true)])]),
Token::Array(vec![Token::Tuple(vec![Token::Bool(false), Token::Bool(true)])]),
])]
);
assert_eq!(
LenientTokenizer::tokenize_struct(
"([(true)],[(false,true)])",
&[
ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Bool]))),
ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Bool, ParamType::Bool]))),
]
)
.unwrap(),
vec![
Token::Array(vec![Token::Tuple(vec![Token::Bool(true)])]),
Token::Array(vec![Token::Tuple(vec![Token::Bool(false), Token::Bool(true)])]),
]
);
}
#[test]
fn tuple_array_nested() {
assert_eq!(
LenientTokenizer::tokenize_struct(
"([(5c9d55b78febcc2061715ba4f57ecf8ea2711f2c)],2)",
&[ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Address,],)),), ParamType::Uint(256,),]
)
.unwrap(),
vec![
Token::Array(vec![Token::Tuple(vec![Token::Address(
"0x5c9d55b78febcc2061715ba4f57ecf8ea2711f2c".parse().unwrap(),
),])]),
Token::Uint(2u64.into()),
]
);
}
}
-- file: ethabi/src/param.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Function param.
#[cfg(feature = "serde")]
use core::fmt;
#[cfg(feature = "serde")]
use serde::{
de::{Error, MapAccess, Visitor},
ser::{SerializeMap, SerializeSeq},
Deserialize, Deserializer, Serialize, Serializer,
};
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::ParamType;
#[cfg(feature = "serde")]
use crate::{param_type::Writer, TupleParam};
/// Function param.
#[derive(Debug, Clone, PartialEq)]
pub struct Param {
/// Param name.
pub name: String,
/// Param type.
pub kind: ParamType,
/// Additional Internal type.
pub internal_type: Option<String>,
}
#[cfg(feature = "serde")]
impl<'a> Deserialize<'a> for Param {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
deserializer.deserialize_any(ParamVisitor)
}
}
#[cfg(feature = "serde")]
struct ParamVisitor;
#[cfg(feature = "serde")]
impl<'a> Visitor<'a> for ParamVisitor {
type Value = Param;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a valid event parameter spec")
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where
V: MapAccess<'a>,
{
let mut name = None;
let mut kind = None;
let mut components = None;
let mut internal_type = None;
while let Some(ref key) = map.next_key::<String>()? {
match key.as_ref() {
"name" => {
if name.is_some() {
return Err(Error::duplicate_field("name"));
}
name = Some(map.next_value()?);
}
"type" => {
if kind.is_some() {
return Err(Error::duplicate_field("kind"));
}
kind = Some(map.next_value()?);
}
"internalType" => {
if internal_type.is_some() {
return Err(Error::duplicate_field("internalType"));
}
internal_type = Some(map.next_value()?);
}
"components" => {
if components.is_some() {
return Err(Error::duplicate_field("components"));
}
let component: Vec<TupleParam> = map.next_value()?;
components = Some(component)
}
_ => {}
}
}
let name = name.ok_or_else(|| Error::missing_field("name"))?;
let mut kind = kind.ok_or_else(|| Error::missing_field("kind"))?;
set_tuple_components::<V::Error>(&mut kind, components)?;
Ok(Param { name, kind, internal_type })
}
}
#[cfg(feature = "serde")]
impl Serialize for Param {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(None)?;
if let Some(ref internal_type) = self.internal_type {
map.serialize_entry("internalType", internal_type)?;
}
map.serialize_entry("name", &self.name)?;
map.serialize_entry("type", &Writer::write_for_abi(&self.kind, false))?;
if let Some(inner_tuple) = inner_tuple(&self.kind) {
map.serialize_key("components")?;
map.serialize_value(&SerializeableParamVec(inner_tuple))?;
}
map.end()
}
}
#[cfg(feature = "serde")]
pub(crate) fn inner_tuple_mut(mut param: &mut ParamType) -> Option<&mut Vec<ParamType>> {
loop {
match param {
ParamType::Array(inner) => param = inner.as_mut(),
ParamType::FixedArray(inner, _) => param = inner.as_mut(),
ParamType::Tuple(inner) => return Some(inner),
_ => return None,
}
}
}
#[cfg(feature = "serde")]
pub(crate) fn inner_tuple(mut param: &ParamType) -> Option<&Vec<ParamType>> {
loop {
match param {
ParamType::Array(inner) => param = inner.as_ref(),
ParamType::FixedArray(inner, _) => param = inner.as_ref(),
ParamType::Tuple(inner) => return Some(inner),
_ => return None,
}
}
}
#[cfg(feature = "serde")]
pub(crate) fn set_tuple_components<Error: serde::de::Error>(
kind: &mut ParamType,
components: Option<Vec<TupleParam>>,
) -> Result<(), Error> {
if let Some(inner_tuple_mut) = inner_tuple_mut(kind) {
let tuple_params = components.ok_or_else(|| Error::missing_field("components"))?;
inner_tuple_mut.extend(tuple_params.into_iter().map(|param| param.kind))
}
Ok(())
}
#[cfg(feature = "serde")]
pub(crate) struct SerializeableParamVec<'a>(pub(crate) &'a [ParamType]);
#[cfg(feature = "serde")]
impl Serialize for SerializeableParamVec<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(None)?;
for param in self.0 {
seq.serialize_element(&SerializeableParam(param))?;
}
seq.end()
}
}
#[cfg(feature = "serde")]
pub(crate) struct SerializeableParam<'a>(pub(crate) &'a ParamType);
#[cfg(feature = "serde")]
impl Serialize for SerializeableParam<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", &Writer::write_for_abi(self.0, false))?;
if let Some(inner_tuple) = inner_tuple(self.0) {
map.serialize_key("components")?;
map.serialize_value(&SerializeableParamVec(inner_tuple))?;
}
map.end()
}
}
#[cfg(all(test, feature = "serde"))]
mod tests {
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{
tests::{assert_json_eq, assert_ser_de},
Param, ParamType,
};
#[test]
fn param_simple() {
let s = r#"{
"name": "foo",
"type": "address"
}"#;
let deserialized: Param = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, Param { name: "foo".to_owned(), kind: ParamType::Address, internal_type: None });
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn param_simple_internal_type() {
let s = r#"{
"name": "foo",
"type": "address",
"internalType": "struct Verifier.Proof"
}"#;
let deserialized: Param = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
Param {
name: "foo".to_owned(),
kind: ParamType::Address,
internal_type: Some("struct Verifier.Proof".to_string())
}
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn param_tuple() {
let s = r#"{
"name": "foo",
"type": "tuple",
"components": [
{
"type": "uint48"
},
{
"type": "tuple",
"components": [
{
"type": "address"
}
]
}
]
}"#;
let deserialized: Param = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
Param {
name: "foo".to_owned(),
kind: ParamType::Tuple(vec![ParamType::Uint(48), ParamType::Tuple(vec![ParamType::Address])]),
internal_type: None
}
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn param_tuple_internal_type() {
let s = r#"{
"name": "foo",
"type": "tuple",
"internalType": "struct Pairing.G1Point[]",
"components": [
{
"type": "uint48"
},
{
"type": "tuple",
"components": [
{
"type": "address"
}
]
}
]
}"#;
let deserialized: Param = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
Param {
name: "foo".to_owned(),
kind: ParamType::Tuple(vec![ParamType::Uint(48), ParamType::Tuple(vec![ParamType::Address])]),
internal_type: Some("struct Pairing.G1Point[]".to_string())
}
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn param_tuple_named() {
let s = r#"{
"name": "foo",
"type": "tuple",
"components": [
{
"name": "amount",
"type": "uint48"
},
{
"name": "things",
"type": "tuple",
"components": [
{
"name": "baseTupleParam",
"type": "address"
}
]
}
]
}"#;
let deserialized: Param = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
Param {
name: "foo".to_owned(),
kind: ParamType::Tuple(vec![ParamType::Uint(48), ParamType::Tuple(vec![ParamType::Address])]),
internal_type: None
}
);
assert_ser_de(&deserialized);
}
#[test]
fn param_tuple_array() {
let s = r#"{
"name": "foo",
"type": "tuple[]",
"components": [
{
"type": "uint48"
},
{
"type": "address"
},
{
"type": "address"
}
]
}"#;
let deserialized: Param = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
Param {
name: "foo".to_owned(),
kind: ParamType::Array(Box::new(ParamType::Tuple(vec![
ParamType::Uint(48),
ParamType::Address,
ParamType::Address
]))),
internal_type: None
}
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn param_array_of_array_of_tuple() {
let s = r#"{
"name": "foo",
"type": "tuple[][]",
"components": [
{
"type": "uint8"
},
{
"type": "uint16"
}
]
}"#;
let deserialized: Param = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
Param {
name: "foo".to_owned(),
kind: ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Tuple(vec![
ParamType::Uint(8),
ParamType::Uint(16),
]))))),
internal_type: None
}
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn param_tuple_fixed_array() {
let s = r#"{
"name": "foo",
"type": "tuple[2]",
"components": [
{
"type": "uint48"
},
{
"type": "address"
},
{
"type": "address"
}
]
}"#;
let deserialized: Param = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
Param {
name: "foo".to_owned(),
kind: ParamType::FixedArray(
Box::new(ParamType::Tuple(vec![ParamType::Uint(48), ParamType::Address, ParamType::Address])),
2
),
internal_type: None
}
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn param_tuple_with_nested_tuple_arrays() {
let s = r#"{
"name": "foo",
"type": "tuple",
"components": [
{
"type": "tuple[]",
"components": [
{
"type": "address"
}
]
},
{
"type": "tuple[42]",
"components": [
{
"type": "address"
}
]
}
]
}"#;
let deserialized: Param = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
Param {
name: "foo".to_owned(),
kind: ParamType::Tuple(vec![
ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Address]))),
ParamType::FixedArray(Box::new(ParamType::Tuple(vec![ParamType::Address])), 42,)
]),
internal_type: None
}
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
}
-- file: ethabi/src/encoder.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! ABI encoder.
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{util::pad_u32, Bytes, Token, Word};
fn pad_bytes_len(bytes: &[u8]) -> u32 {
// "+ 1" because len is also appended
((bytes.len() + 31) / 32) as u32 + 1
}
fn pad_bytes_append(data: &mut Vec<Word>, bytes: &[u8]) {
data.push(pad_u32(bytes.len() as u32));
fixed_bytes_append(data, bytes);
}
fn fixed_bytes_len(bytes: &[u8]) -> u32 {
((bytes.len() + 31) / 32) as u32
}
fn fixed_bytes_append(result: &mut Vec<Word>, bytes: &[u8]) {
let len = (bytes.len() + 31) / 32;
for i in 0..len {
let mut padded = [0u8; 32];
let to_copy = match i == len - 1 {
false => 32,
true => match bytes.len() % 32 {
0 => 32,
x => x,
},
};
let offset = 32 * i;
padded[..to_copy].copy_from_slice(&bytes[offset..offset + to_copy]);
result.push(padded);
}
}
#[derive(Debug)]
enum Mediate<'a> {
// head
Raw(u32, &'a Token),
RawArray(Vec<Mediate<'a>>),
// head + tail
Prefixed(u32, &'a Token),
PrefixedArray(Vec<Mediate<'a>>),
PrefixedArrayWithLength(Vec<Mediate<'a>>),
}
impl Mediate<'_> {
fn head_len(&self) -> u32 {
match self {
Mediate::Raw(len, _) => 32 * len,
Mediate::RawArray(ref mediates) => mediates.iter().map(|mediate| mediate.head_len()).sum(),
Mediate::Prefixed(_, _) | Mediate::PrefixedArray(_) | Mediate::PrefixedArrayWithLength(_) => 32,
}
}
fn tail_len(&self) -> u32 {
match self {
Mediate::Raw(_, _) | Mediate::RawArray(_) => 0,
Mediate::Prefixed(len, _) => 32 * len,
Mediate::PrefixedArray(ref mediates) => mediates.iter().fold(0, |acc, m| acc + m.head_len() + m.tail_len()),
Mediate::PrefixedArrayWithLength(ref mediates) => {
mediates.iter().fold(32, |acc, m| acc + m.head_len() + m.tail_len())
}
}
}
fn head_append(&self, acc: &mut Vec<Word>, suffix_offset: u32) {
match *self {
Mediate::Raw(_, raw) => encode_token_append(acc, raw),
Mediate::RawArray(ref raw) => raw.iter().for_each(|mediate| mediate.head_append(acc, 0)),
Mediate::Prefixed(_, _) | Mediate::PrefixedArray(_) | Mediate::PrefixedArrayWithLength(_) => {
acc.push(pad_u32(suffix_offset))
}
}
}
fn tail_append(&self, acc: &mut Vec<Word>) {
match *self {
Mediate::Raw(_, _) | Mediate::RawArray(_) => {}
Mediate::Prefixed(_, raw) => encode_token_append(acc, raw),
Mediate::PrefixedArray(ref mediates) => encode_head_tail_append(acc, mediates),
Mediate::PrefixedArrayWithLength(ref mediates) => {
// + 32 added to offset represents len of the array prepended to tail
acc.push(pad_u32(mediates.len() as u32));
encode_head_tail_append(acc, mediates);
}
};
}
}
/// Encodes vector of tokens into ABI compliant vector of bytes.
pub fn encode(tokens: &[Token]) -> Bytes {
let mediates = &tokens.iter().map(mediate_token).collect::<Vec<_>>();
encode_head_tail(mediates).into_iter().flatten().collect()
}
fn encode_head_tail(mediates: &[Mediate]) -> Vec<Word> {
let (heads_len, tails_len) =
mediates.iter().fold((0, 0), |(head_acc, tail_acc), m| (head_acc + m.head_len(), tail_acc + m.tail_len()));
let mut result = Vec::with_capacity((heads_len + tails_len) as usize);
encode_head_tail_append(&mut result, mediates);
result
}
fn encode_head_tail_append(acc: &mut Vec<Word>, mediates: &[Mediate]) {
let heads_len = mediates.iter().fold(0, |head_acc, m| head_acc + m.head_len());
let mut offset = heads_len;
for mediate in mediates {
mediate.head_append(acc, offset);
offset += mediate.tail_len();
}
mediates.iter().for_each(|m| m.tail_append(acc));
}
fn mediate_token(token: &Token) -> Mediate {
match token {
Token::Address(_) => Mediate::Raw(1, token),
Token::Bytes(bytes) => Mediate::Prefixed(pad_bytes_len(bytes), token),
Token::String(s) => Mediate::Prefixed(pad_bytes_len(s.as_bytes()), token),
Token::FixedBytes(bytes) => Mediate::Raw(fixed_bytes_len(bytes), token),
Token::Int(_) | Token::Uint(_) | Token::Bool(_) => Mediate::Raw(1, token),
Token::Array(ref tokens) => {
let mediates = tokens.iter().map(mediate_token).collect();
Mediate::PrefixedArrayWithLength(mediates)
}
Token::FixedArray(ref tokens) | Token::Tuple(ref tokens) => {
let mediates = tokens.iter().map(mediate_token).collect();
if token.is_dynamic() {
Mediate::PrefixedArray(mediates)
} else {
Mediate::RawArray(mediates)
}
}
}
}
fn encode_token_append(data: &mut Vec<Word>, token: &Token) {
match *token {
Token::Address(ref address) => {
let mut padded = [0u8; 32];
padded[12..].copy_from_slice(address.as_ref());
data.push(padded);
}
Token::Bytes(ref bytes) => pad_bytes_append(data, bytes),
Token::String(ref s) => pad_bytes_append(data, s.as_bytes()),
Token::FixedBytes(ref bytes) => fixed_bytes_append(data, bytes),
Token::Int(int) => data.push(int.into()),
Token::Uint(uint) => data.push(uint.into()),
Token::Bool(b) => {
let mut value = [0u8; 32];
if b {
value[31] = 1;
}
data.push(value);
}
_ => panic!("Unhandled nested token: {:?}", token),
};
}
#[cfg(test)]
mod tests {
use hex_literal::hex;
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{encode, util::pad_u32, Token};
#[test]
fn encode_address() {
let address = Token::Address([0x11u8; 20].into());
let encoded = encode(&[address]);
let expected = hex!("0000000000000000000000001111111111111111111111111111111111111111");
assert_eq!(encoded, expected);
}
#[test]
fn encode_dynamic_array_of_addresses() {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let addresses = Token::Array(vec![address1, address2]);
let encoded = encode(&[addresses]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_fixed_array_of_addresses() {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let addresses = Token::FixedArray(vec![address1, address2]);
let encoded = encode(&[addresses]);
let expected = hex!(
"
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_two_addresses() {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let encoded = encode(&[address1, address2]);
let expected = hex!(
"
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_fixed_array_of_dynamic_array_of_addresses() {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let array0 = Token::Array(vec![address1, address2]);
let array1 = Token::Array(vec![address3, address4]);
let fixed = Token::FixedArray(vec![array0, array1]);
let encoded = encode(&[fixed]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_dynamic_array_of_fixed_array_of_addresses() {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let array0 = Token::FixedArray(vec![address1, address2]);
let array1 = Token::FixedArray(vec![address3, address4]);
let dynamic = Token::Array(vec![array0, array1]);
let encoded = encode(&[dynamic]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_dynamic_array_of_dynamic_arrays() {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let array0 = Token::Array(vec![address1]);
let array1 = Token::Array(vec![address2]);
let dynamic = Token::Array(vec![array0, array1]);
let encoded = encode(&[dynamic]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000002222222222222222222222222222222222222222
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_dynamic_array_of_dynamic_arrays2() {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let array0 = Token::Array(vec![address1, address2]);
let array1 = Token::Array(vec![address3, address4]);
let dynamic = Token::Array(vec![array0, array1]);
let encoded = encode(&[dynamic]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_fixed_array_of_fixed_arrays() {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let array0 = Token::FixedArray(vec![address1, address2]);
let array1 = Token::FixedArray(vec![address3, address4]);
let fixed = Token::FixedArray(vec![array0, array1]);
let encoded = encode(&[fixed]);
let expected = hex!(
"
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_fixed_array_of_static_tuples_followed_by_dynamic_type() {
let tuple1 = Token::Tuple(vec![
Token::Uint(93523141.into()),
Token::Uint(352332135.into()),
Token::Address([0x44u8; 20].into()),
]);
let tuple2 =
Token::Tuple(vec![Token::Uint(12411.into()), Token::Uint(451.into()), Token::Address([0x22u8; 20].into())]);
let fixed = Token::FixedArray(vec![tuple1, tuple2]);
let s = Token::String("gavofyork".to_owned());
let encoded = encode(&[fixed, s]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000005930cc5
0000000000000000000000000000000000000000000000000000000015002967
0000000000000000000000004444444444444444444444444444444444444444
000000000000000000000000000000000000000000000000000000000000307b
00000000000000000000000000000000000000000000000000000000000001c3
0000000000000000000000002222222222222222222222222222222222222222
00000000000000000000000000000000000000000000000000000000000000e0
0000000000000000000000000000000000000000000000000000000000000009
6761766f66796f726b0000000000000000000000000000000000000000000000
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_empty_array() {
// Empty arrays
let encoded = encode(&[Token::Array(vec![]), Token::Array(vec![])]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
"
)
.to_vec();
assert_eq!(encoded, expected);
// Nested empty arrays
let encoded = encode(&[Token::Array(vec![Token::Array(vec![])]), Token::Array(vec![Token::Array(vec![])])]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000000
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_bytes() {
let bytes = Token::Bytes(vec![0x12, 0x34]);
let encoded = encode(&[bytes]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
1234000000000000000000000000000000000000000000000000000000000000
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_fixed_bytes() {
let bytes = Token::FixedBytes(vec![0x12, 0x34]);
let encoded = encode(&[bytes]);
let expected = hex!("1234000000000000000000000000000000000000000000000000000000000000");
assert_eq!(encoded, expected);
}
#[test]
fn encode_string() {
let s = Token::String("gavofyork".to_owned());
let encoded = encode(&[s]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000009
6761766f66796f726b0000000000000000000000000000000000000000000000
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_bytes2() {
let bytes = Token::Bytes(hex!("10000000000000000000000000000000000000000000000000000000000002").to_vec());
let encoded = encode(&[bytes]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
000000000000000000000000000000000000000000000000000000000000001f
1000000000000000000000000000000000000000000000000000000000000200
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_bytes3() {
let bytes = Token::Bytes(
hex!(
"
1000000000000000000000000000000000000000000000000000000000000000
1000000000000000000000000000000000000000000000000000000000000000
"
)
.to_vec(),
);
let encoded = encode(&[bytes]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000040
1000000000000000000000000000000000000000000000000000000000000000
1000000000000000000000000000000000000000000000000000000000000000
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_two_bytes() {
let bytes1 = Token::Bytes(hex!("10000000000000000000000000000000000000000000000000000000000002").to_vec());
let bytes2 = Token::Bytes(hex!("0010000000000000000000000000000000000000000000000000000000000002").to_vec());
let encoded = encode(&[bytes1, bytes2]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
000000000000000000000000000000000000000000000000000000000000001f
1000000000000000000000000000000000000000000000000000000000000200
0000000000000000000000000000000000000000000000000000000000000020
0010000000000000000000000000000000000000000000000000000000000002
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_uint() {
let mut uint = [0u8; 32];
uint[31] = 4;
let encoded = encode(&[Token::Uint(uint.into())]);
let expected = hex!("0000000000000000000000000000000000000000000000000000000000000004");
assert_eq!(encoded, expected);
}
#[test]
fn encode_int() {
let mut int = [0u8; 32];
int[31] = 4;
let encoded = encode(&[Token::Int(int.into())]);
let expected = hex!("0000000000000000000000000000000000000000000000000000000000000004");
assert_eq!(encoded, expected);
}
#[test]
fn encode_bool() {
let encoded = encode(&[Token::Bool(true)]);
let expected = hex!("0000000000000000000000000000000000000000000000000000000000000001");
assert_eq!(encoded, expected);
}
#[test]
fn encode_bool2() {
let encoded = encode(&[Token::Bool(false)]);
let expected = hex!("0000000000000000000000000000000000000000000000000000000000000000");
assert_eq!(encoded, expected);
}
#[test]
fn comprehensive_test() {
let bytes = hex!(
"
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
"
)
.to_vec();
let encoded =
encode(&[Token::Int(5.into()), Token::Bytes(bytes.clone()), Token::Int(3.into()), Token::Bytes(bytes)]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000005
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000003
00000000000000000000000000000000000000000000000000000000000000e0
0000000000000000000000000000000000000000000000000000000000000040
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
0000000000000000000000000000000000000000000000000000000000000040
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn test_pad_u32() {
// this will fail if endianess is not supported
assert_eq!(pad_u32(0x1)[31], 1);
assert_eq!(pad_u32(0x100)[30], 1);
}
#[test]
fn comprehensive_test2() {
let encoded = encode(&vec![
Token::Int(1.into()),
Token::String("gavofyork".to_owned()),
Token::Int(2.into()),
Token::Int(3.into()),
Token::Int(4.into()),
Token::Array(vec![Token::Int(5.into()), Token::Int(6.into()), Token::Int(7.into())]),
]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000001
00000000000000000000000000000000000000000000000000000000000000c0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000000000000000000000000000000000000000000004
0000000000000000000000000000000000000000000000000000000000000100
0000000000000000000000000000000000000000000000000000000000000009
6761766f66796f726b0000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000000000000000000000000000000000000000000005
0000000000000000000000000000000000000000000000000000000000000006
0000000000000000000000000000000000000000000000000000000000000007
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_dynamic_array_of_bytes() {
let bytes = hex!("019c80031b20d5e69c8093a571162299032018d913930d93ab320ae5ea44a4218a274f00d607");
let encoded = encode(&[Token::Array(vec![Token::Bytes(bytes.to_vec())])]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000026
019c80031b20d5e69c8093a571162299032018d913930d93ab320ae5ea44a421
8a274f00d6070000000000000000000000000000000000000000000000000000
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_dynamic_array_of_bytes2() {
let bytes = hex!("4444444444444444444444444444444444444444444444444444444444444444444444444444");
let bytes2 = hex!("6666666666666666666666666666666666666666666666666666666666666666666666666666");
let encoded = encode(&[Token::Array(vec![Token::Bytes(bytes.to_vec()), Token::Bytes(bytes2.to_vec())])]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000026
4444444444444444444444444444444444444444444444444444444444444444
4444444444440000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000026
6666666666666666666666666666666666666666666666666666666666666666
6666666666660000000000000000000000000000000000000000000000000000
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_static_tuple_of_addresses() {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let encoded = encode(&[Token::Tuple(vec![address1, address2])]);
let expected = hex!(
"
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_dynamic_tuple() {
let string1 = Token::String("gavofyork".to_owned());
let string2 = Token::String("gavofyork".to_owned());
let tuple = Token::Tuple(vec![string1, string2]);
let encoded = encode(&[tuple]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000009
6761766f66796f726b0000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000009
6761766f66796f726b0000000000000000000000000000000000000000000000
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_dynamic_tuple_of_bytes2() {
let bytes = hex!("4444444444444444444444444444444444444444444444444444444444444444444444444444");
let bytes2 = hex!("6666666666666666666666666666666666666666666666666666666666666666666666666666");
let encoded = encode(&[Token::Tuple(vec![Token::Bytes(bytes.to_vec()), Token::Bytes(bytes2.to_vec())])]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000026
4444444444444444444444444444444444444444444444444444444444444444
4444444444440000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000026
6666666666666666666666666666666666666666666666666666666666666666
6666666666660000000000000000000000000000000000000000000000000000
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_complex_tuple() {
let uint = Token::Uint([0x11u8; 32].into());
let string = Token::String("gavofyork".to_owned());
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let tuple = Token::Tuple(vec![uint, string, address1, address2]);
let encoded = encode(&[tuple]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
1111111111111111111111111111111111111111111111111111111111111111
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000000000000000000000000000000000000000000009
6761766f66796f726b0000000000000000000000000000000000000000000000
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_nested_tuple() {
let string1 = Token::String("test".to_owned());
let string2 = Token::String("cyborg".to_owned());
let string3 = Token::String("night".to_owned());
let string4 = Token::String("day".to_owned());
let string5 = Token::String("weee".to_owned());
let string6 = Token::String("funtests".to_owned());
let bool = Token::Bool(true);
let deep_tuple = Token::Tuple(vec![string5, string6]);
let inner_tuple = Token::Tuple(vec![string3, string4, deep_tuple]);
let outer_tuple = Token::Tuple(vec![string1, bool, string2, inner_tuple]);
let encoded = encode(&[outer_tuple]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000001
00000000000000000000000000000000000000000000000000000000000000c0
0000000000000000000000000000000000000000000000000000000000000100
0000000000000000000000000000000000000000000000000000000000000004
7465737400000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000006
6379626f72670000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000060
00000000000000000000000000000000000000000000000000000000000000a0
00000000000000000000000000000000000000000000000000000000000000e0
0000000000000000000000000000000000000000000000000000000000000005
6e69676874000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000003
6461790000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000004
7765656500000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000008
66756e7465737473000000000000000000000000000000000000000000000000
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_params_containing_dynamic_tuple() {
let address1 = Token::Address([0x22u8; 20].into());
let bool1 = Token::Bool(true);
let string1 = Token::String("spaceship".to_owned());
let string2 = Token::String("cyborg".to_owned());
let tuple = Token::Tuple(vec![bool1, string1, string2]);
let address2 = Token::Address([0x33u8; 20].into());
let address3 = Token::Address([0x44u8; 20].into());
let bool2 = Token::Bool(false);
let encoded = encode(&[address1, tuple, address2, address3, bool2]);
let expected = hex!(
"
0000000000000000000000002222222222222222222222222222222222222222
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000060
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000009
7370616365736869700000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000006
6379626f72670000000000000000000000000000000000000000000000000000
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_params_containing_static_tuple() {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let bool1 = Token::Bool(true);
let bool2 = Token::Bool(false);
let tuple = Token::Tuple(vec![address2, bool1, bool2]);
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let encoded = encode(&[address1, tuple, address3, address4]);
let expected = hex!(
"
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444
"
)
.to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn encode_dynamic_tuple_with_nested_static_tuples() {
let token = {
use crate::Token::*;
Tuple(vec![
Tuple(vec![Tuple(vec![Bool(false), Uint(0x777.into())])]),
Array(vec![Uint(0x42.into()), Uint(0x1337.into())]),
])
};
let encoded = encode(&[token]);
let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000777
0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000042
0000000000000000000000000000000000000000000000000000000000001337
"
)
.to_vec();
assert_eq!(encoded, expected);
}
}
-- file: ethabi/src/operation.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Operation type.
use crate::{error::Error, Constructor, Event, Function};
use serde::{Deserialize, Serialize};
/// Operation type.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Operation {
/// Contract constructor.
#[serde(rename = "constructor")]
Constructor(Constructor),
/// Contract function.
#[serde(rename = "function")]
Function(Function),
/// Contract event.
#[serde(rename = "event")]
Event(Event),
/// Contract event.
#[serde(rename = "error")]
Error(Error),
/// Fallback function.
#[serde(rename = "fallback")]
Fallback,
/// Receive function.
#[serde(rename = "receive")]
Receive,
}
#[cfg(test)]
mod tests {
use super::Operation;
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{tests::assert_ser_de, Event, EventParam, Function, Param, ParamType, StateMutability};
#[test]
fn operation() {
let s = r#"{
"type":"function",
"inputs": [{
"name":"a",
"type":"address"
}],
"name":"foo",
"outputs": []
}"#;
let deserialized: Operation = serde_json::from_str(s).unwrap();
#[allow(deprecated)]
let function = Function {
name: "foo".to_owned(),
inputs: vec![Param { name: "a".to_owned(), kind: ParamType::Address, internal_type: None }],
outputs: vec![],
constant: None,
state_mutability: StateMutability::NonPayable,
};
assert_eq!(deserialized, Operation::Function(function));
assert_ser_de(&deserialized);
}
#[test]
fn event_operation_with_tuple_array_input() {
let s = r#"{
"type":"event",
"inputs": [
{
"name":"a",
"type":"address",
"indexed":true
},
{
"components": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"indexed": false,
"internalType": "struct Action[]",
"name": "b",
"type": "tuple[]"
}
],
"name":"E",
"outputs": [],
"anonymous": false
}"#;
let deserialized: Operation = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
Operation::Event(Event {
name: "E".to_owned(),
inputs: vec![
EventParam { name: "a".to_owned(), kind: ParamType::Address, indexed: true },
EventParam {
name: "b".to_owned(),
kind: ParamType::Array(Box::new(ParamType::Tuple(vec![
ParamType::Address,
ParamType::Uint(256),
ParamType::Bytes
]))),
indexed: false
},
],
anonymous: false,
})
);
assert_ser_de(&deserialized);
}
#[test]
fn sanitize_function_name() {
fn test_sanitize_function_name(name: &str, expected: &str) {
let s = format!(
r#"{{
"type":"function",
"inputs": [{{
"name":"a",
"type":"address"
}}],
"name":"{}",
"outputs": []
}}"#,
name
);
let deserialized: Operation = serde_json::from_str(&s).unwrap();
let function = match &deserialized {
Operation::Function(f) => f,
_ => panic!("expected function"),
};
assert_eq!(function.name, expected);
assert_ser_de(&deserialized);
}
test_sanitize_function_name("foo", "foo");
test_sanitize_function_name("foo()", "foo");
test_sanitize_function_name("()", "");
test_sanitize_function_name("", "");
}
#[test]
fn sanitize_event_name() {
fn test_sanitize_event_name(name: &str, expected: &str) {
let s = format!(
r#"{{
"type":"event",
"inputs": [{{
"name":"a",
"type":"address",
"indexed":true
}}],
"name":"{}",
"outputs": [],
"anonymous": false
}}"#,
name
);
let deserialized: Operation = serde_json::from_str(&s).unwrap();
let event = match deserialized {
Operation::Event(e) => e,
_ => panic!("expected event!"),
};
assert_eq!(event.name, expected);
assert_ser_de(&Operation::Event(event));
}
test_sanitize_event_name("foo", "foo");
test_sanitize_event_name("foo()", "foo");
test_sanitize_event_name("()", "");
test_sanitize_event_name("", "");
}
}
-- file: ethabi/src/util.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Utils used by different modules.
use crate::Word;
/// Converts a u32 to a right aligned array of 32 bytes.
pub fn pad_u32(value: u32) -> Word {
let mut padded = [0u8; 32];
padded[28..32].copy_from_slice(&value.to_be_bytes());
padded
}
// This is a workaround to support non-spec compliant function and event names,
// see: https://github.com/paritytech/parity/issues/4122
#[cfg(feature = "serde")]
pub(crate) mod sanitize_name {
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use serde::{Deserialize, Deserializer};
pub fn deserialize<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let mut name = String::deserialize(deserializer)?;
sanitize_name(&mut name);
Ok(name)
}
fn sanitize_name(name: &mut String) {
if let Some(i) = name.find('(') {
name.truncate(i);
}
}
}
#[cfg(test)]
mod tests {
use super::pad_u32;
use hex_literal::hex;
#[test]
fn test_pad_u32() {
// this will fail if endianness is not supported
assert_eq!(
pad_u32(0).to_vec(),
hex!("0000000000000000000000000000000000000000000000000000000000000000").to_vec()
);
assert_eq!(
pad_u32(1).to_vec(),
hex!("0000000000000000000000000000000000000000000000000000000000000001").to_vec()
);
assert_eq!(
pad_u32(0x100).to_vec(),
hex!("0000000000000000000000000000000000000000000000000000000000000100").to_vec()
);
assert_eq!(
pad_u32(0xffffffff).to_vec(),
hex!("00000000000000000000000000000000000000000000000000000000ffffffff").to_vec()
);
}
}
-- file: ethabi/src/error.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Contract error
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{
decode, encode, errors,
signature::{long_signature, short_signature},
Bytes, Hash, Param, ParamType, Result, Token,
};
/// Contract error specification.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct Error {
/// Error name.
#[cfg_attr(feature = "serde", serde(deserialize_with = "crate::util::sanitize_name::deserialize"))]
pub name: String,
/// Error input.
pub inputs: Vec<Param>,
}
impl Error {
/// Returns types of all params.
fn param_types(&self) -> Vec<ParamType> {
self.inputs.iter().map(|p| p.kind.clone()).collect()
}
/// Error signature
pub fn signature(&self) -> Hash {
long_signature(&self.name, &self.param_types())
}
/// Prepares ABI error with given input params.
pub fn encode(&self, tokens: &[Token]) -> Result<Bytes> {
let params = self.param_types();
if !Token::types_check(tokens, ¶ms) {
return Err(errors::Error::InvalidData);
}
let signed = short_signature(&self.name, ¶ms).to_vec();
let encoded = encode(tokens);
Ok(signed.into_iter().chain(encoded.into_iter()).collect())
}
/// Parses the ABI function input to a list of tokens.
pub fn decode(&self, data: &[u8]) -> Result<Vec<Token>> {
decode(&self.param_types(), data)
}
}
-- file: ethabi/src/param_type/param_type.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Function and event param types.
use core::fmt;
use super::Writer;
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
/// Function and event param types.
#[derive(Debug, Clone, PartialEq)]
pub enum ParamType {
/// Address.
Address,
/// Bytes.
Bytes,
/// Signed integer.
Int(usize),
/// Unsigned integer.
Uint(usize),
/// Boolean.
Bool,
/// String.
String,
/// Array of unknown size.
Array(Box<ParamType>),
/// Vector of bytes with fixed size.
FixedBytes(usize),
/// Array with fixed size.
FixedArray(Box<ParamType>, usize),
/// Tuple containing different types
Tuple(Vec<ParamType>),
}
impl fmt::Display for ParamType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", Writer::write(self))
}
}
impl ParamType {
/// returns whether a zero length byte slice (`0x`) is
/// a valid encoded form of this param type
pub fn is_empty_bytes_valid_encoding(&self) -> bool {
match self {
ParamType::FixedBytes(len) => *len == 0,
ParamType::FixedArray(_, len) => *len == 0,
_ => false,
}
}
/// returns whether a ParamType is dynamic
/// used to decide how the ParamType should be encoded
pub fn is_dynamic(&self) -> bool {
match self {
ParamType::Bytes | ParamType::String | ParamType::Array(_) => true,
ParamType::FixedArray(elem_type, _) => elem_type.is_dynamic(),
ParamType::Tuple(params) => params.iter().any(|param| param.is_dynamic()),
_ => false,
}
}
}
#[cfg(test)]
mod tests {
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::ParamType;
#[test]
fn test_param_type_display() {
assert_eq!(format!("{}", ParamType::Address), "address".to_owned());
assert_eq!(format!("{}", ParamType::Bytes), "bytes".to_owned());
assert_eq!(format!("{}", ParamType::FixedBytes(32)), "bytes32".to_owned());
assert_eq!(format!("{}", ParamType::Uint(256)), "uint256".to_owned());
assert_eq!(format!("{}", ParamType::Int(64)), "int64".to_owned());
assert_eq!(format!("{}", ParamType::Bool), "bool".to_owned());
assert_eq!(format!("{}", ParamType::String), "string".to_owned());
assert_eq!(format!("{}", ParamType::Array(Box::new(ParamType::Bool))), "bool[]".to_owned());
assert_eq!(format!("{}", ParamType::FixedArray(Box::new(ParamType::Uint(256)), 2)), "uint256[2]".to_owned());
assert_eq!(format!("{}", ParamType::FixedArray(Box::new(ParamType::String), 2)), "string[2]".to_owned());
assert_eq!(
format!("{}", ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Bool))), 2)),
"bool[][2]".to_owned()
);
}
#[test]
fn test_is_dynamic() {
assert!(!ParamType::Address.is_dynamic());
assert!(ParamType::Bytes.is_dynamic());
assert!(!ParamType::FixedBytes(32).is_dynamic());
assert!(!ParamType::Uint(256).is_dynamic());
assert!(!ParamType::Int(64).is_dynamic());
assert!(!ParamType::Bool.is_dynamic());
assert!(ParamType::String.is_dynamic());
assert!(ParamType::Array(Box::new(ParamType::Bool)).is_dynamic());
assert!(!ParamType::FixedArray(Box::new(ParamType::Uint(256)), 2).is_dynamic());
assert!(ParamType::FixedArray(Box::new(ParamType::String), 2).is_dynamic());
assert!(ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Bool))), 2).is_dynamic());
}
}
-- file: ethabi/src/param_type/mod.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Function and event param types.
#[cfg(feature = "serde")]
mod deserialize;
mod param_type;
pub use param_type::ParamType;
#[cfg(feature = "serde")]
mod reader;
#[cfg(feature = "serde")]
pub use reader::Reader;
mod writer;
pub use writer::Writer;
-- file: ethabi/src/param_type/deserialize.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use super::{ParamType, Reader};
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use core::fmt;
use serde::{
de::{Error as SerdeError, Visitor},
Deserialize, Deserializer,
};
impl<'a> Deserialize<'a> for ParamType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
deserializer.deserialize_identifier(ParamTypeVisitor)
}
}
struct ParamTypeVisitor;
impl<'a> Visitor<'a> for ParamTypeVisitor {
type Value = ParamType;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a correct name of abi-encodable parameter type")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: SerdeError,
{
Reader::read(value).map_err(|e| SerdeError::custom(format!("{e:?}").as_str()))
}
fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
where
E: SerdeError,
{
self.visit_str(value.as_str())
}
}
#[cfg(test)]
mod tests {
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::ParamType;
#[test]
fn param_type_deserialization() {
let s = r#"["address", "bytes", "bytes32", "bool", "string", "int", "uint", "address[]", "uint[3]", "bool[][5]", "tuple[]"]"#;
let deserialized: Vec<ParamType> = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
vec![
ParamType::Address,
ParamType::Bytes,
ParamType::FixedBytes(32),
ParamType::Bool,
ParamType::String,
ParamType::Int(256),
ParamType::Uint(256),
ParamType::Array(Box::new(ParamType::Address)),
ParamType::FixedArray(Box::new(ParamType::Uint(256)), 3),
ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Bool))), 5),
ParamType::Array(Box::new(ParamType::Tuple(vec![]))),
]
);
}
}
-- file: ethabi/src/param_type/writer.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::ParamType;
/// Output formatter for param type.
pub struct Writer;
impl Writer {
/// Returns string which is a formatted represenation of param.
pub fn write(param: &ParamType) -> String {
Writer::write_for_abi(param, true)
}
/// If `serialize_tuple_contents` is `true`, tuples will be represented
/// as list of inner types in parens, for example `(int256,bool)`.
/// If it is `false`, tuples will be represented as keyword `tuple`.
pub fn write_for_abi(param: &ParamType, serialize_tuple_contents: bool) -> String {
match *param {
ParamType::Address => "address".to_owned(),
ParamType::Bytes => "bytes".to_owned(),
ParamType::FixedBytes(len) => format!("bytes{len}"),
ParamType::Int(len) => format!("int{len}"),
ParamType::Uint(len) => format!("uint{len}"),
ParamType::Bool => "bool".to_owned(),
ParamType::String => "string".to_owned(),
ParamType::FixedArray(ref param, len) => {
format!("{}[{len}]", Writer::write_for_abi(param, serialize_tuple_contents))
}
ParamType::Array(ref param) => {
format!("{}[]", Writer::write_for_abi(param, serialize_tuple_contents))
}
ParamType::Tuple(ref params) => {
if serialize_tuple_contents {
let formatted = params
.iter()
.map(|t| Writer::write_for_abi(t, serialize_tuple_contents))
.collect::<Vec<String>>()
.join(",");
format!("({formatted})")
} else {
"tuple".to_owned()
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::Writer;
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::ParamType;
#[test]
fn test_write_param() {
assert_eq!(Writer::write(&ParamType::Address), "address".to_owned());
assert_eq!(Writer::write(&ParamType::Bytes), "bytes".to_owned());
assert_eq!(Writer::write(&ParamType::FixedBytes(32)), "bytes32".to_owned());
assert_eq!(Writer::write(&ParamType::Uint(256)), "uint256".to_owned());
assert_eq!(Writer::write(&ParamType::Int(64)), "int64".to_owned());
assert_eq!(Writer::write(&ParamType::Bool), "bool".to_owned());
assert_eq!(Writer::write(&ParamType::String), "string".to_owned());
assert_eq!(Writer::write(&ParamType::Array(Box::new(ParamType::Bool))), "bool[]".to_owned());
assert_eq!(Writer::write(&ParamType::FixedArray(Box::new(ParamType::String), 2)), "string[2]".to_owned());
assert_eq!(
Writer::write(&ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Bool))), 2)),
"bool[][2]".to_owned()
);
assert_eq!(
Writer::write(&ParamType::Array(Box::new(ParamType::Tuple(vec![
ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Int(256), ParamType::Uint(256)]))),
ParamType::FixedBytes(32),
])))),
"((int256,uint256)[],bytes32)[]".to_owned()
);
assert_eq!(
Writer::write_for_abi(
&ParamType::Array(Box::new(ParamType::Tuple(vec![
ParamType::Array(Box::new(ParamType::Int(256))),
ParamType::FixedBytes(32),
]))),
false
),
"tuple[]".to_owned()
);
}
}
-- file: ethabi/src/param_type/reader.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{Error, ParamType};
/// Used to convert param type represented as a string to rust structure.
pub struct Reader;
impl Reader {
/// Converts string to param type.
pub fn read(name: &str) -> Result<ParamType, Error> {
match name.chars().last() {
// check if it is a struct
Some(')') => {
if !name.starts_with('(') {
return Err(Error::InvalidName(name.to_owned()));
};
let mut subtypes = Vec::new();
let mut subtuples = Vec::new();
let mut nested = 0isize;
let mut top_level_paren_open = 0usize;
let mut last_item = 1;
let mut chars = name.chars().enumerate();
// Iterate over name and build the nested tuple structure
while let Some((mut pos, c)) = chars.next() {
match c {
'(' => {
top_level_paren_open = pos;
nested += 1;
// If an '(' is encountered within the tuple
// insert an empty subtuples vector to be filled
if nested > 1 {
subtuples.push(vec![]);
last_item = pos + 1;
}
}
')' => {
nested -= 1;
// End parsing and return an error if parentheses aren't symmetrical
if nested < 0 {
return Err(Error::InvalidName(name.to_owned()));
}
// If there have not been any characters since the last item
// increment position without inserting any subtypes
else if name[last_item..pos].is_empty() {
last_item = pos + 1;
}
// If the item is in the top level of the tuple insert it into subtypes
else if nested == 0 {
// check for trailing brackets that indicate array of tuples
let sub = &name[last_item..pos];
let subtype = Reader::read(sub)?;
subtypes.push(subtype);
last_item = pos + 1;
}
// If the item is in a sublevel of the tuple
else if nested > 0 {
// this makes sure trailing brackets are included for the next step
loop {
match chars.clone().next() {
Some((_, ',')) | Some((_, ')')) | None => break,
_ => {
// consume the char and shift position
chars.next();
pos += 1;
}
}
}
// parse the nested tuple
let inner_tuple = &name[top_level_paren_open..=pos];
let subtype = Reader::read(inner_tuple)?;
if nested > 1 {
let mut subtuple = core::mem::take(&mut subtuples[(nested - 2) as usize]);
subtuple.push(subtype);
subtypes.push(ParamType::Tuple(subtuple));
} else {
subtypes.push(subtype);
}
last_item = pos + 1;
}
}
',' => {
// If there have not been any characters since the last item
// increment position without inserting any subtypes
if name[last_item..pos].is_empty() {
last_item = pos + 1
}
// If the item is in the top level of the tuple insert it into subtypes
else if nested == 1 {
let sub = &name[last_item..pos];
let subtype = Reader::read(sub)?;
subtypes.push(subtype);
last_item = pos + 1;
}
// If the item is in a sublevel of the tuple
// insert it into the subtuple vector for the current depth level
else if nested > 1 {
let sub = &name[last_item..pos];
let subtype = Reader::read(sub)?;
subtuples[(nested - 2) as usize].push(subtype);
last_item = pos + 1;
}
}
_ => (),
}
}
return Ok(ParamType::Tuple(subtypes));
}
// check if it is a fixed or dynamic array.
Some(']') => {
// take number part
let num: String =
name.chars().rev().skip(1).take_while(|c| *c != '[').collect::<String>().chars().rev().collect();
let count = name.chars().count();
return if num.is_empty() {
// we already know it's a dynamic array!
let subtype = Reader::read(&name[..count - 2])?;
Ok(ParamType::Array(Box::new(subtype)))
} else {
// it's a fixed array.
let len = num.parse().map_err(Error::ParseInt)?;
let subtype = Reader::read(&name[..count - num.len() - 2])?;
Ok(ParamType::FixedArray(Box::new(subtype), len))
};
}
_ => (),
}
let result = match name {
"address" => ParamType::Address,
"bytes" => ParamType::Bytes,
"bool" => ParamType::Bool,
"string" => ParamType::String,
"int" => ParamType::Int(256),
"tuple" => ParamType::Tuple(vec![]),
"uint" => ParamType::Uint(256),
s if s.starts_with("int") => {
let len = s[3..].parse().map_err(Error::ParseInt)?;
ParamType::Int(len)
}
s if s.starts_with("uint") => {
let len = s[4..].parse().map_err(Error::ParseInt)?;
ParamType::Uint(len)
}
s if s.starts_with("bytes") => {
let len = s[5..].parse().map_err(Error::ParseInt)?;
ParamType::FixedBytes(len)
}
// As discussed in https://github.com/rust-ethereum/ethabi/issues/254,
// any type that does not fit the above corresponds to a Solidity
// `enum`, and as a result we treat it as a `uint8`. This is a unique
// case which occurs in libraries with exported (`public`) Solidity
// functions with `enum` parameters.
_ => ParamType::Uint(8),
};
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::Reader;
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::ParamType;
#[test]
fn test_read_param() {
assert_eq!(Reader::read("address").unwrap(), ParamType::Address);
assert_eq!(Reader::read("bytes").unwrap(), ParamType::Bytes);
assert_eq!(Reader::read("bytes32").unwrap(), ParamType::FixedBytes(32));
assert_eq!(Reader::read("bool").unwrap(), ParamType::Bool);
assert_eq!(Reader::read("string").unwrap(), ParamType::String);
assert_eq!(Reader::read("int").unwrap(), ParamType::Int(256));
assert_eq!(Reader::read("uint").unwrap(), ParamType::Uint(256));
assert_eq!(Reader::read("int32").unwrap(), ParamType::Int(32));
assert_eq!(Reader::read("uint32").unwrap(), ParamType::Uint(32));
}
#[test]
fn test_read_array_param() {
assert_eq!(Reader::read("address[]").unwrap(), ParamType::Array(Box::new(ParamType::Address)));
assert_eq!(Reader::read("uint[]").unwrap(), ParamType::Array(Box::new(ParamType::Uint(256))));
assert_eq!(Reader::read("bytes[]").unwrap(), ParamType::Array(Box::new(ParamType::Bytes)));
assert_eq!(
Reader::read("bool[][]").unwrap(),
ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Bool))))
);
}
#[test]
fn test_read_fixed_array_param() {
assert_eq!(Reader::read("address[2]").unwrap(), ParamType::FixedArray(Box::new(ParamType::Address), 2));
assert_eq!(Reader::read("bool[17]").unwrap(), ParamType::FixedArray(Box::new(ParamType::Bool), 17));
assert_eq!(
Reader::read("bytes[45][3]").unwrap(),
ParamType::FixedArray(Box::new(ParamType::FixedArray(Box::new(ParamType::Bytes), 45)), 3)
);
}
#[test]
fn test_read_mixed_arrays() {
assert_eq!(
Reader::read("bool[][3]").unwrap(),
ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Bool))), 3)
);
assert_eq!(
Reader::read("bool[3][]").unwrap(),
ParamType::Array(Box::new(ParamType::FixedArray(Box::new(ParamType::Bool), 3)))
);
}
#[test]
fn test_read_struct_param() {
assert_eq!(
Reader::read("(address,bool)").unwrap(),
ParamType::Tuple(vec![ParamType::Address, ParamType::Bool])
);
assert_eq!(
Reader::read("(bool[3],uint256)").unwrap(),
ParamType::Tuple(vec![ParamType::FixedArray(Box::new(ParamType::Bool), 3), ParamType::Uint(256)])
);
}
#[test]
fn test_read_nested_struct_param() {
assert_eq!(
Reader::read("(address,bool,(bool,uint256))").unwrap(),
ParamType::Tuple(vec![
ParamType::Address,
ParamType::Bool,
ParamType::Tuple(vec![ParamType::Bool, ParamType::Uint(256)])
])
);
}
#[test]
fn test_read_complex_nested_struct_param() {
assert_eq!(
Reader::read("(address,bool,(bool,uint256,(bool,uint256)),(bool,uint256))").unwrap(),
ParamType::Tuple(vec![
ParamType::Address,
ParamType::Bool,
ParamType::Tuple(vec![
ParamType::Bool,
ParamType::Uint(256),
ParamType::Tuple(vec![ParamType::Bool, ParamType::Uint(256)])
]),
ParamType::Tuple(vec![ParamType::Bool, ParamType::Uint(256)])
])
);
}
#[test]
fn test_read_nested_tuple_array_param() {
assert_eq!(
Reader::read("(uint256,bytes32)[]").unwrap(),
ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Uint(256), ParamType::FixedBytes(32)])))
)
}
#[test]
fn test_read_inner_tuple_array_param() {
use crate::param_type::Writer;
let abi = "((uint256,bytes32)[],address)";
let read = Reader::read(abi).unwrap();
let param = ParamType::Tuple(vec![
ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Uint(256), ParamType::FixedBytes(32)]))),
ParamType::Address,
]);
assert_eq!(read, param);
assert_eq!(abi, Writer::write(¶m));
}
}
-- file: ethabi/src/constructor.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Contract constructor call builder.
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{encode, Bytes, Error, Param, ParamType, Result, Token};
/// Contract constructor specification.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct Constructor {
/// Constructor input.
pub inputs: Vec<Param>,
}
impl Constructor {
/// Returns all input params of given constructor.
fn param_types(&self) -> Vec<ParamType> {
self.inputs.iter().map(|p| p.kind.clone()).collect()
}
/// Prepares ABI constructor call with given input params.
pub fn encode_input(&self, code: Bytes, tokens: &[Token]) -> Result<Bytes> {
let params = self.param_types();
if Token::types_check(tokens, ¶ms) {
Ok(code.into_iter().chain(encode(tokens)).collect())
} else {
Err(Error::InvalidData)
}
}
}
-- file: ethabi/src/log.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{Bytes, Hash, Result, Token, TopicFilter};
/// Common filtering functions that are available for any event.
pub trait LogFilter {
/// Match any log parameters.
fn wildcard_filter(&self) -> TopicFilter;
}
/// trait common to things (events) that have an associated `Log` type
/// that can be parsed from a `RawLog`
pub trait ParseLog {
/// the associated `Log` type that can be parsed from a `RawLog`
/// by calling `parse_log`
type Log;
/// parse the associated `Log` type from a `RawLog`
fn parse_log(&self, log: RawLog) -> Result<Self::Log>;
}
/// Ethereum log.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RawLog {
/// Indexed event params are represented as log topics.
pub topics: Vec<Hash>,
/// Others are just plain data.
pub data: Bytes,
}
impl From<(Vec<Hash>, Bytes)> for RawLog {
fn from(raw: (Vec<Hash>, Bytes)) -> Self {
RawLog { topics: raw.0, data: raw.1 }
}
}
/// Decoded log param.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Clone)]
pub struct LogParam {
/// Decoded log name.
pub name: String,
/// Decoded log value.
pub value: Token,
}
/// Decoded log.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Clone)]
pub struct Log {
/// Log params.
pub params: Vec<LogParam>,
}
-- file: ethabi/src/lib.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Ethereum ABI encoding decoding library.
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(clippy::module_inception)]
#![warn(missing_docs)]
#[cfg_attr(not(feature = "std"), macro_use)]
extern crate alloc;
#[cfg(not(feature = "std"))]
mod no_std_prelude {
pub use alloc::{
borrow::{Cow, ToOwned},
boxed::Box,
string::{self, String, ToString},
vec::Vec,
};
}
#[cfg(feature = "std")]
mod no_std_prelude {
pub use std::borrow::Cow;
}
#[cfg(not(feature = "std"))]
use no_std_prelude::*;
mod constructor;
mod contract;
mod decoder;
mod encoder;
mod error;
mod errors;
mod event;
mod event_param;
mod filter;
mod function;
mod log;
#[cfg(feature = "serde")]
mod operation;
mod param;
pub mod param_type;
mod signature;
mod state_mutability;
pub mod token;
#[cfg(feature = "serde")]
mod tuple_param;
mod util;
#[cfg(test)]
mod tests;
pub use ethereum_types;
#[cfg(feature = "serde")]
pub use crate::tuple_param::TupleParam;
pub use crate::{
constructor::Constructor,
contract::{Contract, Events, Functions},
decoder::{decode, decode_whole},
encoder::encode,
error::Error as AbiError,
errors::{Error, Result},
event::Event,
event_param::EventParam,
filter::{RawTopicFilter, Topic, TopicFilter},
function::Function,
log::{Log, LogFilter, LogParam, ParseLog, RawLog},
param::Param,
param_type::ParamType,
signature::{long_signature, short_signature},
state_mutability::StateMutability,
token::Token,
};
/// ABI word.
pub type Word = [u8; 32];
/// ABI address.
pub type Address = ethereum_types::Address;
/// ABI fixed bytes.
pub type FixedBytes = Vec<u8>;
/// ABI bytes.
pub type Bytes = Vec<u8>;
/// ABI signed integer.
pub type Int = ethereum_types::U256;
/// ABI unsigned integer.
pub type Uint = ethereum_types::U256;
/// Commonly used FixedBytes of size 32
pub type Hash = ethereum_types::H256;
/// Contract functions generated by ethabi-derive
pub trait FunctionOutputDecoder {
/// Output types of the contract function
type Output;
/// Decodes the given bytes output for the contract function
fn decode(&self, _: &[u8]) -> Result<Self::Output>;
}
-- file: ethabi/src/decoder.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! ABI decoder.
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{Error, ParamType, Token, Word};
#[derive(Debug)]
struct DecodeResult {
token: Token,
new_offset: usize,
}
fn as_usize(slice: &Word) -> Result<usize, Error> {
if !slice[..28].iter().all(|x| *x == 0) {
return Err(Error::InvalidData);
}
let result = ((slice[28] as usize) << 24)
+ ((slice[29] as usize) << 16)
+ ((slice[30] as usize) << 8)
+ (slice[31] as usize);
Ok(result)
}
fn as_bool(slice: &Word) -> Result<bool, Error> {
if !slice[..31].iter().all(|x| *x == 0) {
return Err(Error::InvalidData);
}
Ok(slice[31] == 1)
}
fn decode_offset(types: &[ParamType], data: &[u8]) -> Result<(Vec<Token>, usize), Error> {
let is_empty_bytes_valid_encoding = types.iter().all(|t| t.is_empty_bytes_valid_encoding());
if !is_empty_bytes_valid_encoding && data.is_empty() {
return Err(Error::InvalidName(
"please ensure the contract and method you're calling exist! \
failed to decode empty bytes. if you're using jsonrpc this is \
likely due to jsonrpc returning `0x` in case contract or method \
don't exist"
.into(),
));
}
let mut tokens = vec![];
let mut offset = 0;
for param in types {
let res = decode_param(param, data, offset)?;
offset = res.new_offset;
tokens.push(res.token);
}
Ok((tokens, offset))
}
/// Decodes ABI compliant vector of bytes into vector of tokens described by types param.
/// Fails, if some data left to decode
pub fn decode_whole(types: &[ParamType], data: &[u8]) -> Result<Vec<Token>, Error> {
decode_offset(types, data).and_then(
|(tokens, offset)| {
if offset != data.len() {
Err(Error::InvalidData)
} else {
Ok(tokens)
}
},
)
}
/// Decodes ABI compliant vector of bytes into vector of tokens described by types param.
/// Returns ok, even if some data left to decode
pub fn decode(types: &[ParamType], data: &[u8]) -> Result<Vec<Token>, Error> {
decode_offset(types, data).map(|(tokens, _)| tokens)
}
fn peek(data: &[u8], offset: usize, len: usize) -> Result<&[u8], Error> {
if offset + len > data.len() {
Err(Error::InvalidData)
} else {
Ok(&data[offset..(offset + len)])
}
}
fn peek_32_bytes(data: &[u8], offset: usize) -> Result<Word, Error> {
peek(data, offset, 32).map(|x| {
let mut out: Word = [0u8; 32];
out.copy_from_slice(&x[0..32]);
out
})
}
fn take_bytes(data: &[u8], offset: usize, len: usize) -> Result<Vec<u8>, Error> {
if offset + len > data.len() {
Err(Error::InvalidData)
} else {
Ok(data[offset..(offset + len)].to_vec())
}
}
fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> Result<DecodeResult, Error> {
match *param {
ParamType::Address => {
let slice = peek_32_bytes(data, offset)?;
let mut address = [0u8; 20];
address.copy_from_slice(&slice[12..]);
let result = DecodeResult { token: Token::Address(address.into()), new_offset: offset + 32 };
Ok(result)
}
ParamType::Int(_) => {
let slice = peek_32_bytes(data, offset)?;
let result = DecodeResult { token: Token::Int(slice.into()), new_offset: offset + 32 };
Ok(result)
}
ParamType::Uint(_) => {
let slice = peek_32_bytes(data, offset)?;
let result = DecodeResult { token: Token::Uint(slice.into()), new_offset: offset + 32 };
Ok(result)
}
ParamType::Bool => {
let b = as_bool(&peek_32_bytes(data, offset)?)?;
let result = DecodeResult { token: Token::Bool(b), new_offset: offset + 32 };
Ok(result)
}
ParamType::FixedBytes(len) => {
// FixedBytes is anything from bytes1 to bytes32. These values
// are padded with trailing zeros to fill 32 bytes.
let bytes = take_bytes(data, offset, len)?;
let result = DecodeResult { token: Token::FixedBytes(bytes), new_offset: offset + 32 };
Ok(result)
}
ParamType::Bytes => {
let dynamic_offset = as_usize(&peek_32_bytes(data, offset)?)?;
let len = as_usize(&peek_32_bytes(data, dynamic_offset)?)?;
let bytes = take_bytes(data, dynamic_offset + 32, len)?;
let result = DecodeResult { token: Token::Bytes(bytes), new_offset: offset + 32 };
Ok(result)
}
ParamType::String => {
let dynamic_offset = as_usize(&peek_32_bytes(data, offset)?)?;
let len = as_usize(&peek_32_bytes(data, dynamic_offset)?)?;
let bytes = take_bytes(data, dynamic_offset + 32, len)?;
let result = DecodeResult {
// NOTE: We're decoding strings using lossy UTF-8 decoding to
// prevent invalid strings written into contracts by either users or
// Solidity bugs from causing graph-node to fail decoding event
// data.
token: Token::String(String::from_utf8_lossy(&bytes).into()),
new_offset: offset + 32,
};
Ok(result)
}
ParamType::Array(ref t) => {
let len_offset = as_usize(&peek_32_bytes(data, offset)?)?;
let len = as_usize(&peek_32_bytes(data, len_offset)?)?;
let tail_offset = len_offset + 32;
let tail = &data[tail_offset..];
let mut tokens = vec![];
let mut new_offset = 0;
for _ in 0..len {
let res = decode_param(t, tail, new_offset)?;
new_offset = res.new_offset;
tokens.push(res.token);
}
let result = DecodeResult { token: Token::Array(tokens), new_offset: offset + 32 };
Ok(result)
}
ParamType::FixedArray(ref t, len) => {
let is_dynamic = param.is_dynamic();
let (tail, mut new_offset) = if is_dynamic {
let offset = as_usize(&peek_32_bytes(data, offset)?)?;
if offset > data.len() {
return Err(Error::InvalidData);
}
(&data[offset..], 0)
} else {
(data, offset)
};
let mut tokens = vec![];
for _ in 0..len {
let res = decode_param(t, tail, new_offset)?;
new_offset = res.new_offset;
tokens.push(res.token);
}
let result = DecodeResult {
token: Token::FixedArray(tokens),
new_offset: if is_dynamic { offset + 32 } else { new_offset },
};
Ok(result)
}
ParamType::Tuple(ref t) => {
let is_dynamic = param.is_dynamic();
// The first element in a dynamic Tuple is an offset to the Tuple's data
// For a static Tuple the data begins right away
let (tail, mut new_offset) = if is_dynamic {
let offset = as_usize(&peek_32_bytes(data, offset)?)?;
if offset > data.len() {
return Err(Error::InvalidData);
}
(&data[offset..], 0)
} else {
(data, offset)
};
let len = t.len();
let mut tokens = Vec::with_capacity(len);
for param in t {
let res = decode_param(param, tail, new_offset)?;
new_offset = res.new_offset;
tokens.push(res.token);
}
// The returned new_offset depends on whether the Tuple is dynamic
// dynamic Tuple -> follows the prefixed Tuple data offset element
// static Tuple -> follows the last data element
let result = DecodeResult {
token: Token::Tuple(tokens),
new_offset: if is_dynamic { offset + 32 } else { new_offset },
};
Ok(result)
}
}
}
#[cfg(test)]
mod tests {
use hex_literal::hex;
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{decode, decode_whole, ParamType, Token, Uint};
#[test]
fn decode_from_empty_byte_slice() {
// these can NOT be decoded from empty byte slice
assert!(decode(&[ParamType::Address], &[]).is_err());
assert!(decode(&[ParamType::Bytes], &[]).is_err());
assert!(decode(&[ParamType::Int(0)], &[]).is_err());
assert!(decode(&[ParamType::Int(1)], &[]).is_err());
assert!(decode(&[ParamType::Int(0)], &[]).is_err());
assert!(decode(&[ParamType::Int(1)], &[]).is_err());
assert!(decode(&[ParamType::Bool], &[]).is_err());
assert!(decode(&[ParamType::String], &[]).is_err());
assert!(decode(&[ParamType::Array(Box::new(ParamType::Bool))], &[]).is_err());
assert!(decode(&[ParamType::FixedBytes(1)], &[]).is_err());
assert!(decode(&[ParamType::FixedArray(Box::new(ParamType::Bool), 1)], &[]).is_err());
// these are the only ones that can be decoded from empty byte slice
assert!(decode(&[ParamType::FixedBytes(0)], &[]).is_ok());
assert!(decode(&[ParamType::FixedArray(Box::new(ParamType::Bool), 0)], &[]).is_ok());
}
#[test]
fn decode_static_tuple_of_addresses_and_uints() {
let encoded = hex!(
"
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
1111111111111111111111111111111111111111111111111111111111111111
"
);
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let uint = Token::Uint([0x11u8; 32].into());
let tuple = Token::Tuple(vec![address1, address2, uint]);
let expected = vec![tuple];
let decoded =
decode(&[ParamType::Tuple(vec![ParamType::Address, ParamType::Address, ParamType::Uint(32)])], &encoded)
.unwrap();
assert_eq!(decoded, expected);
}
#[test]
fn decode_dynamic_tuple() {
let encoded = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000009
6761766f66796f726b0000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000009
6761766f66796f726b0000000000000000000000000000000000000000000000
"
);
let string1 = Token::String("gavofyork".to_owned());
let string2 = Token::String("gavofyork".to_owned());
let tuple = Token::Tuple(vec![string1, string2]);
let decoded = decode(&[ParamType::Tuple(vec![ParamType::String, ParamType::String])], &encoded).unwrap();
let expected = vec![tuple];
assert_eq!(decoded, expected);
}
#[test]
fn decode_nested_tuple() {
let encoded = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000001
00000000000000000000000000000000000000000000000000000000000000c0
0000000000000000000000000000000000000000000000000000000000000100
0000000000000000000000000000000000000000000000000000000000000004
7465737400000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000006
6379626f72670000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000060
00000000000000000000000000000000000000000000000000000000000000a0
00000000000000000000000000000000000000000000000000000000000000e0
0000000000000000000000000000000000000000000000000000000000000005
6e69676874000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000003
6461790000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000004
7765656500000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000008
66756e7465737473000000000000000000000000000000000000000000000000
"
);
let string1 = Token::String("test".to_owned());
let string2 = Token::String("cyborg".to_owned());
let string3 = Token::String("night".to_owned());
let string4 = Token::String("day".to_owned());
let string5 = Token::String("weee".to_owned());
let string6 = Token::String("funtests".to_owned());
let bool = Token::Bool(true);
let deep_tuple = Token::Tuple(vec![string5, string6]);
let inner_tuple = Token::Tuple(vec![string3, string4, deep_tuple]);
let outer_tuple = Token::Tuple(vec![string1, bool, string2, inner_tuple]);
let expected = vec![outer_tuple];
let decoded = decode(
&[ParamType::Tuple(vec![
ParamType::String,
ParamType::Bool,
ParamType::String,
ParamType::Tuple(vec![
ParamType::String,
ParamType::String,
ParamType::Tuple(vec![ParamType::String, ParamType::String]),
]),
])],
&encoded,
)
.unwrap();
assert_eq!(decoded, expected);
}
#[test]
fn decode_complex_tuple_of_dynamic_and_static_types() {
let encoded = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
1111111111111111111111111111111111111111111111111111111111111111
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000000000000000000000000000000000000000000009
6761766f66796f726b0000000000000000000000000000000000000000000000
"
);
let uint = Token::Uint([0x11u8; 32].into());
let string = Token::String("gavofyork".to_owned());
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let tuple = Token::Tuple(vec![uint, string, address1, address2]);
let expected = vec![tuple];
let decoded = decode(
&[ParamType::Tuple(vec![ParamType::Uint(32), ParamType::String, ParamType::Address, ParamType::Address])],
&encoded,
)
.unwrap();
assert_eq!(decoded, expected);
}
#[test]
fn decode_params_containing_dynamic_tuple() {
let encoded = hex!(
"
0000000000000000000000002222222222222222222222222222222222222222
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000060
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000009
7370616365736869700000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000006
6379626f72670000000000000000000000000000000000000000000000000000
"
);
let address1 = Token::Address([0x22u8; 20].into());
let bool1 = Token::Bool(true);
let string1 = Token::String("spaceship".to_owned());
let string2 = Token::String("cyborg".to_owned());
let tuple = Token::Tuple(vec![bool1, string1, string2]);
let address2 = Token::Address([0x33u8; 20].into());
let address3 = Token::Address([0x44u8; 20].into());
let bool2 = Token::Bool(false);
let expected = vec![address1, tuple, address2, address3, bool2];
let decoded = decode(
&[
ParamType::Address,
ParamType::Tuple(vec![ParamType::Bool, ParamType::String, ParamType::String]),
ParamType::Address,
ParamType::Address,
ParamType::Bool,
],
&encoded,
)
.unwrap();
assert_eq!(decoded, expected);
}
#[test]
fn decode_params_containing_static_tuple() {
let encoded = hex!(
"
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444
"
);
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let bool1 = Token::Bool(true);
let bool2 = Token::Bool(false);
let tuple = Token::Tuple(vec![address2, bool1, bool2]);
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let expected = vec![address1, tuple, address3, address4];
let decoded = decode(
&[
ParamType::Address,
ParamType::Tuple(vec![ParamType::Address, ParamType::Bool, ParamType::Bool]),
ParamType::Address,
ParamType::Address,
],
&encoded,
)
.unwrap();
assert_eq!(decoded, expected);
}
#[test]
fn decode_data_with_size_that_is_not_a_multiple_of_32() {
let encoded = hex!(
"
0000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000152
0000000000000000000000000000000000000000000000000000000000000001
000000000000000000000000000000000000000000000000000000000054840d
0000000000000000000000000000000000000000000000000000000000000092
3132323033393637623533326130633134633938306235616566666231373034
3862646661656632633239336139353039663038656233633662306635663866
3039343265376239636337366361353163636132366365353436393230343438
6533303866646136383730623565326165313261323430396439343264653432
3831313350373230703330667073313678390000000000000000000000000000
0000000000000000000000000000000000103933633731376537633061363531
3761
"
);
assert_eq!(
decode(
&[
ParamType::Uint(256),
ParamType::String,
ParamType::String,
ParamType::Uint(256),
ParamType::Uint(256),
],
&encoded,
).unwrap(),
&[
Token::Uint(Uint::from(0)),
Token::String(String::from("12203967b532a0c14c980b5aeffb17048bdfaef2c293a9509f08eb3c6b0f5f8f0942e7b9cc76ca51cca26ce546920448e308fda6870b5e2ae12a2409d942de428113P720p30fps16x9")),
Token::String(String::from("93c717e7c0a6517a")),
Token::Uint(Uint::from(1)),
Token::Uint(Uint::from(5538829))
]
);
}
#[test]
fn decode_after_fixed_bytes_with_less_than_32_bytes() {
let encoded = hex!(
"
0000000000000000000000008497afefdc5ac170a664a231f6efb25526ef813f
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000080
000000000000000000000000000000000000000000000000000000000000000a
3078303030303030314600000000000000000000000000000000000000000000
"
);
assert_eq!(
decode(
&[ParamType::Address, ParamType::FixedBytes(32), ParamType::FixedBytes(4), ParamType::String,],
&encoded,
)
.unwrap(),
&[
Token::Address(hex!("8497afefdc5ac170a664a231f6efb25526ef813f").into()),
Token::FixedBytes([0u8; 32].to_vec()),
Token::FixedBytes([0u8; 4].to_vec()),
Token::String("0x0000001F".into()),
]
)
}
#[test]
fn decode_broken_utf8() {
let encoded = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000004
e4b88de500000000000000000000000000000000000000000000000000000000
"
);
assert_eq!(decode(&[ParamType::String,], &encoded).unwrap(), &[Token::String("不�".into())]);
}
#[test]
fn decode_corrupted_dynamic_array() {
// line 1 at 0x00 = 0: tail offset of array
// line 2 at 0x20 = 32: length of array
// line 3 at 0x40 = 64: first word
// line 4 at 0x60 = 96: second word
let encoded = hex!(
"
0000000000000000000000000000000000000000000000000000000000000020
00000000000000000000000000000000000000000000000000000000ffffffff
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000002
"
);
assert!(decode(&[ParamType::Array(Box::new(ParamType::Uint(32)))], &encoded).is_err());
}
#[test]
fn decode_corrupted_nested_array_tuple() {
let input = hex!(
"
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000002a0
0000000000000000000000000000000000000000000000000000000000000009
00000000000000000000000000000000fffffffffffffffffffffffffffffffe
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000ffffffffffffffff
0008000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000020000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000001000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000053a
0100000000000000000000000000000000000000000000000000000000000000
0000000000000010000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000002000000
0000000000000000000000000000000000000000000000000000000000100000
0000000000000000000000000000000000000000000000000000000000000000
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
0000000000000000000000000000000000000000000000000000000000000006
00000000000000000000000000000000000000000000000000000000000000c0
0000000000000000000000000000000000000000000000000000000000002ce0
0000000000000000000000000000000000000000000000000000000000005880
0000000000000000000000000000000000000000000000000000000000008280
000000000000000000000000000000000000000000000000000000000000acc0
000000000000000000000000000000000000000000000000000000000000d6e0
0000000000000000000000000000000000000000020000000000000000000000
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000009
0000000000000000000000000000000000000000000000000000000000000120
0000000000000000000000000000000000000000000000000000000000000720
0000000000000000000000000000000000000000000000000000000000000b80
0000000000000000000000000000000000000000000000000000000000000fe0
"
);
let func = {
use crate::{Function, Param};
use ParamType::*;
#[allow(deprecated)]
Function {
name: "f_tuple".to_string(),
inputs: vec![
Param {
name: "c".to_string(),
kind: Array(Box::new(Tuple(vec![Uint(256), Uint(256)]))),
internal_type: None,
},
Param {
name: "d".to_string(),
kind: Array(Box::new(Tuple(vec![
Uint(256),
Array(Box::new(Tuple(vec![Uint(256), Array(Box::new(ParamType::String))]))),
]))),
internal_type: None,
},
],
outputs: vec![],
constant: None,
state_mutability: crate::StateMutability::default(),
}
};
assert!(func.decode_input(&input).is_err());
}
#[test]
fn decode_corrupted_fixed_array_of_strings() {
let input = hex!(
"
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000001000040
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000008
5445535454455354000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000008
5445535454455354000000000000000000000000000000000000000000000000
"
);
let func = {
use crate::{Function, Param};
use ParamType::*;
#[allow(deprecated)]
Function {
name: "f".to_string(),
inputs: vec![
Param { name: "i".to_string(), kind: Uint(256), internal_type: None },
Param {
name: "p".to_string(),
kind: FixedArray(Box::new(ParamType::String), 2),
internal_type: None,
},
],
outputs: vec![],
constant: None,
state_mutability: crate::StateMutability::default(),
}
};
assert!(func.decode_input(&input).is_err());
}
#[test]
fn decode_whole_addresses() {
let input = hex!(
"
0000000000000000000000000000000000000000000000000000000000012345
0000000000000000000000000000000000000000000000000000000000054321
"
);
assert!(decode(&[ParamType::Address], &input).is_ok());
assert!(decode_whole(&[ParamType::Address], &input).is_err());
assert!(decode_whole(&[ParamType::Address, ParamType::Address], &input).is_ok());
}
}
-- file: ethabi/src/event.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Contract event.
use alloc::collections::BTreeMap;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use sha3::{Digest, Keccak256};
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{
decode, decode_whole, encode, signature::long_signature, Error, EventParam, Hash, Log, LogParam, ParamType, RawLog,
RawTopicFilter, Result, Token, Topic, TopicFilter,
};
/// Contract event.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Event {
/// Event name.
#[cfg_attr(feature = "serde", serde(deserialize_with = "crate::util::sanitize_name::deserialize"))]
pub name: String,
/// Event input.
pub inputs: Vec<EventParam>,
/// If anonymous, event cannot be found using `from` filter.
pub anonymous: bool,
}
impl Event {
/// Returns names of all params.
fn params_names(&self) -> Vec<String> {
self.inputs.iter().map(|p| p.name.clone()).collect()
}
/// Returns types of all params.
fn param_types(&self) -> Vec<ParamType> {
self.inputs.iter().map(|p| p.kind.clone()).collect()
}
/// Returns all params of the event.
fn indexed_params(&self, indexed: bool) -> Vec<EventParam> {
self.inputs.iter().filter(|p| p.indexed == indexed).cloned().collect()
}
/// Event signature
pub fn signature(&self) -> Hash {
long_signature(&self.name, &self.param_types())
}
/// Creates topic filter
pub fn filter(&self, raw: RawTopicFilter) -> Result<TopicFilter> {
fn convert_token(token: Token, kind: &ParamType) -> Result<Hash> {
if !token.type_check(kind) {
return Err(Error::InvalidData);
}
let encoded = encode(&[token]);
if encoded.len() == 32 {
let mut data = [0u8; 32];
data.copy_from_slice(&encoded);
Ok(data.into())
} else {
Ok(Hash::from_slice(Keccak256::digest(&encoded).as_slice()))
}
}
fn convert_topic(topic: Topic<Token>, kind: Option<&ParamType>) -> Result<Topic<Hash>> {
match topic {
Topic::Any => Ok(Topic::Any),
Topic::OneOf(tokens) => match kind {
None => Err(Error::InvalidData),
Some(kind) => {
let topics =
tokens.into_iter().map(|token| convert_token(token, kind)).collect::<Result<Vec<_>>>()?;
Ok(Topic::OneOf(topics))
}
},
Topic::This(token) => match kind {
None => Err(Error::InvalidData),
Some(kind) => Ok(Topic::This(convert_token(token, kind)?)),
},
}
}
let kinds: Vec<_> = self.indexed_params(true).into_iter().map(|param| param.kind).collect();
let result = if self.anonymous {
TopicFilter {
topic0: convert_topic(raw.topic0, kinds.get(0))?,
topic1: convert_topic(raw.topic1, kinds.get(1))?,
topic2: convert_topic(raw.topic2, kinds.get(2))?,
topic3: Topic::Any,
}
} else {
TopicFilter {
topic0: Topic::This(self.signature()),
topic1: convert_topic(raw.topic0, kinds.get(0))?,
topic2: convert_topic(raw.topic1, kinds.get(1))?,
topic3: convert_topic(raw.topic2, kinds.get(2))?,
}
};
Ok(result)
}
// Converts param types for indexed parameters to bytes32 where appropriate
// This applies to strings, arrays, structs and bytes to follow the encoding of
// these indexed param types according to
// https://solidity.readthedocs.io/en/develop/abi-spec.html#encoding-of-indexed-event-parameters
fn convert_topic_param_type(&self, kind: &ParamType) -> ParamType {
match kind {
ParamType::String
| ParamType::Bytes
| ParamType::Array(_)
| ParamType::FixedArray(_, _)
| ParamType::Tuple(_) => ParamType::FixedBytes(32),
_ => kind.clone(),
}
}
fn parse_log_inner<F: Fn(&[ParamType], &[u8]) -> Result<Vec<Token>>>(&self, log: RawLog, decode: F) -> Result<Log> {
let topics = log.topics;
let data = log.data;
let topics_len = topics.len();
// obtains all params info
let topic_params = self.indexed_params(true);
let data_params = self.indexed_params(false);
// then take first topic if event is not anonymous
let to_skip = if self.anonymous {
0
} else {
// verify
let event_signature = topics.get(0).ok_or(Error::InvalidData)?;
if event_signature != &self.signature() {
return Err(Error::InvalidData);
}
1
};
let topic_types =
topic_params.iter().map(|p| self.convert_topic_param_type(&p.kind)).collect::<Vec<ParamType>>();
let flat_topics = topics.into_iter().skip(to_skip).flat_map(|t| t.as_ref().to_vec()).collect::<Vec<u8>>();
let topic_tokens = decode(&topic_types, &flat_topics)?;
// topic may be only a 32 bytes encoded token
if topic_tokens.len() != topics_len - to_skip {
return Err(Error::InvalidData);
}
let topics_named_tokens = topic_params.into_iter().map(|p| p.name).zip(topic_tokens.into_iter());
let data_types = data_params.iter().map(|p| p.kind.clone()).collect::<Vec<ParamType>>();
let data_tokens = decode(&data_types, &data)?;
let data_named_tokens = data_params.into_iter().map(|p| p.name).zip(data_tokens.into_iter());
let named_tokens = topics_named_tokens.chain(data_named_tokens).collect::<BTreeMap<String, Token>>();
let decoded_params = self
.params_names()
.into_iter()
.map(|name| LogParam { name: name.clone(), value: named_tokens[&name].clone() })
.collect();
let result = Log { params: decoded_params };
Ok(result)
}
/// Parses `RawLog` and retrieves all log params from it.
/// Fails, if some data left to decode
pub fn parse_log_whole(&self, log: RawLog) -> Result<Log> {
self.parse_log_inner(log, decode_whole)
}
/// Parses `RawLog` and retrieves all log params from it.
/// Returns ok, even if some data left to decode
pub fn parse_log(&self, log: RawLog) -> Result<Log> {
self.parse_log_inner(log, decode)
}
}
#[cfg(test)]
mod tests {
use hex_literal::hex;
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{
log::{Log, RawLog},
signature::long_signature,
token::Token,
Event, EventParam, LogParam, ParamType,
};
#[test]
fn test_decoding_event() {
let event = Event {
name: "foo".to_owned(),
inputs: vec![
EventParam { name: "a".to_owned(), kind: ParamType::Int(256), indexed: false },
EventParam { name: "b".to_owned(), kind: ParamType::Int(256), indexed: true },
EventParam { name: "c".to_owned(), kind: ParamType::Address, indexed: false },
EventParam { name: "d".to_owned(), kind: ParamType::Address, indexed: true },
EventParam { name: "e".to_owned(), kind: ParamType::String, indexed: true },
EventParam {
name: "f".to_owned(),
kind: ParamType::Array(Box::new(ParamType::Int(256))),
indexed: true,
},
EventParam {
name: "g".to_owned(),
kind: ParamType::FixedArray(Box::new(ParamType::Address), 5),
indexed: true,
},
],
anonymous: false,
};
let log = RawLog {
topics: vec![
long_signature(
"foo",
&[
ParamType::Int(256),
ParamType::Int(256),
ParamType::Address,
ParamType::Address,
ParamType::String,
ParamType::Array(Box::new(ParamType::Int(256))),
ParamType::FixedArray(Box::new(ParamType::Address), 5),
],
),
hex!("0000000000000000000000000000000000000000000000000000000000000002").into(),
hex!("0000000000000000000000001111111111111111111111111111111111111111").into(),
hex!("00000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").into(),
hex!("00000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").into(),
hex!("00000000000000000ccccccccccccccccccccccccccccccccccccccccccccccc").into(),
],
data: hex!(
"
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000002222222222222222222222222222222222222222
"
)
.into(),
};
let result = event.parse_log(log).unwrap();
assert_eq!(
result,
Log {
params: [
("a", Token::Int(hex!("0000000000000000000000000000000000000000000000000000000000000003").into()),),
("b", Token::Int(hex!("0000000000000000000000000000000000000000000000000000000000000002").into()),),
("c", Token::Address(hex!("2222222222222222222222222222222222222222").into())),
("d", Token::Address(hex!("1111111111111111111111111111111111111111").into())),
(
"e",
Token::FixedBytes(
hex!("00000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").into()
)
),
(
"f",
Token::FixedBytes(
hex!("00000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").into()
)
),
(
"g",
Token::FixedBytes(
hex!("00000000000000000ccccccccccccccccccccccccccccccccccccccccccccccc").into()
)
),
]
.iter()
.cloned()
.map(|(name, value)| LogParam { name: name.to_string(), value })
.collect::<Vec<_>>()
}
);
}
#[test]
fn parse_log_whole() {
let correct_event = Event {
name: "Test".into(),
inputs: vec![
EventParam {
name: "tuple".into(),
kind: ParamType::Tuple(vec![ParamType::Address, ParamType::Address]),
indexed: false,
},
EventParam { name: "addr".into(), kind: ParamType::Address, indexed: true },
],
anonymous: false,
};
// swap indexed params
let mut wrong_event = correct_event.clone();
wrong_event.inputs[0].indexed = true;
wrong_event.inputs[1].indexed = false;
let log = RawLog {
topics: vec![
hex!("cf74b4e62f836eeedcd6f92120ffb5afea90e6fa490d36f8b81075e2a7de0cf7").into(),
hex!("0000000000000000000000000000000000000000000000000000000000012321").into(),
],
data: hex!(
"
0000000000000000000000000000000000000000000000000000000000012345
0000000000000000000000000000000000000000000000000000000000054321
"
)
.into(),
};
assert!(wrong_event.parse_log(log.clone()).is_ok());
assert!(wrong_event.parse_log_whole(log.clone()).is_err());
assert!(correct_event.parse_log_whole(log).is_ok());
}
}
-- file: ethabi/src/event_param.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Event param specification.
#[cfg(feature = "serde")]
use core::fmt;
#[cfg(feature = "serde")]
use serde::{
de::{Error, MapAccess, Visitor},
ser::SerializeMap,
Deserialize, Deserializer, Serialize, Serializer,
};
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::ParamType;
#[cfg(feature = "serde")]
use crate::{param_type::Writer, TupleParam};
/// Event param specification.
#[derive(Debug, Clone, PartialEq)]
pub struct EventParam {
/// Param name.
pub name: String,
/// Param type.
pub kind: ParamType,
/// Indexed flag. If true, param is used to build block bloom.
pub indexed: bool,
}
#[cfg(feature = "serde")]
impl<'a> Deserialize<'a> for EventParam {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
deserializer.deserialize_any(EventParamVisitor)
}
}
#[cfg(feature = "serde")]
struct EventParamVisitor;
#[cfg(feature = "serde")]
impl<'a> Visitor<'a> for EventParamVisitor {
type Value = EventParam;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a valid event parameter spec")
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where
V: MapAccess<'a>,
{
let mut name = None;
let mut kind = None;
let mut indexed = None;
let mut components = None;
while let Some(ref key) = map.next_key::<String>()? {
match key.as_ref() {
"name" => {
if name.is_some() {
return Err(Error::duplicate_field("name"));
}
name = Some(map.next_value()?);
}
"type" => {
if kind.is_some() {
return Err(Error::duplicate_field("kind"));
}
kind = Some(map.next_value()?);
}
"components" => {
if components.is_some() {
return Err(Error::duplicate_field("components"));
}
let component: Vec<TupleParam> = map.next_value()?;
components = Some(component)
}
"indexed" => {
if indexed.is_some() {
return Err(Error::duplicate_field("indexed"));
}
indexed = Some(map.next_value()?);
}
_ => {}
}
}
let name = name.ok_or_else(|| Error::missing_field("name"))?;
let mut kind = kind.ok_or_else(|| Error::missing_field("kind"))?;
crate::param::set_tuple_components(&mut kind, components)?;
let indexed = indexed.unwrap_or(false);
Ok(EventParam { name, kind, indexed })
}
}
#[cfg(feature = "serde")]
impl Serialize for EventParam {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("name", &self.name)?;
map.serialize_entry("type", &Writer::write_for_abi(&self.kind, false))?;
map.serialize_entry("indexed", &self.indexed)?;
if let Some(inner_tuple) = crate::param::inner_tuple(&self.kind) {
map.serialize_key("components")?;
map.serialize_value(&crate::param::SerializeableParamVec(inner_tuple))?;
}
map.end()
}
}
#[cfg(all(test, feature = "serde"))]
mod tests {
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{tests::assert_json_eq, EventParam, ParamType};
#[test]
fn event_param_deserialization() {
let s = r#"{
"name": "foo",
"type": "address",
"indexed": true
}"#;
let deserialized: EventParam = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, EventParam { name: "foo".to_owned(), kind: ParamType::Address, indexed: true });
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn event_param_tuple_deserialization() {
let s = r#"{
"name": "foo",
"type": "tuple",
"indexed": true,
"components": [
{
"type": "uint48"
},
{
"type": "tuple",
"components": [
{
"type": "address"
}
]
}
]
}"#;
let deserialized: EventParam = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
EventParam {
name: "foo".to_owned(),
kind: ParamType::Tuple(vec![ParamType::Uint(48), ParamType::Tuple(vec![ParamType::Address])]),
indexed: true,
}
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn event_param_tuple_array_deserialization() {
let s = r#"{
"components": [
{ "type": "uint256" },
{ "type": "address" },
{
"components": [
{ "type": "address" },
{ "type": "address" }
],
"type": "tuple"
},
{ "type": "uint256" },
{
"components": [
{
"components": [
{ "type": "address" },
{ "type": "bytes" }
],
"type": "tuple[]"
},
{
"components": [
{ "type": "address" },
{ "type": "uint256" }
],
"type": "tuple[]"
},
{ "type": "uint256" }
],
"type": "tuple[]"
},
{ "type": "uint256" }
],
"indexed": false,
"name": "LogTaskSubmitted",
"type": "tuple"
}"#;
let deserialized: EventParam = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
EventParam {
name: "LogTaskSubmitted".to_owned(),
kind: ParamType::Tuple(vec![
ParamType::Uint(256),
ParamType::Address,
ParamType::Tuple(vec![ParamType::Address, ParamType::Address]),
ParamType::Uint(256),
ParamType::Array(Box::new(ParamType::Tuple(vec![
ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Address, ParamType::Bytes,]))),
ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Address, ParamType::Uint(256)]))),
ParamType::Uint(256),
]))),
ParamType::Uint(256),
]),
indexed: false,
}
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
}
-- file: ethabi/src/state_mutability.rs --
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Whether a function modifies or reads blockchain state
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum StateMutability {
/// Specified not to read blockchain state
#[cfg_attr(feature = "serde", serde(rename = "pure"))]
Pure,
/// Specified to not modify the blockchain state
#[cfg_attr(feature = "serde", serde(rename = "view"))]
View,
/// Function does not accept Ether - the default
#[cfg_attr(feature = "serde", serde(rename = "nonpayable"))]
NonPayable,
/// Function accepts Ether
#[cfg_attr(feature = "serde", serde(rename = "payable"))]
Payable,
}
impl Default for StateMutability {
fn default() -> Self {
Self::NonPayable
}
}
#[cfg(all(test, feature = "serde"))]
mod test {
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{tests::assert_json_eq, StateMutability};
#[test]
fn state_mutability() {
let json = r#"
[
"pure",
"view",
"nonpayable",
"payable"
]
"#;
let deserialized: Vec<StateMutability> = serde_json::from_str(json).unwrap();
assert_eq!(
deserialized,
vec![StateMutability::Pure, StateMutability::View, StateMutability::NonPayable, StateMutability::Payable,]
);
assert_json_eq(json, &serde_json::to_string(&deserialized).unwrap());
}
}
-- file: ethabi/src/function.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Contract function call builder.
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{
decode, encode, signature::short_signature, Bytes, Error, Param, ParamType, Result, StateMutability, Token,
};
/// Contract function specification.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct Function {
/// Function name.
#[cfg_attr(feature = "serde", serde(deserialize_with = "crate::util::sanitize_name::deserialize"))]
pub name: String,
/// Function input.
pub inputs: Vec<Param>,
/// Function output.
pub outputs: Vec<Param>,
#[deprecated(note = "The constant attribute was removed in Solidity 0.5.0 and has been \
replaced with stateMutability.")]
/// Constant function.
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub constant: Option<bool>,
/// Whether the function reads or modifies blockchain state
#[cfg_attr(feature = "serde", serde(rename = "stateMutability", default))]
pub state_mutability: StateMutability,
}
impl Function {
/// Returns all input params of given function.
fn input_param_types(&self) -> Vec<ParamType> {
self.inputs.iter().map(|p| p.kind.clone()).collect()
}
/// Returns all output params of given function.
fn output_param_types(&self) -> Vec<ParamType> {
self.outputs.iter().map(|p| p.kind.clone()).collect()
}
/// Prepares ABI function call with given input params.
pub fn encode_input(&self, tokens: &[Token]) -> Result<Bytes> {
let params = self.input_param_types();
if !Token::types_check(tokens, ¶ms) {
return Err(Error::InvalidData);
}
let signed = short_signature(&self.name, ¶ms).to_vec();
let encoded = encode(tokens);
Ok(signed.into_iter().chain(encoded.into_iter()).collect())
}
/// Return the 4 byte short signature of this function.
pub fn short_signature(&self) -> [u8; 4] {
let params = self.input_param_types();
short_signature(&self.name, ¶ms)
}
/// Parses the ABI function output to list of tokens.
pub fn decode_output(&self, data: &[u8]) -> Result<Vec<Token>> {
decode(&self.output_param_types(), data)
}
/// Parses the ABI function input to a list of tokens.
pub fn decode_input(&self, data: &[u8]) -> Result<Vec<Token>> {
decode(&self.input_param_types(), data)
}
/// Returns a signature that uniquely identifies this function.
///
/// Examples:
/// - `functionName()`
/// - `functionName():(uint256)`
/// - `functionName(bool):(uint256,string)`
/// - `functionName(uint256,bytes32):(string,uint256)`
pub fn signature(&self) -> String {
let inputs = self.inputs.iter().map(|p| p.kind.to_string()).collect::<Vec<_>>().join(",");
let outputs = self.outputs.iter().map(|p| p.kind.to_string()).collect::<Vec<_>>().join(",");
match (inputs.len(), outputs.len()) {
(_, 0) => format!("{}({inputs})", self.name),
(_, _) => format!("{}({inputs}):({outputs})", self.name),
}
}
}
#[cfg(test)]
mod tests {
use hex_literal::hex;
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{Function, Param, ParamType, StateMutability, Token};
#[test]
fn test_function_encode_call() {
#[allow(deprecated)]
let func = Function {
name: "baz".to_owned(),
inputs: vec![
Param { name: "a".to_owned(), kind: ParamType::Uint(32), internal_type: None },
Param { name: "b".to_owned(), kind: ParamType::Bool, internal_type: None },
],
outputs: vec![],
constant: None,
state_mutability: StateMutability::Payable,
};
let mut uint = [0u8; 32];
uint[31] = 69;
let encoded = func.encode_input(&[Token::Uint(uint.into()), Token::Bool(true)]).unwrap();
let expected = hex!("cdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001").to_vec();
assert_eq!(encoded, expected);
let expected_sig = hex!("cdcd77c0").to_vec();
assert_eq!(func.short_signature().to_vec(), expected_sig);
}
}
-- file: ethabi/src/errors.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::no_std_prelude::Cow;
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
#[cfg(feature = "serde")]
use core::num;
#[cfg(feature = "std")]
use thiserror::Error;
/// Ethabi result type
pub type Result<T> = core::result::Result<T, Error>;
/// Ethabi errors
#[cfg_attr(feature = "std", derive(Error))]
#[derive(Debug)]
pub enum Error {
/// Invalid entity such as a bad function name.
#[cfg_attr(feature = "std", error("Invalid name: {0}"))]
InvalidName(String),
/// Invalid data.
#[cfg_attr(feature = "std", error("Invalid data"))]
InvalidData,
/// Serialization error.
#[cfg(feature = "full-serde")]
#[error("Serialization error: {0}")]
SerdeJson(#[from] serde_json::Error),
/// Integer parsing error.
#[cfg(feature = "serde")]
#[cfg_attr(feature = "std", error("Integer parsing error: {0}"))]
ParseInt(#[cfg_attr(feature = "std", from)] num::ParseIntError),
/// Hex string parsing error.
#[cfg(feature = "serde")]
#[cfg_attr(feature = "std", error("Hex parsing error: {0}"))]
Hex(#[cfg_attr(feature = "std", from)] hex::FromHexError),
/// Other errors.
#[cfg_attr(feature = "std", error("{0}"))]
Other(Cow<'static, str>),
}
#[cfg(feature = "serde")]
impl From<uint::FromDecStrErr> for Error {
fn from(err: uint::FromDecStrErr) -> Self {
use uint::FromDecStrErr::*;
match err {
InvalidCharacter => Self::Other(Cow::Borrowed("Uint parse error: InvalidCharacter")),
InvalidLength => Self::Other(Cow::Borrowed("Uint parse error: InvalidLength")),
}
}
}
-- file: ethabi/src/contract.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use alloc::collections::{btree_map::Values, BTreeMap};
#[cfg(feature = "serde")]
use core::fmt;
use core::iter::Flatten;
#[cfg(feature = "full-serde")]
use std::io;
#[cfg(feature = "serde")]
use serde::{
de::{SeqAccess, Visitor},
ser::SerializeSeq,
Deserialize, Deserializer, Serialize, Serializer,
};
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
#[cfg(feature = "serde")]
use crate::operation::Operation;
use crate::{error::Error as AbiError, errors, Constructor, Error, Event, Function};
/// API building calls to contracts ABI.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Contract {
/// Contract constructor.
pub constructor: Option<Constructor>,
/// Contract functions.
pub functions: BTreeMap<String, Vec<Function>>,
/// Contract events, maps signature to event.
pub events: BTreeMap<String, Vec<Event>>,
/// Contract errors, maps signature to error.
pub errors: BTreeMap<String, Vec<AbiError>>,
/// Contract has receive function.
pub receive: bool,
/// Contract has fallback function.
pub fallback: bool,
}
#[cfg(feature = "serde")]
impl<'a> Deserialize<'a> for Contract {
fn deserialize<D>(deserializer: D) -> Result<Contract, D::Error>
where
D: Deserializer<'a>,
{
deserializer.deserialize_any(ContractVisitor)
}
}
#[cfg(feature = "serde")]
struct ContractVisitor;
#[cfg(feature = "serde")]
impl<'a> Visitor<'a> for ContractVisitor {
type Value = Contract;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("valid abi spec file")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'a>,
{
let mut result = Contract::default();
while let Some(operation) = seq.next_element()? {
match operation {
Operation::Constructor(constructor) => {
result.constructor = Some(constructor);
}
Operation::Function(func) => {
result.functions.entry(func.name.clone()).or_default().push(func);
}
Operation::Event(event) => {
result.events.entry(event.name.clone()).or_default().push(event);
}
Operation::Error(error) => {
result.errors.entry(error.name.clone()).or_default().push(error);
}
Operation::Fallback => {
result.fallback = true;
}
Operation::Receive => {
result.receive = true;
}
}
}
Ok(result)
}
}
#[cfg(feature = "serde")]
impl Serialize for Contract {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// Serde's FlatMapSerializer is private, so we'll have to improvise...
#[derive(Serialize)]
#[serde(tag = "type")]
enum OperationRef<'a> {
#[serde(rename = "constructor")]
Constructor(&'a Constructor),
#[serde(rename = "function")]
Function(&'a Function),
#[serde(rename = "event")]
Event(&'a Event),
#[serde(rename = "error")]
Error(&'a AbiError),
#[serde(rename = "fallback")]
Fallback,
#[serde(rename = "receive")]
Receive,
}
let mut seq = serializer.serialize_seq(None)?;
if let Some(constructor) = &self.constructor {
seq.serialize_element(&OperationRef::Constructor(constructor))?;
}
for functions in self.functions.values() {
for function in functions {
seq.serialize_element(&OperationRef::Function(function))?;
}
}
for events in self.events.values() {
for event in events {
seq.serialize_element(&OperationRef::Event(event))?;
}
}
for errors in self.errors.values() {
for error in errors {
seq.serialize_element(&OperationRef::Error(error))?;
}
}
if self.receive {
seq.serialize_element(&OperationRef::Receive)?;
}
if self.fallback {
seq.serialize_element(&OperationRef::Fallback)?;
}
seq.end()
}
}
impl Contract {
/// Loads contract from json.
#[cfg(feature = "full-serde")]
pub fn load<T: io::Read>(reader: T) -> errors::Result<Self> {
serde_json::from_reader(reader).map_err(From::from)
}
/// Creates constructor call builder.
pub fn constructor(&self) -> Option<&Constructor> {
self.constructor.as_ref()
}
/// Get the function named `name`, the first if there are overloaded
/// versions of the same function.
pub fn function(&self, name: &str) -> errors::Result<&Function> {
self.functions.get(name).into_iter().flatten().next().ok_or_else(|| Error::InvalidName(name.to_owned()))
}
/// Get the contract event named `name`, the first if there are multiple.
pub fn event(&self, name: &str) -> errors::Result<&Event> {
self.events.get(name).into_iter().flatten().next().ok_or_else(|| Error::InvalidName(name.to_owned()))
}
/// Get the contract error named `name`, the first if there are multiple.
pub fn error(&self, name: &str) -> errors::Result<&AbiError> {
self.errors.get(name).into_iter().flatten().next().ok_or_else(|| Error::InvalidName(name.to_owned()))
}
/// Get all contract events named `name`.
pub fn events_by_name(&self, name: &str) -> errors::Result<&Vec<Event>> {
self.events.get(name).ok_or_else(|| Error::InvalidName(name.to_owned()))
}
/// Get all functions named `name`.
pub fn functions_by_name(&self, name: &str) -> errors::Result<&Vec<Function>> {
self.functions.get(name).ok_or_else(|| Error::InvalidName(name.to_owned()))
}
/// Get all errors named `name`.
pub fn errors_by_name(&self, name: &str) -> errors::Result<&Vec<AbiError>> {
self.errors.get(name).ok_or_else(|| Error::InvalidName(name.to_owned()))
}
/// Iterate over all functions of the contract in arbitrary order.
pub fn functions(&self) -> Functions {
Functions(self.functions.values().flatten())
}
/// Iterate over all events of the contract in arbitrary order.
pub fn events(&self) -> Events {
Events(self.events.values().flatten())
}
/// Iterate over all errors of the contract in arbitrary order.
pub fn errors(&self) -> AbiErrors {
AbiErrors(self.errors.values().flatten())
}
}
/// Contract functions iterator.
pub struct Functions<'a>(Flatten<Values<'a, String, Vec<Function>>>);
impl<'a> Iterator for Functions<'a> {
type Item = &'a Function;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
/// Contract events iterator.
pub struct Events<'a>(Flatten<Values<'a, String, Vec<Event>>>);
impl<'a> Iterator for Events<'a> {
type Item = &'a Event;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
/// Contract errors iterator.
pub struct AbiErrors<'a>(Flatten<Values<'a, String, Vec<AbiError>>>);
impl<'a> Iterator for AbiErrors<'a> {
type Item = &'a AbiError;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
#[cfg(all(test, feature = "serde"))]
#[allow(deprecated)]
mod test {
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use alloc::collections::BTreeMap;
use core::iter::FromIterator;
use crate::{tests::assert_ser_de, AbiError, Constructor, Contract, Event, EventParam, Function, Param, ParamType};
#[test]
fn empty() {
let json = "[]";
let deserialized: Contract = serde_json::from_str(json).unwrap();
assert_eq!(
deserialized,
Contract {
constructor: None,
functions: BTreeMap::new(),
events: BTreeMap::new(),
errors: BTreeMap::new(),
receive: false,
fallback: false,
}
);
assert_ser_de(&deserialized);
}
#[test]
fn constructor() {
let json = r#"
[
{
"type": "constructor",
"inputs": [
{
"name":"a",
"type":"address"
}
]
}
]
"#;
let deserialized: Contract = serde_json::from_str(json).unwrap();
assert_eq!(
deserialized,
Contract {
constructor: Some(Constructor {
inputs: vec![Param { name: "a".to_string(), kind: ParamType::Address, internal_type: None }]
}),
functions: BTreeMap::new(),
events: BTreeMap::new(),
errors: BTreeMap::new(),
receive: false,
fallback: false,
}
);
assert_ser_de(&deserialized);
}
#[test]
fn functions() {
let json = r#"
[
{
"type": "function",
"name": "foo",
"inputs": [
{
"name":"a",
"type":"address"
}
],
"outputs": [
{
"name": "res",
"type":"address"
}
]
},
{
"type": "function",
"name": "bar",
"inputs": [],
"outputs": []
}
]
"#;
let deserialized: Contract = serde_json::from_str(json).unwrap();
assert_eq!(
deserialized,
Contract {
constructor: None,
functions: BTreeMap::from_iter(vec![
(
"foo".to_string(),
vec![Function {
name: "foo".to_string(),
inputs: vec![Param {
name: "a".to_string(),
kind: ParamType::Address,
internal_type: None,
}],
outputs: vec![Param {
name: "res".to_string(),
kind: ParamType::Address,
internal_type: None,
}],
constant: None,
state_mutability: Default::default(),
}]
),
(
"bar".to_string(),
vec![Function {
name: "bar".to_string(),
inputs: vec![],
outputs: vec![],
constant: None,
state_mutability: Default::default(),
}]
),
]),
events: BTreeMap::new(),
errors: BTreeMap::new(),
receive: false,
fallback: false,
}
);
assert_ser_de(&deserialized);
}
#[test]
fn functions_overloads() {
let json = r#"
[
{
"type": "function",
"name": "foo",
"inputs": [
{
"name":"a",
"type":"address"
}
],
"outputs": [
{
"name": "res",
"type":"address"
}
]
},
{
"type": "function",
"name": "foo",
"inputs": [],
"outputs": []
}
]
"#;
let deserialized: Contract = serde_json::from_str(json).unwrap();
assert_eq!(
deserialized,
Contract {
constructor: None,
functions: BTreeMap::from_iter(vec![(
"foo".to_string(),
vec![
Function {
name: "foo".to_string(),
inputs: vec![Param {
name: "a".to_string(),
kind: ParamType::Address,
internal_type: None,
}],
outputs: vec![Param {
name: "res".to_string(),
kind: ParamType::Address,
internal_type: None,
}],
constant: None,
state_mutability: Default::default(),
},
Function {
name: "foo".to_string(),
inputs: vec![],
outputs: vec![],
constant: None,
state_mutability: Default::default(),
},
]
)]),
events: BTreeMap::new(),
errors: BTreeMap::new(),
receive: false,
fallback: false,
}
);
assert_ser_de(&deserialized);
}
#[test]
fn events() {
let json = r#"
[
{
"type": "event",
"name": "foo",
"inputs": [
{
"name":"a",
"type":"address"
}
],
"anonymous": false
},
{
"type": "event",
"name": "bar",
"inputs": [
{
"name":"a",
"type":"address",
"indexed": true
}
],
"anonymous": false
}
]
"#;
let deserialized: Contract = serde_json::from_str(json).unwrap();
assert_eq!(
deserialized,
Contract {
constructor: None,
functions: BTreeMap::new(),
events: BTreeMap::from_iter(vec![
(
"foo".to_string(),
vec![Event {
name: "foo".to_string(),
inputs: vec![EventParam {
name: "a".to_string(),
kind: ParamType::Address,
indexed: false,
}],
anonymous: false,
}]
),
(
"bar".to_string(),
vec![Event {
name: "bar".to_string(),
inputs: vec![EventParam { name: "a".to_string(), kind: ParamType::Address, indexed: true }],
anonymous: false,
}]
),
]),
errors: BTreeMap::new(),
receive: false,
fallback: false,
}
);
assert_ser_de(&deserialized);
}
#[test]
fn events_overload() {
let json = r#"
[
{
"type": "event",
"name": "foo",
"inputs": [
{
"name":"a",
"type":"address"
}
],
"anonymous": false
},
{
"type": "event",
"name": "foo",
"inputs": [
{
"name":"a",
"type":"address",
"indexed": true
}
],
"anonymous": false
}
]
"#;
let deserialized: Contract = serde_json::from_str(json).unwrap();
assert_eq!(
deserialized,
Contract {
constructor: None,
functions: BTreeMap::new(),
events: BTreeMap::from_iter(vec![(
"foo".to_string(),
vec![
Event {
name: "foo".to_string(),
inputs: vec![EventParam {
name: "a".to_string(),
kind: ParamType::Address,
indexed: false,
}],
anonymous: false,
},
Event {
name: "foo".to_string(),
inputs: vec![EventParam { name: "a".to_string(), kind: ParamType::Address, indexed: true }],
anonymous: false,
},
]
)]),
errors: BTreeMap::new(),
receive: false,
fallback: false,
}
);
assert_ser_de(&deserialized);
}
#[test]
fn errors() {
let json = r#"
[
{
"type": "error",
"inputs": [
{
"name": "available",
"type": "uint256"
},
{
"name": "required",
"type": "address"
}
],
"name": "foo"
},
{
"type": "error",
"inputs": [
{
"name": "a",
"type": "uint256"
},
{
"name": "b",
"type": "address"
}
],
"name": "bar"
}
]
"#;
let deserialized: Contract = serde_json::from_str(json).unwrap();
assert_eq!(
deserialized,
Contract {
constructor: None,
functions: BTreeMap::new(),
events: BTreeMap::new(),
errors: BTreeMap::from_iter(vec![
(
"foo".to_string(),
vec![AbiError {
name: "foo".to_string(),
inputs: vec![
Param {
name: "available".to_string(),
kind: ParamType::Uint(256),
internal_type: None,
},
Param { name: "required".to_string(), kind: ParamType::Address, internal_type: None }
],
}]
),
(
"bar".to_string(),
vec![AbiError {
name: "bar".to_string(),
inputs: vec![
Param { name: "a".to_string(), kind: ParamType::Uint(256), internal_type: None },
Param { name: "b".to_string(), kind: ParamType::Address, internal_type: None }
],
}]
),
]),
receive: false,
fallback: false,
}
);
assert_ser_de(&deserialized);
}
#[test]
fn errors_overload() {
let json = r#"
[
{
"type": "error",
"inputs": [
{
"name": "a",
"type": "uint256"
}
],
"name": "foo"
},
{
"type": "error",
"inputs": [
{
"name": "a",
"type": "uint256"
},
{
"name": "b",
"type": "address"
}
],
"name": "foo"
}
]
"#;
let deserialized: Contract = serde_json::from_str(json).unwrap();
assert_eq!(
deserialized,
Contract {
constructor: None,
functions: BTreeMap::new(),
events: BTreeMap::new(),
errors: BTreeMap::from_iter(vec![(
"foo".to_string(),
vec![
AbiError {
name: "foo".to_string(),
inputs: vec![Param {
name: "a".to_string(),
kind: ParamType::Uint(256),
internal_type: None,
}],
},
AbiError {
name: "foo".to_string(),
inputs: vec![
Param { name: "a".to_string(), kind: ParamType::Uint(256), internal_type: None },
Param { name: "b".to_string(), kind: ParamType::Address, internal_type: None }
],
},
]
),]),
receive: false,
fallback: false,
}
);
assert_ser_de(&deserialized);
}
#[test]
fn receive() {
let json = r#"
[
{ "type": "receive" }
]
"#;
let deserialized: Contract = serde_json::from_str(json).unwrap();
assert_eq!(
deserialized,
Contract {
constructor: None,
functions: BTreeMap::new(),
events: BTreeMap::new(),
errors: BTreeMap::new(),
receive: true,
fallback: false,
}
);
assert_ser_de(&deserialized);
}
#[test]
fn fallback() {
let json = r#"
[
{ "type": "fallback" }
]
"#;
let deserialized: Contract = serde_json::from_str(json).unwrap();
assert_eq!(
deserialized,
Contract {
constructor: None,
functions: BTreeMap::new(),
events: BTreeMap::new(),
errors: BTreeMap::new(),
receive: false,
fallback: true,
}
);
assert_ser_de(&deserialized);
}
}
-- file: ethabi/src/signature.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use sha3::{Digest, Keccak256};
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{
param_type::{ParamType, Writer},
Hash,
};
/// Returns the first four bytes of the Keccak-256 hash of the signature of the given params
pub fn short_signature(name: &str, params: &[ParamType]) -> [u8; 4] {
let mut result = [0u8; 4];
fill_signature(name, params, &mut result);
result
}
/// Returns the full Keccak-256 hash of the signature of the given params
pub fn long_signature(name: &str, params: &[ParamType]) -> Hash {
let mut result = [0u8; 32];
fill_signature(name, params, &mut result);
result.into()
}
fn fill_signature(name: &str, params: &[ParamType], result: &mut [u8]) {
let types = params.iter().map(Writer::write).collect::<Vec<String>>().join(",");
let data: Vec<u8> = From::from(format!("{name}({types})").as_str());
result.copy_from_slice(&Keccak256::digest(data)[..result.len()])
}
#[cfg(test)]
mod tests {
use super::short_signature;
use crate::ParamType;
use hex_literal::hex;
#[test]
fn test_signature() {
assert_eq!(hex!("cdcd77c0"), short_signature("baz", &[ParamType::Uint(32), ParamType::Bool]));
}
}
-- file: ethabi/src/tests.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[cfg(feature = "serde")]
use core::fmt::Debug;
use hex_literal::hex;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")]
use serde_json::Value;
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{decode, encode, ParamType, Token};
#[cfg(feature = "serde")]
pub(crate) fn assert_json_eq(left: &str, right: &str) {
let left: Value = serde_json::from_str(left).unwrap();
let right: Value = serde_json::from_str(right).unwrap();
assert_eq!(left, right);
}
#[cfg(feature = "serde")]
pub(crate) fn assert_ser_de<T>(canon: &T)
where
T: Serialize + for<'a> Deserialize<'a> + PartialEq + Debug,
{
let ser = serde_json::to_string(canon).unwrap();
let de = serde_json::from_str(&ser).unwrap();
assert_eq!(canon, &de);
}
macro_rules! test_encode_decode {
(name: $name:tt, types: $types:expr, tokens: $tokens:expr, data: $data:tt) => {
paste::item! {
#[test]
fn [<encode_ $name>]() {
let encoded = encode(&$tokens);
let expected = hex!($data).to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn [<decode_ $name>]() {
let encoded = hex!($data);
let expected = $tokens;
let decoded = decode(&$types, &encoded).unwrap();
assert_eq!(decoded, expected);
}
}
};
}
// test address
test_encode_decode! {
name: address,
types: [ParamType::Address],
tokens: [Token::Address([0x11u8; 20].into())],
data: "0000000000000000000000001111111111111111111111111111111111111111"
}
test_encode_decode! {
name: addresses,
types: [
ParamType::Address,
ParamType::Address
],
tokens: [
Token::Address([0x11u8; 20].into()),
Token::Address([0x22u8; 20].into())
],
data: "
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222"
}
// test bytes
test_encode_decode! {
name: bytes,
types: [ParamType::Bytes],
tokens: [Token::Bytes(vec![0x12, 0x34])],
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
1234000000000000000000000000000000000000000000000000000000000000"
}
test_encode_decode! {
name: bytes2,
types: [ParamType::Bytes],
tokens: [Token::Bytes(hex!("10000000000000000000000000000000000000000000000000000000000002").to_vec())],
data: "
0000000000000000000000000000000000000000000000000000000000000020
000000000000000000000000000000000000000000000000000000000000001f
1000000000000000000000000000000000000000000000000000000000000200"
}
test_encode_decode! {
name: bytes3,
types: [ParamType::Bytes],
tokens: [
Token::Bytes(hex!("
1000000000000000000000000000000000000000000000000000000000000000
1000000000000000000000000000000000000000000000000000000000000000
").to_vec())
],
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000040
1000000000000000000000000000000000000000000000000000000000000000
1000000000000000000000000000000000000000000000000000000000000000"
}
test_encode_decode! {
name: two_bytes,
types: [ParamType::Bytes, ParamType::Bytes],
tokens: [
Token::Bytes(hex!("10000000000000000000000000000000000000000000000000000000000002").to_vec()),
Token::Bytes(hex!("0010000000000000000000000000000000000000000000000000000000000002").to_vec())
],
data: "
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
000000000000000000000000000000000000000000000000000000000000001f
1000000000000000000000000000000000000000000000000000000000000200
0000000000000000000000000000000000000000000000000000000000000020
0010000000000000000000000000000000000000000000000000000000000002"
}
// test int
test_encode_decode! {
name: int,
types: [ParamType::Int(32)],
tokens: [Token::Int([0x11u8; 32].into())],
data: "1111111111111111111111111111111111111111111111111111111111111111"
}
test_encode_decode! {
name: int2,
types: [ParamType::Int(32)],
tokens: {
let mut int = [0u8; 32];
int[31] = 4;
[Token::Int(int.into())]
},
data: "0000000000000000000000000000000000000000000000000000000000000004"
}
// test uint
test_encode_decode! {
name: uint,
types: [ParamType::Uint(32)],
tokens: [Token::Uint([0x11u8; 32].into())],
data: "1111111111111111111111111111111111111111111111111111111111111111"
}
test_encode_decode! {
name: uint2,
types: [ParamType::Uint(32)],
tokens: {
let mut uint = [0u8; 32];
uint[31] = 4;
[Token::Uint(uint.into())]
},
data: "0000000000000000000000000000000000000000000000000000000000000004"
}
// test bool
test_encode_decode! {
name: bool,
types: [ParamType::Bool],
tokens: [Token::Bool(true)],
data: "0000000000000000000000000000000000000000000000000000000000000001"
}
test_encode_decode! {
name: bool2,
types: [ParamType::Bool],
tokens: [Token::Bool(false)],
data: "0000000000000000000000000000000000000000000000000000000000000000"
}
// test string
test_encode_decode! {
name: string,
types: [ParamType::String],
tokens: [Token::String("gavofyork".to_owned())],
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000009
6761766f66796f726b0000000000000000000000000000000000000000000000"
}
// test array
test_encode_decode! {
name: dynamic_array_of_addresses,
types: [ParamType::Array(Box::new(ParamType::Address))],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
[Token::Array(vec![address1, address2])]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222"
}
test_encode_decode! {
name: dynamic_array_of_fixed_arrays_of_addresses,
types: [
ParamType::Array(Box::new(
ParamType::FixedArray(Box::new(ParamType::Address), 2)
))
],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let array0 = Token::FixedArray(vec![address1, address2]);
let array1 = Token::FixedArray(vec![address3, address4]);
[Token::Array(vec![array0, array1])]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444"
}
test_encode_decode! {
name: dynamic_array_of_fixed_arrays_of_dynamic_array,
types: [
ParamType::Array(Box::new(
ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Address))), 2)
))
],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let address5 = Token::Address([0x55u8; 20].into());
let address6 = Token::Address([0x66u8; 20].into());
let address7 = Token::Address([0x77u8; 20].into());
let address8 = Token::Address([0x88u8; 20].into());
let array0 = Token::FixedArray(vec![
Token::Array(vec![address1, address2]),
Token::Array(vec![address3, address4]),
]);
let array1 = Token::FixedArray(vec![
Token::Array(vec![address5, address6]),
Token::Array(vec![address7, address8]),
]);
[Token::Array(vec![array0, array1])]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000140
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000005555555555555555555555555555555555555555
0000000000000000000000006666666666666666666666666666666666666666
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000007777777777777777777777777777777777777777
0000000000000000000000008888888888888888888888888888888888888888"
// outer array:
// 0: 0000000000000000000000000000000000000000000000000000000000000020
// 32: 0000000000000000000000000000000000000000000000000000000000000002 len outer => 2
// 64: 0000000000000000000000000000000000000000000000000000000000000040 tail of outer => offset of array0
// 96: 0000000000000000000000000000000000000000000000000000000000000140
// array0:
// 128: 0000000000000000000000000000000000000000000000000000000000000040 tail offset of array0 => offset of array0[0]
// 160: 00000000000000000000000000000000000000000000000000000000000000a0 offset of array0[1] => 160
// array0[0]:
// 192: 0000000000000000000000000000000000000000000000000000000000000002 len of dynamic array array0[0] => 2
// 224: 0000000000000000000000001111111111111111111111111111111111111111 array0[0][0] = address1
// 256: 0000000000000000000000002222222222222222222222222222222222222222 array0[0][1] = address2
// array0[1]:
// 288: 0000000000000000000000000000000000000000000000000000000000000002 len of dynamic array0[1][0]
// 320: 0000000000000000000000003333333333333333333333333333333333333333 array0[1][0] = address3
// 352: 0000000000000000000000004444444444444444444444444444444444444444 array0[1][1] = address4
// 384: 0000000000000000000000000000000000000000000000000000000000000040
// 416: 00000000000000000000000000000000000000000000000000000000000000a0
// 448: 0000000000000000000000000000000000000000000000000000000000000002
// 480: 0000000000000000000000005555555555555555555555555555555555555555
// 512: 0000000000000000000000006666666666666666666666666666666666666666
// 544: 0000000000000000000000000000000000000000000000000000000000000002
// 576: 0000000000000000000000007777777777777777777777777777777777777777
// 608: 0000000000000000000000008888888888888888888888888888888888888888"
}
test_encode_decode! {
name: dynamic_array_of_dynamic_arrays,
types: [
ParamType::Array(Box::new(
ParamType::Array(Box::new(ParamType::Address))
))
],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let array0 = Token::Array(vec![address1]);
let array1 = Token::Array(vec![address2]);
let dynamic = Token::Array(vec![array0, array1]);
[dynamic]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000002222222222222222222222222222222222222222"
// Encoding explanation:
// line 1 at 0x00 = 0: tail offset of dynamic array (0x20 = 32 => line 2)
// line 2 at 0x20 = 32: length of dynamic array (0x2 = 2)
// line 3 at 0x40 = 64: offset of array0 (0x80 = 128 = 5 * 32 => line 5)
// line 4 at 0x60 = 96: offset of array1 (0xc0 = 192 = 7 * 32 => line 7)
// line 5 at 0x80 = 128: length of array0 (0x1 = 1)
// line 6 at 0xa0 = 160: value array0[0] (0x1111111111111111111111111111111111111111)
// line 7 at 0xc0 = 192: length of array1 (0x1 = 1)
// line 8 at 0xe0 = 224: value array1[0] (0x2222222222222222222222222222222222222222)
}
test_encode_decode! {
name: dynamic_array_of_dynamic_arrays2,
types: [
ParamType::Array(Box::new(
ParamType::Array(Box::new(ParamType::Address))
))
],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let array0 = Token::Array(vec![address1, address2]);
let array1 = Token::Array(vec![address3, address4]);
let dynamic = Token::Array(vec![array0, array1]);
[dynamic]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444"
}
test_encode_decode! {
name: dynamic_array_of_bool,
types: [ParamType::Array(Box::new(ParamType::Bool))],
tokens: {
[Token::Array(vec![Token::Bool(true), Token::Bool(false)])]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000000
"
}
test_encode_decode! {
name: dynamic_array_of_bytes,
types: [ParamType::Array(Box::new(ParamType::Bytes))],
tokens: {
let bytes = hex!("019c80031b20d5e69c8093a571162299032018d913930d93ab320ae5ea44a4218a274f00d607").to_vec();
[Token::Array(vec![Token::Bytes(bytes)])]
},
// line 1 at 0x00 = 0: tail offset of array
// line 2 at 0x20 = 32: length of array
// line 3 at 0x40 = 64: offset of array[0] (bytes)
// line 4 at 0x60 = 96: length of array[0] (bytes)
// line 5 at 0x80 = 128: first word of bytes
// line 6 at 0xa0 = 160: first word of bytes
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000026
019c80031b20d5e69c8093a571162299032018d913930d93ab320ae5ea44a421
8a274f00d6070000000000000000000000000000000000000000000000000000"
}
test_encode_decode! {
name: dynamic_array_of_bytes2,
types: [ParamType::Array(Box::new(ParamType::Bytes))],
tokens: [
Token::Array(vec![
Token::Bytes(hex!("4444444444444444444444444444444444444444444444444444444444444444444444444444").to_vec()),
Token::Bytes(hex!("6666666666666666666666666666666666666666666666666666666666666666666666666666").to_vec()),
])
],
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000026
4444444444444444444444444444444444444444444444444444444444444444
4444444444440000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000026
6666666666666666666666666666666666666666666666666666666666666666
6666666666660000000000000000000000000000000000000000000000000000"
}
test_encode_decode! {
name: empty_dynamic_array,
types: [
ParamType::Array(Box::new(ParamType::Bool)),
ParamType::Array(Box::new(ParamType::Bool)),
],
tokens: [
Token::Array(vec![]),
Token::Array(vec![])
],
data: "
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000"
}
test_encode_decode! {
name: dynamic_array_of_empty_dynamic_array,
types: [
ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Bool)))),
ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Bool)))),
],
tokens: [
Token::Array(vec![Token::Array(vec![])]),
Token::Array(vec![Token::Array(vec![])]),
],
data: "
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000000"
}
// test fixed array
test_encode_decode! {
name: fixed_array_of_addresses,
types: [ParamType::FixedArray(Box::new(ParamType::Address), 2)],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
[Token::FixedArray(vec![address1, address2])]
},
data: "
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222"
}
test_encode_decode! {
name: fixed_array_of_strings,
types: [ParamType::FixedArray(Box::new(ParamType::String), 2)],
tokens: {
let s1 = Token::String("foo".into());
let s2 = Token::String("bar".into());
[Token::FixedArray(vec![s1, s2])]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000003
666f6f0000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000003
6261720000000000000000000000000000000000000000000000000000000000"
// `data` explained:
// line 1 at 0x00 = 0: tail offset for the array
// line 2 at 0x20 = 32: offset of string 1
// line 3 at 0x40 = 64: offset of string 2
// line 4 at 0x60 = 96: length of string 1
// line 5 at 0x80 = 128: value of string 1
// line 6 at 0xa0 = 160: length of string 2
// line 7 at 0xc0 = 192: value of string 2
}
test_encode_decode! {
name: fixed_array_of_fixed_arrays,
types: [
ParamType::FixedArray(
Box::new(ParamType::FixedArray(Box::new(ParamType::Address), 2)),
2
)
],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let array0 = Token::FixedArray(vec![address1, address2]);
let array1 = Token::FixedArray(vec![address3, address4]);
let fixed = Token::FixedArray(vec![array0, array1]);
[fixed]
},
data: "
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444"
}
test_encode_decode! {
name: fixed_array_of_dynamic_array_of_addresses,
types: [
ParamType::FixedArray(
Box::new(ParamType::Array(Box::new(ParamType::Address))),
2
)
],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let array0 = Token::Array(vec![address1, address2]);
let array1 = Token::Array(vec![address3, address4]);
[Token::FixedArray(vec![array0, array1])]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444"
}
// test fixed bytes
test_encode_decode! {
name: fixed_bytes,
types: [ParamType::FixedBytes(2)],
tokens: [Token::FixedBytes(vec![0x12, 0x34])],
data: "1234000000000000000000000000000000000000000000000000000000000000"
}
// test tuple with tuple array member
test_encode_decode! {
name: tuple_with_tuple_array_test,
types: [
ParamType::Tuple(vec![
ParamType::Array(Box::new(ParamType::Tuple(
vec![
ParamType::Address,
ParamType::Uint(256)
]
)))
])
],
tokens: {
[
Token::Tuple(
vec![
Token::Array(vec![
Token::Tuple(vec![
Token::Address([0x11u8; 20].into()),
Token::Uint([0x11u8; 32].into()),
]),
Token::Tuple(vec![
Token::Address([0x22u8; 20].into()),
Token::Uint([0x22u8; 32].into()),
]),
Token::Tuple(vec![
Token::Address([0x33u8; 20].into()),
Token::Uint([0x44u8; 32].into()),
])
])
]
)
]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000001111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
2222222222222222222222222222222222222222222222222222222222222222
0000000000000000000000003333333333333333333333333333333333333333
4444444444444444444444444444444444444444444444444444444444444444
"
}
// comprehensive test
test_encode_decode! {
name: comprehensive_test,
types: [
ParamType::Int(32),
ParamType::Bytes,
ParamType::Int(32),
ParamType::Bytes,
],
tokens: {
let bytes = hex!("
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
").to_vec();
[
Token::Int(5.into()),
Token::Bytes(bytes.clone()),
Token::Int(3.into()),
Token::Bytes(bytes),
]
},
data: "
0000000000000000000000000000000000000000000000000000000000000005
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000003
00000000000000000000000000000000000000000000000000000000000000e0
0000000000000000000000000000000000000000000000000000000000000040
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
0000000000000000000000000000000000000000000000000000000000000040
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b"
}
test_encode_decode! {
name: comprehensive_test2,
types: [
ParamType::Int(32),
ParamType::String,
ParamType::Int(32),
ParamType::Int(32),
ParamType::Int(32),
ParamType::Array(Box::new(ParamType::Int(32))),
],
tokens: [
Token::Int(1.into()),
Token::String("gavofyork".to_owned()),
Token::Int(2.into()),
Token::Int(3.into()),
Token::Int(4.into()),
Token::Array(vec![
Token::Int(5.into()),
Token::Int(6.into()),
Token::Int(7.into()),
])
],
data: "
0000000000000000000000000000000000000000000000000000000000000001
00000000000000000000000000000000000000000000000000000000000000c0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000000000000000000000000000000000000000000004
0000000000000000000000000000000000000000000000000000000000000100
0000000000000000000000000000000000000000000000000000000000000009
6761766f66796f726b0000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000000000000000000000000000000000000000000005
0000000000000000000000000000000000000000000000000000000000000006
0000000000000000000000000000000000000000000000000000000000000007"
}
-- file: ethabi/src/filter.rs --
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use core::ops;
#[cfg(feature = "serde")]
use serde::{Serialize, Serializer};
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{Hash, Token};
/// Raw topic filter.
#[derive(Debug, PartialEq, Default)]
pub struct RawTopicFilter {
/// Topic.
pub topic0: Topic<Token>,
/// Topic.
pub topic1: Topic<Token>,
/// Topic.
pub topic2: Topic<Token>,
}
/// Topic filter.
#[derive(Debug, PartialEq, Eq, Default)]
pub struct TopicFilter {
/// Usually (for not-anonymous transactions) the first topic is event signature.
pub topic0: Topic<Hash>,
/// Second topic.
pub topic1: Topic<Hash>,
/// Third topic.
pub topic2: Topic<Hash>,
/// Fourth topic.
pub topic3: Topic<Hash>,
}
#[cfg(feature = "serde")]
impl Serialize for TopicFilter {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
vec![&self.topic0, &self.topic1, &self.topic2, &self.topic3].serialize(serializer)
}
}
/// Acceptable topic possibilities.
#[derive(Debug, PartialEq, Eq)]
pub enum Topic<T> {
/// Match any.
Any,
/// Match any of the hashes.
OneOf(Vec<T>),
/// Match only this hash.
This(T),
}
impl<T> Topic<T> {
/// Map
pub fn map<F, O>(self, f: F) -> Topic<O>
where
F: Fn(T) -> O,
{
match self {
Topic::Any => Topic::Any,
Topic::OneOf(topics) => Topic::OneOf(topics.into_iter().map(f).collect()),
Topic::This(topic) => Topic::This(f(topic)),
}
}
/// Returns true if topic is empty (Topic::Any)
pub fn is_any(&self) -> bool {
match *self {
Topic::Any => true,
Topic::This(_) | Topic::OneOf(_) => false,
}
}
}
impl<T> Default for Topic<T> {
fn default() -> Self {
Topic::Any
}
}
impl<T> From<Option<T>> for Topic<T> {
fn from(o: Option<T>) -> Self {
match o {
Some(topic) => Topic::This(topic),
None => Topic::Any,
}
}
}
impl<T> From<T> for Topic<T> {
fn from(topic: T) -> Self {
Topic::This(topic)
}
}
impl<T> From<Vec<T>> for Topic<T> {
fn from(topics: Vec<T>) -> Self {
Topic::OneOf(topics)
}
}
impl<T> From<Topic<T>> for Vec<T> {
fn from(topic: Topic<T>) -> Self {
match topic {
Topic::Any => vec![],
Topic::This(topic) => vec![topic],
Topic::OneOf(topics) => topics,
}
}
}
#[cfg(feature = "serde")]
impl Serialize for Topic<Hash> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
Topic::Any => Option::<()>::None.serialize(serializer),
Topic::OneOf(ref vec) => vec.serialize(serializer),
Topic::This(ref hash) => hash.serialize(serializer),
}
}
}
impl<T> ops::Index<usize> for Topic<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
match *self {
Topic::Any => panic!("Topic unavailable"),
Topic::This(ref topic) => {
if index != 0 {
panic!("Topic unavailable");
}
topic
}
Topic::OneOf(ref topics) => topics.index(index),
}
}
}
#[cfg(test)]
mod tests {
use super::Topic;
#[cfg(feature = "serde")]
use super::TopicFilter;
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
#[cfg(feature = "serde")]
use crate::Hash;
#[cfg(feature = "serde")]
fn hash(s: &'static str) -> Hash {
s.parse().unwrap()
}
#[cfg(feature = "serde")]
#[test]
fn test_topic_filter_serialization() {
let expected = r#"["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b",null,["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b","0x0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc"],null]"#;
let topic = TopicFilter {
topic0: Topic::This(hash("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b")),
topic1: Topic::Any,
topic2: Topic::OneOf(vec![
hash("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"),
hash("0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc"),
]),
topic3: Topic::Any,
};
let topic_str = serde_json::to_string(&topic).unwrap();
assert_eq!(expected, &topic_str);
}
#[test]
fn test_topic_from() {
assert_eq!(Topic::Any as Topic<u64>, None.into());
assert_eq!(Topic::This(10u64), 10u64.into());
assert_eq!(Topic::OneOf(vec![10u64, 20]), vec![10u64, 20].into());
}
#[test]
fn test_topic_into_vec() {
let expected: Vec<u64> = vec![];
let is: Vec<u64> = (Topic::Any as Topic<u64>).into();
assert_eq!(expected, is);
let expected: Vec<u64> = vec![10];
let is: Vec<u64> = Topic::This(10u64).into();
assert_eq!(expected, is);
let expected: Vec<u64> = vec![10, 20];
let is: Vec<u64> = Topic::OneOf(vec![10u64, 20]).into();
assert_eq!(expected, is);
}
#[test]
fn test_topic_is_any() {
assert!((Topic::Any as Topic<u8>).is_any());
assert!(!Topic::OneOf(vec![10u64, 20]).is_any());
assert!(!Topic::This(10u64).is_any());
}
#[test]
fn test_topic_index() {
assert_eq!(Topic::OneOf(vec![10u64, 20])[0], 10);
assert_eq!(Topic::OneOf(vec![10u64, 20])[1], 20);
assert_eq!(Topic::This(10u64)[0], 10);
}
#[test]
#[should_panic(expected = "Topic unavailable")]
fn test_topic_index_panic() {
let _ = (Topic::Any as Topic<u8>)[0];
}
#[test]
#[should_panic(expected = "Topic unavailable")]
fn test_topic_index_panic2() {
assert_eq!(Topic::This(10u64)[1], 10);
}
}
-- file: ethabi/src/tuple_param.rs --
// Copyright 2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Tuple param type.
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{param_type::Writer, ParamType};
use core::fmt;
use serde::{
de::{Error, MapAccess, Visitor},
ser::SerializeMap,
Deserialize, Deserializer, Serialize, Serializer,
};
/// Tuple params specification
#[derive(Debug, Clone, PartialEq)]
pub struct TupleParam {
/// Param name.
pub name: Option<String>,
/// Param type.
pub kind: ParamType,
/// Additional Internal type.
pub internal_type: Option<String>,
}
impl<'a> Deserialize<'a> for TupleParam {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
deserializer.deserialize_any(TupleParamVisitor)
}
}
struct TupleParamVisitor;
impl<'a> Visitor<'a> for TupleParamVisitor {
type Value = TupleParam;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a valid tuple parameter spec")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'a>,
{
let mut name = None;
let mut kind = None;
let mut components = None;
let mut internal_type = None;
while let Some(ref key) = map.next_key::<String>()? {
match key.as_ref() {
"name" => {
if name.is_some() {
return Err(Error::duplicate_field("name"));
}
name = Some(map.next_value()?);
}
"type" => {
if kind.is_some() {
return Err(Error::duplicate_field("type"));
}
kind = Some(map.next_value()?);
}
"internalType" => {
if internal_type.is_some() {
return Err(Error::duplicate_field("internalType"));
}
internal_type = Some(map.next_value()?);
}
"components" => {
if components.is_some() {
return Err(Error::duplicate_field("components"));
}
let component: Vec<TupleParam> = map.next_value()?;
components = Some(component)
}
_ => {}
}
}
let mut kind = kind.ok_or_else(|| Error::missing_field("kind"))?;
crate::param::set_tuple_components(&mut kind, components)?;
Ok(TupleParam { name, kind, internal_type })
}
}
impl Serialize for TupleParam {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(None)?;
if let Some(ref internal_type) = self.internal_type {
map.serialize_entry("internalType", internal_type)?;
}
if let Some(name) = &self.name {
map.serialize_entry("name", name)?;
}
map.serialize_entry("type", &Writer::write_for_abi(&self.kind, false))?;
if let Some(inner_tuple) = crate::param::inner_tuple(&self.kind) {
map.serialize_key("components")?;
map.serialize_value(&crate::param::SerializeableParamVec(inner_tuple))?;
}
map.end()
}
}
#[cfg(test)]
mod tests {
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{
tests::{assert_json_eq, assert_ser_de},
ParamType, TupleParam,
};
#[test]
fn param_simple() {
let s = r#"{
"name": "foo",
"type": "address"
}"#;
let deserialized: TupleParam = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
TupleParam { name: Some("foo".to_owned()), kind: ParamType::Address, internal_type: None }
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn param_internal_type() {
let s = r#"{
"internalType": "struct Verifier.Proof",
"name": "foo",
"type": "address"
}"#;
let deserialized: TupleParam = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
TupleParam {
name: Some("foo".to_owned()),
kind: ParamType::Address,
internal_type: Some("struct Verifier.Proof".to_string())
}
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn param_unnamed() {
let s = r#"{
"type": "address"
}"#;
let deserialized: TupleParam = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, TupleParam { name: None, kind: ParamType::Address, internal_type: None });
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn param_tuple() {
let s = r#"{
"type": "tuple",
"components": [
{
"type": "uint48"
},
{
"type": "tuple",
"components": [
{
"type": "address"
}
]
}
]
}"#;
let deserialized: TupleParam = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
TupleParam {
name: None,
kind: ParamType::Tuple(vec![ParamType::Uint(48), ParamType::Tuple(vec![ParamType::Address])]),
internal_type: None
}
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn param_tuple_named() {
let s = r#"{
"type": "tuple",
"components": [
{
"name": "amount",
"type": "uint48"
},
{
"name": "things",
"type": "tuple",
"components": [
{
"name": "baseTupleParam",
"type": "address"
}
]
}
]
}"#;
let deserialized: TupleParam = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
TupleParam {
name: None,
kind: ParamType::Tuple(vec![ParamType::Uint(48), ParamType::Tuple(vec![ParamType::Address])]),
internal_type: None
}
);
assert_ser_de(&deserialized);
}
#[test]
fn param_tuple_array() {
let s = r#"{
"type": "tuple[]",
"components": [
{
"type": "uint48"
},
{
"type": "address"
},
{
"type": "address"
}
]
}"#;
let deserialized: TupleParam = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
TupleParam {
name: None,
kind: ParamType::Array(Box::new(ParamType::Tuple(vec![
ParamType::Uint(48),
ParamType::Address,
ParamType::Address
]))),
internal_type: None
}
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn param_array_of_array_of_tuple() {
let s = r#"{
"type": "tuple[][]",
"components": [
{
"type": "uint8"
},
{
"type": "uint16"
}
]
}"#;
let deserialized: TupleParam = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
TupleParam {
name: None,
kind: ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Tuple(vec![
ParamType::Uint(8),
ParamType::Uint(16),
]))))),
internal_type: None
}
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn param_tuple_fixed_array() {
let s = r#"{
"type": "tuple[2]",
"components": [
{
"type": "uint48"
},
{
"type": "address"
},
{
"type": "address"
}
]
}"#;
let deserialized: TupleParam = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
TupleParam {
name: None,
kind: ParamType::FixedArray(
Box::new(ParamType::Tuple(vec![ParamType::Uint(48), ParamType::Address, ParamType::Address])),
2
),
internal_type: None
}
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
#[test]
fn param_tuple_with_nested_tuple_arrays() {
let s = r#"{
"type": "tuple",
"components": [
{
"type": "tuple[]",
"components": [
{
"type": "address"
}
]
},
{
"type": "tuple[42]",
"components": [
{
"type": "address"
}
]
}
]
}"#;
let deserialized: TupleParam = serde_json::from_str(s).unwrap();
assert_eq!(
deserialized,
TupleParam {
name: None,
kind: ParamType::Tuple(vec![
ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Address]))),
ParamType::FixedArray(Box::new(ParamType::Tuple(vec![ParamType::Address])), 42,)
]),
internal_type: None
}
);
assert_json_eq(s, serde_json::to_string(&deserialized).unwrap().as_str());
}
}
Last active
May 1, 2023 06:36
-
-
Save bernardoaraujor/96c80ba1ed810a4212488bd11f05bce8 to your computer and use it in GitHub Desktop.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment