diff --git a/crates/dyn-abi/src/eip712/typed_data.rs b/crates/dyn-abi/src/eip712/typed_data.rs index 12c53dc3c..b0a3f7ed9 100644 --- a/crates/dyn-abi/src/eip712/typed_data.rs +++ b/crates/dyn-abi/src/eip712/typed_data.rs @@ -291,6 +291,55 @@ mod tests { ); } + #[test] + fn test_full_domain_contract_format() { + let json = json!({ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ] + }, + "primaryType": "EIP712Domain", + "domain": { + "name": "example.metamask.io", + "version": "1", + "chainId": 1, + "verifyingContract": "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359" + }, + "message": {} + }); + + let typed_data: TypedData = serde_json::from_value(json).unwrap(); + + let serialized_contract = typed_data.domain.verifying_contract.unwrap(); + assert_eq!(serialized_contract.to_string(), "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"); + + let hash = typed_data.eip712_signing_hash().unwrap(); + assert_eq!( + hex::encode(&hash[..]), + "4863a6e9735dee205f3010f78d613c425a26ae2db6e4cf207f88b5d26735d378", + ); + } + #[test] fn test_minimal_message() { let json = json!({ diff --git a/crates/primitives/src/bits/macros.rs b/crates/primitives/src/bits/macros.rs index c07d8dccd..537745004 100644 --- a/crates/primitives/src/bits/macros.rs +++ b/crates/primitives/src/bits/macros.rs @@ -595,6 +595,9 @@ macro_rules! impl_allocative { #[macro_export] #[cfg(feature = "serde")] macro_rules! impl_serde { + (Address) => { + // Use custom implementation for Address + }; ($t:ty) => { #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl $crate::private::serde::Serialize for $t { diff --git a/crates/primitives/src/bits/serde.rs b/crates/primitives/src/bits/serde.rs index 2e77dcd67..70faceec1 100644 --- a/crates/primitives/src/bits/serde.rs +++ b/crates/primitives/src/bits/serde.rs @@ -1,10 +1,31 @@ -use super::FixedBytes; -use core::fmt; +use super::{Address, FixedBytes}; +use alloc::string::String; +use core::{fmt, str::FromStr}; use serde::{ de::{self, Visitor}, Deserialize, Deserializer, Serialize, Serializer, }; +impl Serialize for Address { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let checksum_address = self.to_checksum(None); + serializer.serialize_str(&checksum_address) + } +} + +impl<'de> Deserialize<'de> for Address { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + Self::from_str(&s).map_err(serde::de::Error::custom) + } +} + impl Serialize for FixedBytes { fn serialize(&self, serializer: S) -> Result { if serializer.is_human_readable() { @@ -67,6 +88,8 @@ impl<'de, const N: usize> Deserialize<'de> for FixedBytes { #[cfg(test)] mod tests { + use core::str::FromStr; + use super::*; use alloc::string::ToString; use serde::Deserialize; @@ -88,6 +111,14 @@ mod tests { assert_eq!(serde_json::from_value::>(val).unwrap(), bytes); } + #[test] + fn serde_address() { + let address = Address::from_str("0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359").unwrap(); + let ser = serde_json::to_string(&address).unwrap(); + // serialize in checksum format + assert_eq!(ser, "\"0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359\""); + } + #[test] fn serde_num_array() { let json = serde_json::json! {{"fixed": [0,1,2,3,4]}};