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

feat: add contract interface helpers #982

Merged
merged 3 commits into from
Mar 2, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 116 additions & 9 deletions ethers-contract/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::Contract;

pub use ethers_core::abi::AbiError;
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},
};
use ethers_providers::Middleware;
Expand Down Expand Up @@ -68,6 +68,47 @@ impl BaseContract {
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
/// `log.data` fields from the transaction receipt
pub fn decode_event<D: Detokenize>(
Expand All @@ -80,6 +121,34 @@ impl BaseContract {
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
pub fn decode_with_selector<D: Detokenize, T: AsRef<[u8]>>(
&self,
Expand All @@ -90,6 +159,28 @@ impl BaseContract {
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> {
Ok(self
.methods
Expand Down Expand Up @@ -119,17 +210,25 @@ impl AsRef<Abi> for BaseContract {
}
}

pub(crate) fn decode_event<D: Detokenize>(
pub fn decode_event_raw(
event: &Event,
topics: Vec<H256>,
data: Bytes,
) -> Result<D, AbiError> {
let tokens = event
) -> Result<Vec<Token>, AbiError> {
Ok(event
.parse_log(RawLog { topics, data: data.to_vec() })?
.params
.into_iter()
.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)?)
}

Expand All @@ -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.
pub fn decode_function_data<D: Detokenize, T: AsRef<[u8]>>(
pub fn decode_function_data_raw<T: AsRef<[u8]>>(
function: &Function,
bytes: T,
is_input: bool,
) -> Result<D, AbiError> {
) -> Result<Vec<Token>, AbiError> {
let bytes = bytes.as_ref();
let tokens = if is_input {
Ok(if is_input {
if bytes.len() < 4 || bytes[..4] != function.selector() {
return Err(AbiError::WrongSelector)
}
function.decode_input(&bytes[4..])?
} else {
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)?)
}

Expand Down