feat: add contract interface helpers (#982)

* feat(contract): add helpers for decoding as raw Token

This allows decoding Events and Function data without knowing the return type

* feat(contract): add helpers for decoding function outputs

* chore(contract): remove redundant generic param
This commit is contained in:
Georgios Konstantopoulos 2022-03-02 15:58:18 +02:00 committed by GitHub
parent d20c154e64
commit 9626cc1335
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 116 additions and 9 deletions

View File

@ -2,7 +2,7 @@ use crate::Contract;
pub use ethers_core::abi::AbiError; pub use ethers_core::abi::AbiError;
use ethers_core::{ use ethers_core::{
abi::{Abi, Detokenize, Error, Event, Function, FunctionExt, RawLog, Tokenize}, abi::{Abi, Detokenize, Error, Event, Function, FunctionExt, RawLog, Token, Tokenize},
types::{Address, Bytes, Selector, H256}, types::{Address, Bytes, Selector, H256},
}; };
use ethers_providers::Middleware; use ethers_providers::Middleware;
@ -68,6 +68,47 @@ impl BaseContract {
decode_function_data(function, bytes, true) decode_function_data(function, bytes, true)
} }
/// Decodes the provided ABI encoded function arguments with the selected function name.
///
/// If the function exists multiple times and you want to use one of the overloaded
/// versions, consider using `decode_with_selector`
///
/// Returns a [`Token`] vector, which lets you decode function arguments dynamically
/// without knowing the return type.
pub fn decode_raw<T: AsRef<[u8]>>(&self, name: &str, bytes: T) -> Result<Vec<Token>, AbiError> {
let function = self.abi.function(name)?;
decode_function_data_raw(function, bytes, true)
}
/// Decodes the provided ABI encoded function output with the selected function name.
///
/// If the function exists multiple times and you want to use one of the overloaded
/// versions, consider using `decode_with_selector`
pub fn decode_output<D: Detokenize, T: AsRef<[u8]>>(
&self,
name: &str,
bytes: T,
) -> Result<D, AbiError> {
let function = self.abi.function(name)?;
decode_function_data(function, bytes, false)
}
/// Decodes the provided ABI encoded function output with the selected function name.
///
/// If the function exists multiple times and you want to use one of the overloaded
/// versions, consider using `decode_with_selector`
///
/// Returns a [`Token`] vector, which lets you decode function arguments dynamically
/// without knowing the return type.
pub fn decode_output_raw<T: AsRef<[u8]>>(
&self,
name: &str,
bytes: T,
) -> Result<Vec<Token>, AbiError> {
let function = self.abi.function(name)?;
decode_function_data_raw(function, bytes, false)
}
/// Decodes for a given event name, given the `log.topics` and /// Decodes for a given event name, given the `log.topics` and
/// `log.data` fields from the transaction receipt /// `log.data` fields from the transaction receipt
pub fn decode_event<D: Detokenize>( pub fn decode_event<D: Detokenize>(
@ -80,6 +121,34 @@ impl BaseContract {
decode_event(event, topics, data) decode_event(event, topics, data)
} }
/// Decodes for a given event name, given the `log.topics` and
/// `log.data` fields from the transaction receipt
///
/// Returns a [`Token`] vector, which lets you decode function arguments dynamically
/// without knowing the return type.
pub fn decode_event_raw(
&self,
name: &str,
topics: Vec<H256>,
data: Bytes,
) -> Result<Vec<Token>, AbiError> {
let event = self.abi.event(name)?;
decode_event_raw(event, topics, data)
}
/// Decodes the provided ABI encoded bytes with the selected function selector
///
/// Returns a [`Token`] vector, which lets you decode function arguments dynamically
/// without knowing the return type.
pub fn decode_with_selector_raw<T: AsRef<[u8]>>(
&self,
signature: Selector,
bytes: T,
) -> Result<Vec<Token>, AbiError> {
let function = self.get_from_signature(signature)?;
decode_function_data_raw(function, bytes, true)
}
/// Decodes the provided ABI encoded bytes with the selected function selector /// Decodes the provided ABI encoded bytes with the selected function selector
pub fn decode_with_selector<D: Detokenize, T: AsRef<[u8]>>( pub fn decode_with_selector<D: Detokenize, T: AsRef<[u8]>>(
&self, &self,
@ -90,6 +159,28 @@ impl BaseContract {
decode_function_data(function, bytes, true) decode_function_data(function, bytes, true)
} }
pub fn decode_output_with_selector<D: Detokenize, T: AsRef<[u8]>>(
&self,
signature: Selector,
bytes: T,
) -> Result<D, AbiError> {
let function = self.get_from_signature(signature)?;
decode_function_data(function, bytes, false)
}
/// Decodes the provided ABI encoded bytes with the selected function selector
///
/// Returns a [`Token`] vector, which lets you decode function arguments dynamically
/// without knowing the return type.
pub fn decode_output_with_selector_raw<T: AsRef<[u8]>>(
&self,
signature: Selector,
bytes: T,
) -> Result<Vec<Token>, AbiError> {
let function = self.get_from_signature(signature)?;
decode_function_data_raw(function, bytes, false)
}
fn get_from_signature(&self, signature: Selector) -> Result<&Function, AbiError> { fn get_from_signature(&self, signature: Selector) -> Result<&Function, AbiError> {
Ok(self Ok(self
.methods .methods
@ -119,17 +210,25 @@ impl AsRef<Abi> for BaseContract {
} }
} }
pub(crate) fn decode_event<D: Detokenize>( pub fn decode_event_raw(
event: &Event, event: &Event,
topics: Vec<H256>, topics: Vec<H256>,
data: Bytes, data: Bytes,
) -> Result<D, AbiError> { ) -> Result<Vec<Token>, AbiError> {
let tokens = event Ok(event
.parse_log(RawLog { topics, data: data.to_vec() })? .parse_log(RawLog { topics, data: data.to_vec() })?
.params .params
.into_iter() .into_iter()
.map(|param| param.value) .map(|param| param.value)
.collect::<Vec<_>>(); .collect::<Vec<_>>())
}
pub fn decode_event<D: Detokenize>(
event: &Event,
topics: Vec<H256>,
data: Bytes,
) -> Result<D, AbiError> {
let tokens = decode_event_raw(event, topics, data)?;
Ok(D::from_tokens(tokens)?) Ok(D::from_tokens(tokens)?)
} }
@ -140,21 +239,29 @@ pub fn encode_function_data<T: Tokenize>(function: &Function, args: T) -> Result
} }
/// Helper for ABI decoding raw data based on a function's input or output. /// Helper for ABI decoding raw data based on a function's input or output.
pub fn decode_function_data<D: Detokenize, T: AsRef<[u8]>>( pub fn decode_function_data_raw<T: AsRef<[u8]>>(
function: &Function, function: &Function,
bytes: T, bytes: T,
is_input: bool, is_input: bool,
) -> Result<D, AbiError> { ) -> Result<Vec<Token>, AbiError> {
let bytes = bytes.as_ref(); let bytes = bytes.as_ref();
let tokens = if is_input { Ok(if is_input {
if bytes.len() < 4 || bytes[..4] != function.selector() { if bytes.len() < 4 || bytes[..4] != function.selector() {
return Err(AbiError::WrongSelector) return Err(AbiError::WrongSelector)
} }
function.decode_input(&bytes[4..])? function.decode_input(&bytes[4..])?
} else { } else {
function.decode_output(bytes)? function.decode_output(bytes)?
}; })
}
/// Helper for ABI decoding raw data based on a function's input or output.
pub fn decode_function_data<D: Detokenize, T: AsRef<[u8]>>(
function: &Function,
bytes: T,
is_input: bool,
) -> Result<D, AbiError> {
let tokens = decode_function_data_raw(function, bytes, is_input)?;
Ok(D::from_tokens(tokens)?) Ok(D::from_tokens(tokens)?)
} }