alloy_dyn_abi/ext/
abi.rs

1use crate::{DynSolValue, Error as CrateError, Result, Specifier};
2use alloc::vec::Vec;
3use alloy_json_abi::{Constructor, Error, Function, Param};
4use alloy_primitives::Selector;
5use alloy_sol_types::abi::Decoder;
6
7#[allow(unknown_lints, unnameable_types)]
8mod sealed {
9    pub trait Sealed {}
10    impl Sealed for super::Constructor {}
11    impl Sealed for super::Error {}
12    impl Sealed for super::Function {}
13}
14use sealed::Sealed;
15
16/// Provides ABI encoding and decoding functionality.
17///
18/// This trait is sealed and cannot be implemented for types outside of this
19/// crate. It is implemented only for the following types:
20/// - [`Constructor`]
21/// - [`Error`]
22/// - [`Function`]
23pub trait JsonAbiExt: Sealed {
24    /// ABI-encodes the given values, prefixed by this item's selector, if any.
25    ///
26    /// The selector is:
27    /// - `None` for [`Constructor`],
28    /// - `Some(self.selector())` for [`Error`] and [`Function`].
29    ///
30    /// This behaviour is to ensure consistency with `ethabi`.
31    ///
32    /// To encode the data without the selector, use
33    /// [`abi_encode_input_raw`](JsonAbiExt::abi_encode_input_raw).
34    ///
35    /// # Errors
36    ///
37    /// This function will return an error if the given values do not match the
38    /// expected input types.
39    fn abi_encode_input(&self, values: &[DynSolValue]) -> Result<Vec<u8>>;
40
41    /// ABI-encodes the given values, without prefixing the data with the item's
42    /// selector.
43    ///
44    /// For [`Constructor`], this is the same as
45    /// [`abi_encode_input`](JsonAbiExt::abi_encode_input).
46    ///
47    /// # Errors
48    ///
49    /// This function will return an error if the given values do not match the
50    /// expected input types.
51    fn abi_encode_input_raw(&self, values: &[DynSolValue]) -> Result<Vec<u8>>;
52
53    /// ABI-decodes the given data according to this item's input types.
54    ///
55    /// # Errors
56    ///
57    /// This function will return an error if the decoded data does not match
58    /// the expected input types.
59    fn abi_decode_input(&self, data: &[u8], validate: bool) -> Result<Vec<DynSolValue>>;
60}
61
62/// Provide ABI encoding and decoding for the [`Function`] type.
63///
64/// This trait is sealed and cannot be implemented for types outside of this
65/// crate. It is implemented only for [`Function`].
66pub trait FunctionExt: JsonAbiExt + Sealed {
67    /// ABI-encodes the given values.
68    ///
69    /// Note that, contrary to
70    /// [`abi_encode_input`](JsonAbiExt::abi_encode_input), this method does
71    /// not prefix the return data with the function selector.
72    ///
73    /// # Errors
74    ///
75    /// This function will return an error if the given values do not match the
76    /// expected input types.
77    fn abi_encode_output(&self, values: &[DynSolValue]) -> Result<Vec<u8>>;
78
79    /// ABI-decodes the given data according to this functions's output types.
80    ///
81    /// This method does not check for any prefixes or selectors.
82    fn abi_decode_output(&self, data: &[u8], validate: bool) -> Result<Vec<DynSolValue>>;
83}
84
85impl JsonAbiExt for Constructor {
86    #[inline]
87    fn abi_encode_input(&self, values: &[DynSolValue]) -> Result<Vec<u8>> {
88        encode_typeck(&self.inputs, values)
89    }
90
91    #[inline]
92    fn abi_encode_input_raw(&self, values: &[DynSolValue]) -> Result<Vec<u8>> {
93        encode_typeck(&self.inputs, values)
94    }
95
96    #[inline]
97    fn abi_decode_input(&self, data: &[u8], validate: bool) -> Result<Vec<DynSolValue>> {
98        abi_decode(data, &self.inputs, validate)
99    }
100}
101
102impl JsonAbiExt for Error {
103    #[inline]
104    fn abi_encode_input(&self, values: &[DynSolValue]) -> Result<Vec<u8>> {
105        encode_typeck(&self.inputs, values).map(prefix_selector(self.selector()))
106    }
107
108    #[inline]
109    fn abi_encode_input_raw(&self, values: &[DynSolValue]) -> Result<Vec<u8>> {
110        encode_typeck(&self.inputs, values)
111    }
112
113    #[inline]
114    fn abi_decode_input(&self, data: &[u8], validate: bool) -> Result<Vec<DynSolValue>> {
115        abi_decode(data, &self.inputs, validate)
116    }
117}
118
119impl JsonAbiExt for Function {
120    #[inline]
121    fn abi_encode_input(&self, values: &[DynSolValue]) -> Result<Vec<u8>> {
122        encode_typeck(&self.inputs, values).map(prefix_selector(self.selector()))
123    }
124
125    #[inline]
126    fn abi_encode_input_raw(&self, values: &[DynSolValue]) -> Result<Vec<u8>> {
127        encode_typeck(&self.inputs, values)
128    }
129
130    #[inline]
131    fn abi_decode_input(&self, data: &[u8], validate: bool) -> Result<Vec<DynSolValue>> {
132        abi_decode(data, &self.inputs, validate)
133    }
134}
135
136impl FunctionExt for Function {
137    #[inline]
138    fn abi_encode_output(&self, values: &[DynSolValue]) -> Result<Vec<u8>> {
139        encode_typeck(&self.outputs, values)
140    }
141
142    #[inline]
143    fn abi_decode_output(&self, data: &[u8], validate: bool) -> Result<Vec<DynSolValue>> {
144        abi_decode(data, &self.outputs, validate)
145    }
146}
147
148#[inline]
149fn prefix_selector(selector: Selector) -> impl FnOnce(Vec<u8>) -> Vec<u8> {
150    move |data| {
151        let mut new = Vec::with_capacity(data.len() + 4);
152        new.extend_from_slice(&selector[..]);
153        new.extend_from_slice(&data[..]);
154        new
155    }
156}
157
158fn encode_typeck(params: &[Param], values: &[DynSolValue]) -> Result<Vec<u8>> {
159    if values.len() != params.len() {
160        return Err(CrateError::EncodeLengthMismatch {
161            expected: params.len(),
162            actual: values.len(),
163        });
164    }
165    for (value, param) in core::iter::zip(values, params) {
166        let ty = param.resolve()?;
167        if !ty.matches(value) {
168            return Err(CrateError::TypeMismatch {
169                expected: ty.sol_type_name().into_owned(),
170                actual: value.sol_type_name().unwrap_or_else(|| "<none>".into()).into_owned(),
171            });
172        }
173    }
174
175    Ok(abi_encode(values))
176}
177
178#[inline]
179fn abi_encode(values: &[DynSolValue]) -> Vec<u8> {
180    DynSolValue::encode_seq(values)
181}
182
183fn abi_decode(data: &[u8], params: &[Param], validate: bool) -> Result<Vec<DynSolValue>> {
184    let mut values = Vec::with_capacity(params.len());
185    let mut decoder = Decoder::new(data, validate);
186    for param in params {
187        let ty = param.resolve()?;
188        let value = ty.abi_decode_inner(&mut decoder, crate::DynToken::decode_single_populate)?;
189        values.push(value);
190    }
191    Ok(values)
192}
193
194#[cfg(test)]
195mod tests {
196    use super::*;
197    use alloy_primitives::{address, bytes, hex, Address, U256};
198
199    #[test]
200    fn can_encode_decode_functions() {
201        let json = r#"{
202            "inputs": [
203                {
204                    "internalType": "address",
205                    "name": "",
206                    "type": "address"
207                },
208                {
209                    "internalType": "address",
210                    "name": "",
211                    "type": "address"
212                }
213            ],
214            "name": "allowance",
215            "outputs": [
216                {
217                    "internalType": "uint256",
218                    "name": "",
219                    "type": "uint256"
220                }
221            ],
222            "stateMutability": "view",
223            "type": "function"
224        }"#;
225
226        let func: Function = serde_json::from_str(json).unwrap();
227        assert_eq!(2, func.inputs.len());
228        assert_eq!(1, func.outputs.len());
229        assert_eq!(func.signature(), "allowance(address,address)");
230
231        // encode
232        let expected = alloy_primitives::hex!(
233            "dd62ed3e"
234            "0000000000000000000000001111111111111111111111111111111111111111"
235            "0000000000000000000000002222222222222222222222222222222222222222"
236        );
237        let input = [
238            DynSolValue::Address(Address::repeat_byte(0x11)),
239            DynSolValue::Address(Address::repeat_byte(0x22)),
240        ];
241        let result = func.abi_encode_input(&input).unwrap();
242        assert_eq!(expected[..], result);
243
244        // Fail on unexpected input
245        let wrong_input = [
246            DynSolValue::Uint(U256::from(10u8), 256),
247            DynSolValue::Address(Address::repeat_byte(2u8)),
248        ];
249        assert!(func.abi_encode_input(&wrong_input).is_err());
250
251        // decode
252        let response = U256::from(1u8).to_be_bytes_vec();
253        let decoded = func.abi_decode_output(&response, true).unwrap();
254        assert_eq!(decoded, [DynSolValue::Uint(U256::from(1u8), 256)]);
255
256        // Fail on wrong response type
257        let bad_response = Address::repeat_byte(3u8).to_vec();
258        assert!(func.abi_decode_output(&bad_response, true).is_err());
259        assert!(func.abi_decode_output(&bad_response, false).is_err());
260    }
261
262    // https://github.com/foundry-rs/foundry/issues/7280
263    // Same as `encode_empty_bytes_array_in_tuple` in sol-types.
264    #[test]
265    fn empty_bytes_array() {
266        let func = Function::parse("register(bytes,address,bytes[])").unwrap();
267        let input = [
268            DynSolValue::Bytes(bytes!("09736b79736b79736b79026f7300").into()),
269            DynSolValue::Address(address!("0xB7b54cd129e6D8B24e6AE652a473449B273eE3E4")),
270            DynSolValue::Array(vec![]),
271        ];
272        let result = func.abi_encode_input(&input).unwrap();
273
274        let expected = hex!(
275            "
276            d123f99a
277            0000000000000000000000000000000000000000000000000000000000000060
278            000000000000000000000000B7b54cd129e6D8B24e6AE652a473449B273eE3E4
279            00000000000000000000000000000000000000000000000000000000000000a0
280            000000000000000000000000000000000000000000000000000000000000000e
281            09736b79736b79736b79026f7300000000000000000000000000000000000000
282            0000000000000000000000000000000000000000000000000000000000000000
283    	"
284        );
285        assert_eq!(hex::encode(expected), hex::encode(result));
286    }
287}