From 2c30468b70dadba1263ca9660487bd1223519395 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 24 Nov 2021 21:15:17 +0100 Subject: [PATCH] fix(codec)!: ambiguity unit8[] and bytes (#613) * fix: unit8 encoding * feat: use ethers::types::Bytes for solidity bytes type * feat: add const generic from impls * fix: failing tests * fix: make compatible with bytes * update changelog * make compatible with encoding changes * chore rm write to file --- CHANGELOG.md | 2 ++ .../src/contract/types.rs | 2 +- ethers-contract/src/multicall/mod.rs | 8 +++---- .../src/multicall/multicall_contract.rs | 4 ++-- ethers-contract/tests/contract.rs | 6 ++--- ethers-core/ethers-derive-eip712/Cargo.toml | 2 +- ethers-core/ethers-derive-eip712/src/lib.rs | 4 ++-- ethers-core/src/abi/mod.rs | 23 ++++++++++++++----- ethers-core/src/abi/tokens.rs | 5 ++-- ethers-core/src/types/bytes.rs | 12 ++++++++++ 10 files changed, 47 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df2a976b..471af9cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## ethers-core +- Change types mapping for solidity `bytes` to rust `ethers::core::Bytes` and solidity `uint8[]` to rust `Vec`. + [613](https://github.com/gakonst/ethers-rs/pull/613) - Fix `format_units` to return a `String` of representing a decimal point float such that the decimal places don't get truncated. [597](https://github.com/gakonst/ethers-rs/pull/597) diff --git a/ethers-contract/ethers-contract-abigen/src/contract/types.rs b/ethers-contract/ethers-contract-abigen/src/contract/types.rs index e44def58..4100c35f 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/types.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/types.rs @@ -8,7 +8,7 @@ pub(crate) fn expand(kind: &ParamType) -> Result { match kind { ParamType::Address => Ok(quote! { #ethers_core::types::Address }), - ParamType::Bytes => Ok(quote! { Vec }), + ParamType::Bytes => Ok(quote! { #ethers_core::types::Bytes }), ParamType::Int(n) => match n / 8 { 1 => Ok(quote! { i8 }), 2 => Ok(quote! { i16 }), diff --git a/ethers-contract/src/multicall/mod.rs b/ethers-contract/src/multicall/mod.rs index 86affacc..096969d6 100644 --- a/ethers-contract/src/multicall/mod.rs +++ b/ethers-contract/src/multicall/mod.rs @@ -297,7 +297,7 @@ impl Multicall { .iter() .zip(&return_data) .map(|(call, bytes)| { - let mut tokens: Vec = call.function.decode_output(bytes)?; + let mut tokens: Vec = call.function.decode_output(bytes.as_ref())?; Ok(match tokens.len() { 0 => Token::Tuple(vec![]), @@ -343,10 +343,10 @@ impl Multicall { Ok(tx_hash) } - fn as_contract_call(&self) -> ContractCall>)> { + fn as_contract_call(&self) -> ContractCall)> { // Map the Multicall struct into appropriate types for `aggregate` function - let calls: Vec<(Address, Vec)> = - self.calls.iter().map(|call| (call.target, call.data.to_vec())).collect(); + let calls: Vec<(Address, Bytes)> = + self.calls.iter().map(|call| (call.target, call.data.clone())).collect(); // Construct the ContractCall for `aggregate` function to broadcast the transaction let mut contract_call = self.contract.aggregate(calls); diff --git a/ethers-contract/src/multicall/multicall_contract.rs b/ethers-contract/src/multicall/multicall_contract.rs index 87c22faa..5706736f 100644 --- a/ethers-contract/src/multicall/multicall_contract.rs +++ b/ethers-contract/src/multicall/multicall_contract.rs @@ -41,8 +41,8 @@ mod multicallcontract_mod { #[doc = "Calls the contract's `aggregate` (0x252dba42) function"] pub fn aggregate( &self, - calls: Vec<(Address, Vec)>, - ) -> ContractCall>)> { + calls: Vec<(Address, Bytes)>, + ) -> ContractCall)> { self.0 .method_hash([37, 45, 186, 66], calls) .expect("method not found (this should never happen)") diff --git a/ethers-contract/tests/contract.rs b/ethers-contract/tests/contract.rs index 606a5f34..8748af2b 100644 --- a/ethers-contract/tests/contract.rs +++ b/ethers-contract/tests/contract.rs @@ -9,7 +9,7 @@ mod eth_tests { use super::*; use ethers_contract::{LogMeta, Multicall}; use ethers_core::{ - types::{transaction::eip712::Eip712, Address, BlockId, I256, U256}, + types::{transaction::eip712::Eip712, Address, BlockId, Bytes, I256, U256}, utils::{keccak256, Ganache}, }; use ethers_derive_eip712::*; @@ -525,7 +525,7 @@ mod eth_tests { struct FooBar { foo: I256, bar: U256, - fizz: Vec, + fizz: Bytes, buzz: [u8; 32], far: String, out: Address, @@ -563,7 +563,7 @@ mod eth_tests { let foo_bar = FooBar { foo: I256::from(10), bar: U256::from(20), - fizz: b"fizz".to_vec(), + fizz: b"fizz".into(), buzz: keccak256("buzz"), far: String::from("space"), out: Address::from([0; 20]), diff --git a/ethers-core/ethers-derive-eip712/Cargo.toml b/ethers-core/ethers-derive-eip712/Cargo.toml index 912b9b21..ee6a1209 100644 --- a/ethers-core/ethers-derive-eip712/Cargo.toml +++ b/ethers-core/ethers-derive-eip712/Cargo.toml @@ -18,6 +18,6 @@ serde_json = "1.0.68" proc-macro2 = "1.0.29" [dev-dependencies] -ethers-contract = { version = "^0.6.0", path = "../../ethers-contract"} +ethers-contract = { version = "^0.6.0", path = "../../ethers-contract", features = ["abigen"]} ethers-contract-derive = { version = "^0.6.0", path = "../../ethers-contract/ethers-contract-derive" } ethers-signers = { version = "^0.6.0", path = "../../ethers-signers" } diff --git a/ethers-core/ethers-derive-eip712/src/lib.rs b/ethers-core/ethers-derive-eip712/src/lib.rs index 5fe9594e..f42a6cbe 100644 --- a/ethers-core/ethers-derive-eip712/src/lib.rs +++ b/ethers-core/ethers-derive-eip712/src/lib.rs @@ -18,7 +18,7 @@ //! //! # Example Usage //! -//! ```rust +//! ```ignore //! use ethers_contract::EthAbiType; //! use ethers_derive_eip712::*; //! use ethers_core::types::{transaction::eip712::Eip712, H160}; @@ -50,7 +50,7 @@ //! project: "radicle-reward".to_string(), //! }; //! -//! let hash = puzzle.encode_eip712()?; +//! let hash = puzzle.encode_eip712().unwrap(); //! ``` //! //! # Limitations diff --git a/ethers-core/src/abi/mod.rs b/ethers-core/src/abi/mod.rs index 29f60512..cc169e40 100644 --- a/ethers-core/src/abi/mod.rs +++ b/ethers-core/src/abi/mod.rs @@ -1,6 +1,9 @@ //! This module implements extensions to the [`ethabi`](https://docs.rs/ethabi) API. // Adapted from [Gnosis' ethcontract](https://github.com/gnosis/ethcontract-rs/blob/master/common/src/abiext.rs) -use crate::{types::Selector, utils::id}; +use crate::{ + types::{Bytes, Selector}, + utils::id, +}; pub use ethabi::{Contract as Abi, *}; @@ -117,7 +120,8 @@ macro_rules! impl_abi_type { } impl_abi_type!( - Vec => Bytes, + Bytes => Bytes, + Vec => Array(Box::new(ParamType::Uint(8))), Address => Address, bool => Bool, String => String, @@ -231,10 +235,17 @@ mod tests { #[test] fn abi_type_works() { - assert_eq!(ParamType::Bytes, Vec::::param_type()); - assert_eq!(ParamType::Array(Box::new(ParamType::Bytes)), Vec::>::param_type()); + assert_eq!(ParamType::Bytes, Bytes::param_type()); + assert_eq!(ParamType::Array(Box::new(ParamType::Uint(8))), Vec::::param_type()); + assert_eq!(ParamType::Array(Box::new(ParamType::Bytes)), Vec::::param_type()); assert_eq!( - ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Bytes)))), + ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Uint(8))))), + Vec::>::param_type() + ); + assert_eq!( + ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Array(Box::new( + ParamType::Uint(8) + )))))), Vec::>>::param_type() ); @@ -242,7 +253,7 @@ mod tests { assert_eq!( ParamType::Tuple(vec![ParamType::Bytes, ParamType::Address]), - <(Vec, Address)>::param_type() + <(Bytes, Address)>::param_type() ); assert_eq!(ParamType::FixedBytes(32), <[u8; 32]>::param_type()); diff --git a/ethers-core/src/abi/tokens.rs b/ethers-core/src/abi/tokens.rs index b32ea32f..ca567078 100644 --- a/ethers-core/src/abi/tokens.rs +++ b/ethers-core/src/abi/tokens.rs @@ -287,7 +287,7 @@ macro_rules! tokenizable_item { tokenizable_item! { Token, String, Address, H256, U256, I256, U128, bool, Vec, - i8, i16, i32, i64, i128, u16, u32, u64, u128, + i8, i16, i32, i64, i128, u16, u32, u64, u128, Bytes, } macro_rules! impl_tokenizable_item_tuple { @@ -321,12 +321,13 @@ impl Tokenizable for Vec { fn from_token(token: Token) -> Result { match token { Token::Bytes(data) => Ok(data), + Token::Array(data) => data.into_iter().map(u8::from_token).collect(), Token::FixedBytes(data) => Ok(data), other => Err(InvalidOutputType(format!("Expected `bytes`, got {:?}", other))), } } fn into_token(self) -> Token { - Token::Bytes(self) + Token::Array(self.into_iter().map(Tokenizable::into_token).collect()) } } diff --git a/ethers-core/src/types/bytes.rs b/ethers-core/src/types/bytes.rs index 485052d8..f2179711 100644 --- a/ethers-core/src/types/bytes.rs +++ b/ethers-core/src/types/bytes.rs @@ -34,6 +34,18 @@ impl From> for Bytes { } } +impl From<[u8; N]> for Bytes { + fn from(src: [u8; N]) -> Self { + src.to_vec().into() + } +} + +impl<'a, const N: usize> From<&'a [u8; N]> for Bytes { + fn from(src: &'a [u8; N]) -> Self { + src.to_vec().into() + } +} + pub fn serialize_bytes(x: T, s: S) -> Result where S: Serializer,