From 73e66ae194f869363857802f673d559aff1f42d0 Mon Sep 17 00:00:00 2001 From: Felipe Rosa Date: Fri, 11 Mar 2022 16:04:32 -0300 Subject: [PATCH] Implement Serialize for Abi --- src/abi.rs | 114 ++++++++++++++++++++++++++--------- src/params.rs | 161 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 221 insertions(+), 54 deletions(-) diff --git a/src/abi.rs b/src/abi.rs index 1243ebe..be71aba 100644 --- a/src/abi.rs +++ b/src/abi.rs @@ -1,6 +1,6 @@ -use anyhow::{anyhow, Error, Result}; +use anyhow::{anyhow, Result}; use ethereum_types::H256; -use serde::{de::Visitor, Deserialize}; +use serde::{de::Visitor, Deserialize, Serialize}; use crate::{params::Param, DecodedParams, Event, Value}; @@ -9,7 +9,6 @@ use crate::{params::Param, DecodedParams, Event, Value}; /// This struct holds defitions for a contracts' ABI. /// /// ```no_run -/// use std::str::FromStr; /// use ethereum_abi::Abi; /// /// let abi_json = r#"[{ @@ -18,7 +17,7 @@ use crate::{params::Param, DecodedParams, Event, Value}; /// "inputs": [{"type": "uint256", "name": "x"}]} /// ]"#; /// -/// let abi = Abi::from_str(abi_json).unwrap(); +/// let abi: Abi = serde_json::from_str(abi_json).unwrap(); /// ``` #[derive(Debug, Clone, Eq, PartialEq)] pub struct Abi { @@ -34,16 +33,6 @@ pub struct Abi { pub has_fallback: bool, } -impl Abi { - /// Parses a JSON ABI definition from a reader (e.g. a file handle). - pub fn from_reader(rdr: R) -> Result - where - R: std::io::Read, - { - Ok(serde_json::from_reader(rdr)?) - } -} - impl Abi { // Decode function input from slice. pub fn decode_input_from_slice<'a>( @@ -93,12 +82,69 @@ impl Abi { } } -impl std::str::FromStr for Abi { - type Err = Error; +impl Serialize for Abi { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut entries = vec![]; + + if let Some(c) = &self.constructor { + entries.push(AbiEntry { + type_: String::from("constructor"), + name: None, + inputs: Some(c.inputs.clone()), + outputs: None, + state_mutability: Some(StateMutability::NonPayable), + anonymous: None, + }); + } + + for f in &self.functions { + entries.push(AbiEntry { + type_: String::from("function"), + name: Some(f.name.clone()), + inputs: Some(f.inputs.clone()), + outputs: Some(f.outputs.clone()), + state_mutability: Some(f.state_mutability), + anonymous: None, + }); + } + + for e in &self.events { + entries.push(AbiEntry { + type_: String::from("event"), + name: Some(e.name.clone()), + inputs: Some(e.inputs.clone()), + outputs: None, + state_mutability: None, + anonymous: Some(e.anonymous), + }); + } + + if self.has_receive { + entries.push(AbiEntry { + type_: String::from("receive"), + name: None, + inputs: None, + outputs: None, + state_mutability: Some(StateMutability::Payable), + anonymous: None, + }); + } + + if self.has_fallback { + entries.push(AbiEntry { + type_: String::from("fallback"), + name: None, + inputs: None, + outputs: None, + state_mutability: Some(StateMutability::Payable), + anonymous: None, + }); + } - /// Parses a JSON ABI definition from a string. - fn from_str(s: &str) -> Result { - Ok(serde_json::from_str(s)?) + entries.serialize(serializer) } } @@ -181,7 +227,7 @@ impl Function { } /// Available state mutability values for functions and constructors. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Deserialize)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum StateMutability { /// Specified to not read the blockchain state. @@ -194,15 +240,20 @@ pub enum StateMutability { Payable, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] struct AbiEntry { #[serde(rename = "type")] type_: String, + #[serde(skip_serializing_if = "Option::is_none")] name: Option, + #[serde(skip_serializing_if = "Option::is_none")] inputs: Option>, + #[serde(skip_serializing_if = "Option::is_none")] outputs: Option>, + #[serde(skip_serializing_if = "Option::is_none")] state_mutability: Option, + #[serde(skip_serializing_if = "Option::is_none")] anonymous: Option, } @@ -308,15 +359,15 @@ impl<'de> Visitor<'de> for AbiVisitor { #[cfg(test)] mod test { - use pretty_assertions::assert_eq; - use std::str::FromStr; - use ethereum_types::{H160, U256}; + use pretty_assertions::assert_eq; use crate::types::Type; use super::*; + const TEST_ABI_V1: &str = r#"[{"inputs":[{"internalType":"address","name":"a","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"x","type":"address"},{"indexed":false,"internalType":"uint256","name":"y","type":"uint256"}],"name":"E","type":"event"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"f","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]"#; + fn test_function() -> Function { Function { name: "funname".to_string(), @@ -393,8 +444,7 @@ mod test { #[test] fn works_v1() { - let s = r#"[{"inputs":[{"internalType":"address","name":"a","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"x","type":"address"},{"indexed":false,"internalType":"uint256","name":"y","type":"uint256"}],"name":"E","type":"event"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"f","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]"#; - let abi = Abi::from_str(s).unwrap(); + let abi: Abi = serde_json::from_str(TEST_ABI_V1).unwrap(); assert_eq!( abi, @@ -478,7 +528,7 @@ mod test { } ]); - let abi = Abi::from_str(&v.to_string()).unwrap(); + let abi: Abi = serde_json::from_str(&v.to_string()).unwrap(); assert_eq!( abi, @@ -510,4 +560,14 @@ mod test { } ); } + + #[test] + fn test_serde() { + let abi: Abi = serde_json::from_str(TEST_ABI_V1).unwrap(); + + let ser_abi = serde_json::to_string(&abi).expect("serialized abi"); + let de_abi: Abi = serde_json::from_str(&ser_abi).expect("deserialized abi"); + + assert_eq!(abi, de_abi); + } } diff --git a/src/params.rs b/src/params.rs index 2d7fc03..afe5d52 100644 --- a/src/params.rs +++ b/src/params.rs @@ -1,4 +1,4 @@ -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use std::{collections::HashMap, rc::Rc}; use crate::{types::Type, Value}; @@ -80,6 +80,52 @@ pub struct Param { pub indexed: Option, } +impl Param { + fn build_param_entry(&self) -> ParamEntry { + let tuple_params = match &self.type_ { + Type::Tuple(params) => Some(params.clone()), + Type::Array(ty) | Type::FixedArray(ty, _) => { + if let Type::Tuple(params) = ty.as_ref() { + Some(params.clone()) + } else { + None + } + } + _ => None, + }; + + let components = tuple_params.map(|params| { + params + .iter() + .map(|(name, ty)| { + Param { + name: name.clone(), + type_: ty.clone(), + indexed: None, + } + .build_param_entry() + }) + .collect() + }); + + ParamEntry { + name: self.name.clone(), + type_: param_type_string(&self.type_), + indexed: self.indexed, + components, + } + } +} + +impl Serialize for Param { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.build_param_entry().serialize(serializer) + } +} + impl<'a> Deserialize<'a> for Param { fn deserialize(deserializer: D) -> Result where @@ -98,12 +144,23 @@ impl<'a> Deserialize<'a> for Param { } } -#[derive(Debug, Clone, Deserialize)] +fn param_type_string(ty: &Type) -> String { + match ty { + Type::Tuple(_) => String::from("tuple"), + Type::Array(ty) => format!("{}[]", param_type_string(ty)), + Type::FixedArray(ty, size) => format!("{}[{}]", param_type_string(ty), size), + _ => format!("{}", ty), + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] struct ParamEntry { pub name: String, #[serde(rename = "type")] pub type_: String, + #[serde(skip_serializing_if = "Option::is_none")] pub indexed: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub components: Option>, } @@ -290,19 +347,20 @@ fn check_fixed_bytes_size(i: &usize) -> bool { #[cfg(test)] mod test { - use serde_json::json; - use super::*; + use pretty_assertions::assert_eq; + use serde_json::json; + #[test] - fn deserialize_uint() { + fn serde_uint() { for i in (8..=256).step_by(8) { let v = json!({ "name": "a", "type": format!("uint{}", i), }); - let param: Param = serde_json::from_value(v).unwrap(); + let param: Param = serde_json::from_value(v.clone()).expect("param deserialized"); assert_eq!( param, @@ -312,18 +370,22 @@ mod test { indexed: None } ); + + let param_json = serde_json::to_value(param).expect("param serialized"); + + assert_eq!(v, param_json); } } #[test] - fn deserialize_int() { + fn serde_int() { for i in (8..=256).step_by(8) { let v = json!({ "name": "a", "type": format!("int{}", i), }); - let param: Param = serde_json::from_value(v).unwrap(); + let param: Param = serde_json::from_value(v.clone()).expect("param deserialized"); assert_eq!( param, @@ -333,17 +395,21 @@ mod test { indexed: None } ); + + let param_json = serde_json::to_value(param).expect("param serialized"); + + assert_eq!(v, param_json); } } #[test] - fn deserialize_address() { + fn serde_address() { let v = json!({ "name": "a", "type": "address", }); - let param: Param = serde_json::from_value(v).unwrap(); + let param: Param = serde_json::from_value(v.clone()).expect("param deserialized"); assert_eq!( param, @@ -353,16 +419,20 @@ mod test { indexed: None } ); + + let param_json = serde_json::to_value(param).expect("param serialized"); + + assert_eq!(v, param_json); } #[test] - fn deserialize_bool() { + fn serde_bool() { let v = json!({ "name": "a", "type": "bool", }); - let param: Param = serde_json::from_value(v).unwrap(); + let param: Param = serde_json::from_value(v.clone()).expect("param deserialized"); assert_eq!( param, @@ -372,16 +442,20 @@ mod test { indexed: None } ); + + let param_json = serde_json::to_value(param).expect("param serialized"); + + assert_eq!(v, param_json); } #[test] - fn deserialize_string() { + fn serde_string() { let v = json!({ "name": "a", "type": "string", }); - let param: Param = serde_json::from_value(v).unwrap(); + let param: Param = serde_json::from_value(v.clone()).expect("param deserialized"); assert_eq!( param, @@ -391,17 +465,21 @@ mod test { indexed: None } ); + + let param_json = serde_json::to_value(param).expect("param serialized"); + + assert_eq!(v, param_json); } #[test] - fn deserialize_bytes() { + fn serde_bytes() { for i in 1..=32 { let v = json!({ "name": "a", "type": format!("bytes{}", i), }); - let param: Param = serde_json::from_value(v).unwrap(); + let param: Param = serde_json::from_value(v.clone()).expect("param deserialized"); assert_eq!( param, @@ -411,6 +489,10 @@ mod test { indexed: None } ); + + let param_json = serde_json::to_value(param).expect("param serialized"); + + assert_eq!(v, param_json); } let v = json!({ @@ -418,7 +500,7 @@ mod test { "type": "bytes", }); - let param: Param = serde_json::from_value(v).unwrap(); + let param: Param = serde_json::from_value(v.clone()).expect("param deserialized"); assert_eq!( param, @@ -428,15 +510,19 @@ mod test { indexed: None } ); + + let param_json = serde_json::to_value(param).expect("param serialized"); + + assert_eq!(v, param_json); } #[test] - fn deserialize_array() { + fn serde_array() { let v = json!({ "name": "a", "type": "uint256[]", }); - let param: Param = serde_json::from_value(v).unwrap(); + let param: Param = serde_json::from_value(v.clone()).expect("param deserialized"); assert_eq!( param, @@ -446,15 +532,19 @@ mod test { indexed: None, } ); + + let param_json = serde_json::to_value(param).expect("param serialized"); + + assert_eq!(v, param_json); } #[test] - fn deserialize_nested_array() { + fn serde_nested_array() { let v = json!({ "name": "a", "type": "address[][]", }); - let param: Param = serde_json::from_value(v).unwrap(); + let param: Param = serde_json::from_value(v.clone()).expect("param deserialized"); assert_eq!( param, @@ -464,15 +554,19 @@ mod test { indexed: None, } ); + + let param_json = serde_json::to_value(param).expect("param serialized"); + + assert_eq!(v, param_json); } #[test] - fn deserialize_mixed_array() { + fn serde_mixed_array() { let v = json!({ "name": "a", "type": "string[2][]", }); - let param: Param = serde_json::from_value(v).unwrap(); + let param: Param = serde_json::from_value(v.clone()).expect("param deserialized"); assert_eq!( param, @@ -483,11 +577,16 @@ mod test { } ); + let param_json = serde_json::to_value(param).expect("param serialized"); + + assert_eq!(v, param_json); + let v = json!({ "name": "a", "type": "string[][3]", }); - let param: Param = serde_json::from_value(v).unwrap(); + + let param: Param = serde_json::from_value(v.clone()).expect("param deserialized"); assert_eq!( param, @@ -497,10 +596,14 @@ mod test { indexed: None, } ); + + let param_json = serde_json::to_value(param).expect("param serialized"); + + assert_eq!(v, param_json); } #[test] - fn deserialize_tuple() { + fn serde_tuple() { let v = json!({ "name": "s", "type": "tuple", @@ -530,7 +633,7 @@ mod test { ] }); - let param: Param = serde_json::from_value(v).unwrap(); + let param: Param = serde_json::from_value(v.clone()).expect("param deserialized"); assert_eq!( param, @@ -549,6 +652,10 @@ mod test { ]), indexed: None, } - ) + ); + + let param_json = serde_json::to_value(param).expect("param serialized"); + + assert_eq!(v, param_json); } }