alloy_dyn_abi/dynamic/
call.rs

1use crate::{DynSolType, DynSolValue, Error, Result};
2use alloy_primitives::Selector;
3use alloy_sol_types::abi::Decoder;
4
5#[cfg(not(feature = "std"))]
6use alloc::{string::String, vec::Vec};
7
8/// A representation of a Solidity call
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct DynSolCall {
11    /// The selector of the call.
12    selector: Selector,
13    /// The types of the call.
14    parameters: Vec<DynSolType>,
15    /// The method name of the call, if available.
16    method: Option<String>,
17    /// The types of the call's returns.
18    returns: DynSolReturns,
19}
20
21impl DynSolCall {
22    /// Create a new `DynSolCall` with the given selector and types.
23    pub const fn new(
24        selector: Selector,
25        parameters: Vec<DynSolType>,
26        method: Option<String>,
27        returns: DynSolReturns,
28    ) -> Self {
29        Self { selector, parameters, method, returns }
30    }
31
32    /// Get the selector of the call.
33    pub const fn selector(&self) -> Selector {
34        self.selector
35    }
36
37    /// Get the types of the call.
38    pub fn types(&self) -> &[DynSolType] {
39        &self.parameters
40    }
41
42    /// Get the method name of the call (if available)
43    pub fn method(&self) -> Option<&str> {
44        self.method.as_deref()
45    }
46
47    /// Get the types of the call's returns.
48    pub const fn returns(&self) -> &DynSolReturns {
49        &self.returns
50    }
51
52    /// ABI encode the given values as function params.
53    pub fn abi_encode_input(&self, values: &[DynSolValue]) -> Result<Vec<u8>> {
54        encode_typeck(&self.parameters, values).map(prefix_selector(self.selector))
55    }
56
57    /// ABI encode the given values as function params without prefixing the
58    /// selector.
59    pub fn abi_encode_input_raw(&self, values: &[DynSolValue]) -> Result<Vec<u8>> {
60        encode_typeck(&self.parameters, values)
61    }
62
63    /// ABI decode the given data as function returns.
64    pub fn abi_decode_input(&self, data: &[u8], validate: bool) -> Result<Vec<DynSolValue>> {
65        abi_decode(data, &self.parameters, validate)
66    }
67
68    /// ABI encode the given values as function return values.
69    pub fn abi_encode_output(&self, values: &[DynSolValue]) -> Result<Vec<u8>> {
70        self.returns.abi_encode_output(values)
71    }
72
73    /// ABI decode the given data as function return values.
74    pub fn abi_decode_output(&self, data: &[u8], validate: bool) -> Result<Vec<DynSolValue>> {
75        self.returns.abi_decode_output(data, validate)
76    }
77}
78
79/// A representation of a Solidity call's returns.
80#[derive(Debug, Clone, PartialEq, Eq)]
81pub struct DynSolReturns(Vec<DynSolType>);
82
83impl From<Vec<DynSolType>> for DynSolReturns {
84    fn from(types: Vec<DynSolType>) -> Self {
85        Self(types)
86    }
87}
88
89impl From<DynSolReturns> for Vec<DynSolType> {
90    fn from(returns: DynSolReturns) -> Self {
91        returns.0
92    }
93}
94
95impl DynSolReturns {
96    /// Create a new `DynSolReturns` with the given types.
97    pub const fn new(types: Vec<DynSolType>) -> Self {
98        Self(types)
99    }
100
101    /// Get the types of the returns.
102    pub fn types(&self) -> &[DynSolType] {
103        &self.0
104    }
105
106    /// ABI encode the given values as function return values.
107    pub fn abi_encode_output(&self, values: &[DynSolValue]) -> Result<Vec<u8>> {
108        encode_typeck(self.types(), values)
109    }
110
111    /// ABI decode the given data as function return values.
112    pub fn abi_decode_output(&self, data: &[u8], validate: bool) -> Result<Vec<DynSolValue>> {
113        abi_decode(data, self.types(), validate)
114    }
115}
116
117#[inline]
118pub(crate) fn prefix_selector(selector: Selector) -> impl FnOnce(Vec<u8>) -> Vec<u8> {
119    move |data| {
120        let mut new = Vec::with_capacity(data.len() + 4);
121        new.extend_from_slice(&selector[..]);
122        new.extend_from_slice(&data[..]);
123        new
124    }
125}
126
127pub(crate) fn encode_typeck(tys: &[DynSolType], values: &[DynSolValue]) -> Result<Vec<u8>> {
128    if values.len() != tys.len() {
129        return Err(Error::EncodeLengthMismatch { expected: tys.len(), actual: values.len() });
130    }
131
132    for (value, ty) in core::iter::zip(values, tys) {
133        if !ty.matches(value) {
134            return Err(Error::TypeMismatch {
135                expected: ty.sol_type_name().into_owned(),
136                actual: value.sol_type_name().unwrap_or_else(|| "<none>".into()).into_owned(),
137            });
138        }
139    }
140
141    Ok(abi_encode(values))
142}
143
144#[inline]
145pub(crate) fn abi_encode(values: &[DynSolValue]) -> Vec<u8> {
146    DynSolValue::encode_seq(values)
147}
148
149pub(crate) fn abi_decode(
150    data: &[u8],
151    tys: &[DynSolType],
152    validate: bool,
153) -> Result<Vec<DynSolValue>> {
154    let mut values = Vec::with_capacity(tys.len());
155    let mut decoder = Decoder::new(data, validate);
156    for ty in tys {
157        let value = ty.abi_decode_inner(&mut decoder, crate::DynToken::decode_single_populate)?;
158        values.push(value);
159    }
160    Ok(values)
161}