alloy_contract/
interface.rs

1use crate::{ContractInstance, Error, Result};
2use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt};
3use alloy_json_abi::{Function, JsonAbi};
4use alloy_primitives::{
5    map::{FbHashMap, SelectorHashMap},
6    Address, FixedBytes, Selector,
7};
8use std::collections::BTreeMap;
9
10/// A smart contract interface.
11#[derive(Clone, Debug)]
12pub struct Interface {
13    abi: JsonAbi,
14    functions: SelectorHashMap<(String, usize)>,
15}
16
17// TODO: events/errors
18impl Interface {
19    /// Creates a new contract interface from the provided ABI.
20    pub fn new(abi: JsonAbi) -> Self {
21        let functions = create_mapping(&abi.functions, Function::selector);
22        Self { abi, functions }
23    }
24
25    /// Returns the ABI encoded data (including the selector) for the provided function and
26    /// arguments.
27    ///
28    /// # Note
29    ///
30    /// If the function exists multiple times and you want to use one of the overloaded versions,
31    /// consider using [`Self::encode_input_with_selector`].
32    pub fn encode_input(&self, name: &str, args: &[DynSolValue]) -> Result<Vec<u8>> {
33        self.get_from_name(name)?.abi_encode_input(args).map_err(Into::into)
34    }
35
36    /// Returns the ABI encoded data (including the selector) for the function with the provided
37    /// selector and arguments.
38    pub fn encode_input_with_selector(
39        &self,
40        selector: &Selector,
41        args: &[DynSolValue],
42    ) -> Result<Vec<u8>> {
43        self.get_from_selector(selector)?.abi_encode_input(args).map_err(Into::into)
44    }
45
46    /// ABI-decodes the given data according to the function's types.
47    ///
48    /// # Note
49    ///
50    /// If the function exists multiple times and you want to use one of the overloaded versions,
51    /// consider using [`Self::decode_input_with_selector`].
52    pub fn decode_input(
53        &self,
54        name: &str,
55        data: &[u8],
56        validate: bool,
57    ) -> Result<Vec<DynSolValue>> {
58        self.get_from_name(name)?.abi_decode_input(data, validate).map_err(Into::into)
59    }
60
61    /// Decode the provided ABI encoded bytes as the input of the provided function selector.
62    pub fn decode_input_with_selector(
63        &self,
64        selector: &Selector,
65        data: &[u8],
66        validate: bool,
67    ) -> Result<Vec<DynSolValue>> {
68        self.get_from_selector(selector)?.abi_decode_input(data, validate).map_err(Into::into)
69    }
70
71    /// Decode the provided ABI encoded bytes as the output of the first function with the given
72    /// name.
73    ///
74    /// # Note
75    ///
76    /// If there are multiple functions with the same name, consider using
77    /// [`Self::decode_output_with_selector`]
78    pub fn decode_output(
79        &self,
80        name: &str,
81        data: &[u8],
82        validate: bool,
83    ) -> Result<Vec<DynSolValue>> {
84        self.get_from_name(name)?.abi_decode_output(data, validate).map_err(Into::into)
85    }
86
87    /// Decode the provided ABI encoded bytes as the output of the provided function selector.
88    pub fn decode_output_with_selector(
89        &self,
90        selector: &Selector,
91        data: &[u8],
92        validate: bool,
93    ) -> Result<Vec<DynSolValue>> {
94        self.get_from_selector(selector)?.abi_decode_output(data, validate).map_err(Into::into)
95    }
96
97    /// Returns a reference to the contract's ABI.
98    pub const fn abi(&self) -> &JsonAbi {
99        &self.abi
100    }
101
102    /// Consumes the interface, returning the inner ABI.
103    pub fn into_abi(self) -> JsonAbi {
104        self.abi
105    }
106
107    pub(crate) fn get_from_name(&self, name: &str) -> Result<&Function> {
108        self.abi
109            .function(name)
110            .and_then(|r| r.first())
111            .ok_or_else(|| Error::UnknownFunction(name.to_string()))
112    }
113
114    pub(crate) fn get_from_selector(&self, selector: &Selector) -> Result<&Function> {
115        self.functions
116            .get(selector)
117            .map(|(name, index)| &self.abi.functions[name][*index])
118            .ok_or_else(|| Error::UnknownSelector(*selector))
119    }
120
121    /// Create a [`ContractInstance`] from this ABI for a contract at the given address.
122    pub const fn connect<P, N>(self, address: Address, provider: P) -> ContractInstance<P, N> {
123        ContractInstance::new(address, provider, self)
124    }
125}
126
127/// Utility function for creating a mapping between a unique signature and a
128/// name-index pair for accessing contract ABI items.
129fn create_mapping<const N: usize, T, F>(
130    elements: &BTreeMap<String, Vec<T>>,
131    signature: F,
132) -> FbHashMap<N, (String, usize)>
133where
134    F: Fn(&T) -> FixedBytes<N> + Copy,
135{
136    elements
137        .iter()
138        .flat_map(|(name, sub_elements)| {
139            sub_elements
140                .iter()
141                .enumerate()
142                .map(move |(index, element)| (signature(element), (name.to_owned(), index)))
143        })
144        .collect()
145}