Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
feat: add abi encode decode impls
Browse files Browse the repository at this point in the history
  • Loading branch information
mattsse committed Oct 25, 2021
1 parent d0314bc commit 15926da
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ impl Context {
}

impl #ethers_contract::AbiEncode for #enum_name {
fn encode(self) -> Result<#ethers_core::types::Bytes, #ethers_contract::AbiError> {
fn encode(self) -> Vec<u8> {
match self {
#(
#enum_name::#variant_names(element) => element.encode()
Expand Down
7 changes: 3 additions & 4 deletions ethers-contract/src/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,15 @@ pub trait EthCall: Tokenizable + AbiDecode + Send + Sync {
}

impl<T: EthCall> AbiEncode for T {
fn encode(self) -> Result<Bytes, AbiError> {
fn encode(self) -> Vec<u8> {
let tokens = self.into_tokens();
let selector = Self::selector();
let encoded = ethers_core::abi::encode(&tokens);
let encoded: Vec<_> = selector
selector
.iter()
.copied()
.chain(encoded.into_iter())
.collect();
Ok(encoded.into())
.collect()
}
}

Expand Down
105 changes: 103 additions & 2 deletions ethers-contract/src/codec.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,115 @@
use crate::AbiError;
use ethers_core::types::Bytes;
use ethers_core::abi::{AbiArrayType, AbiType, Detokenize, Tokenizable, TokenizableItem};
use ethers_core::types::{Address, H256, U128, U256};

/// Trait for ABI encoding
pub trait AbiEncode {
/// ABI encode the type
fn encode(self) -> Result<Bytes, AbiError>;
fn encode(self) -> Vec<u8>;
}

/// Trait for ABI decoding
pub trait AbiDecode: Sized {
/// Decodes the ABI encoded data
fn decode(bytes: impl AsRef<[u8]>) -> Result<Self, AbiError>;
}

macro_rules! impl_abi_codec {
($($name:ty),*) => {
$(
impl AbiEncode for $name {
fn encode(self) -> Vec<u8> {
let token = self.into_token();
ethers_core::abi::encode(&[token]).into()
}
}
impl AbiDecode for $name {
fn decode(bytes: impl AsRef<[u8]>) -> Result<Self, AbiError> {
let tokens = ethers_core::abi::decode(
&[Self::param_type()], bytes.as_ref()
)?;
Ok(<Self as Detokenize>::from_tokens(tokens)?)
}
}
)*
};
}

impl_abi_codec!(
Address, bool, String, H256, U128, U256, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128
);

impl<T: TokenizableItem + Clone, const N: usize> AbiEncode for [T; N] {
fn encode(self) -> Vec<u8> {
let token = self.into_token();
ethers_core::abi::encode(&[token]).into()
}
}

impl<T, const N: usize> AbiDecode for [T; N]
where
T: TokenizableItem + AbiArrayType + Clone,
{
fn decode(bytes: impl AsRef<[u8]>) -> Result<Self, AbiError> {
let tokens = ethers_core::abi::decode(&[Self::param_type()], bytes.as_ref())?;
Ok(<Self as Detokenize>::from_tokens(tokens)?)
}
}

impl<T: TokenizableItem> AbiEncode for Vec<T> {
fn encode(self) -> Vec<u8> {
let token = self.into_token();
ethers_core::abi::encode(&[token]).into()
}
}

impl<T: TokenizableItem + AbiArrayType> AbiDecode for Vec<T> {
fn decode(bytes: impl AsRef<[u8]>) -> Result<Self, AbiError> {
let tokens = ethers_core::abi::decode(&[Self::param_type()], bytes.as_ref())?;
Ok(<Self as Detokenize>::from_tokens(tokens)?)
}
}

macro_rules! impl_abi_codec_tuple {
($num: expr, $( $ty: ident),+) => {
impl<$($ty, )+> AbiEncode for ($($ty,)+) where
$(
$ty: Tokenizable,
)+
{
fn encode(self) -> Vec<u8> {
let token = self.into_token();
ethers_core::abi::encode(&[token]).into()
}
}

impl<$($ty, )+> AbiDecode for ($($ty,)+) where
$(
$ty: AbiType + Tokenizable,
)+ {
fn decode(bytes: impl AsRef<[u8]>) -> Result<Self, AbiError> {
let tokens = ethers_core::abi::decode(
&[Self::param_type()], bytes.as_ref()
)?;
Ok(<Self as Detokenize>::from_tokens(tokens)?)
}
}
}
}

impl_abi_codec_tuple!(1, A);
impl_abi_codec_tuple!(2, A, B);
impl_abi_codec_tuple!(3, A, B, C);
impl_abi_codec_tuple!(4, A, B, C, D);
impl_abi_codec_tuple!(5, A, B, C, D, E);
impl_abi_codec_tuple!(6, A, B, C, D, E, F);
impl_abi_codec_tuple!(7, A, B, C, D, E, F, G);
impl_abi_codec_tuple!(8, A, B, C, D, E, F, G, H);
impl_abi_codec_tuple!(9, A, B, C, D, E, F, G, H, I);
impl_abi_codec_tuple!(10, A, B, C, D, E, F, G, H, I, J);
impl_abi_codec_tuple!(11, A, B, C, D, E, F, G, H, I, J, K);
impl_abi_codec_tuple!(12, A, B, C, D, E, F, G, H, I, J, K, L);
impl_abi_codec_tuple!(13, A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_abi_codec_tuple!(14, A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_abi_codec_tuple!(15, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_abi_codec_tuple!(16, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
20 changes: 10 additions & 10 deletions ethers-contract/tests/abigen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,26 +183,26 @@ fn can_gen_human_readable_with_structs() {
addr: Address::random(),
};
let encoded_call = contract.encode("bar", (call.x, call.y, call.addr)).unwrap();
assert_eq!(encoded_call, call.clone().encode().unwrap());
assert_eq!(encoded_call, call.clone().encode().into());
let decoded_call = BarCall::decode(encoded_call.as_ref()).unwrap();
assert_eq!(call, decoded_call);

let contract_call = SimpleContractCalls::Bar(call);
let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap();
assert_eq!(contract_call, decoded_enum);
assert_eq!(encoded_call, contract_call.encode().unwrap());
assert_eq!(encoded_call, contract_call.encode().into());

let call = YeetCall(1u64.into(), 0u64.into(), Address::zero());
let encoded_call = contract.encode("yeet", (call.0, call.1, call.2)).unwrap();
assert_eq!(encoded_call, call.clone().encode().unwrap());
assert_eq!(encoded_call, call.clone().encode().into());
let decoded_call = YeetCall::decode(encoded_call.as_ref()).unwrap();
assert_eq!(call, decoded_call);

let contract_call = SimpleContractCalls::Yeet(call.clone());
let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap();
assert_eq!(contract_call, decoded_enum);
assert_eq!(contract_call, call.into());
assert_eq!(encoded_call, contract_call.encode().unwrap());
assert_eq!(encoded_call, contract_call.encode().into());
}

#[test]
Expand All @@ -227,14 +227,14 @@ fn can_handle_overloaded_functions() {
let call = GetValueCall;

let encoded_call = contract.encode("getValue", ()).unwrap();
assert_eq!(encoded_call, call.clone().encode().unwrap());
assert_eq!(encoded_call, call.clone().encode().into());
let decoded_call = GetValueCall::decode(encoded_call.as_ref()).unwrap();
assert_eq!(call, decoded_call);

let contract_call = SimpleContractCalls::GetValue(call);
let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap();
assert_eq!(contract_call, decoded_enum);
assert_eq!(encoded_call, contract_call.encode().unwrap());
assert_eq!(encoded_call, contract_call.encode().into());

let call = GetValueWithOtherValueCall {
other_value: 420u64.into(),
Expand All @@ -243,14 +243,14 @@ fn can_handle_overloaded_functions() {
let encoded_call = contract
.encode_with_selector([15, 244, 201, 22], call.other_value)
.unwrap();
assert_eq!(encoded_call, call.clone().encode().unwrap());
assert_eq!(encoded_call, call.clone().encode().into());
let decoded_call = GetValueWithOtherValueCall::decode(encoded_call.as_ref()).unwrap();
assert_eq!(call, decoded_call);

let contract_call = SimpleContractCalls::GetValueWithOtherValue(call);
let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap();
assert_eq!(contract_call, decoded_enum);
assert_eq!(encoded_call, contract_call.encode().unwrap());
assert_eq!(encoded_call, contract_call.encode().into());

let call = GetValueWithOtherValueAndAddrCall {
other_value: 420u64.into(),
Expand All @@ -266,7 +266,7 @@ fn can_handle_overloaded_functions() {
let contract_call = SimpleContractCalls::GetValueWithOtherValueAndAddr(call);
let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap();
assert_eq!(contract_call, decoded_enum);
assert_eq!(encoded_call, contract_call.encode().unwrap());
assert_eq!(encoded_call, contract_call.encode().into());
}

#[tokio::test]
Expand Down Expand Up @@ -330,7 +330,7 @@ async fn can_handle_underscore_functions() {
// Manual call construction
use ethers_providers::Middleware;
// TODO: How do we handle underscores for calls here?
let data = simplestorage_mod::HashPuzzleCall.encode().unwrap();
let data = simplestorage_mod::HashPuzzleCall.encode();
let tx = Eip1559TransactionRequest::new().data(data).to(addr);
let tx = TypedTransaction::Eip1559(tx);
let res5 = client.call(&tx, None).await.unwrap();
Expand Down

0 comments on commit 15926da

Please sign in to comment.