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:
parent
d20c154e64
commit
9626cc1335
|
@ -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)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue