alloy_json_abi/
utils.rs

1use crate::{EventParam, Param, StateMutability};
2use alloc::string::String;
3use alloy_primitives::Selector;
4use core::{fmt::Write, num::NonZeroUsize};
5use parser::{utils::ParsedSignature, ParameterSpecifier, TypeSpecifier, TypeStem};
6
7/// Capacity to allocate per [Param].
8const PARAM_CAP: usize = 32;
9
10/// `($($params),*)`
11macro_rules! params_abi_tuple {
12    ($inputs:expr, $s:expr) => {
13        $s.push('(');
14        for (i, input) in $inputs.iter().enumerate() {
15            if i > 0 {
16                $s.push(',');
17            }
18            input.selector_type_raw($s);
19        }
20        $s.push(')');
21    };
22}
23
24/// `$name($($inputs),*)($($outputs),*)`
25pub(crate) fn signature(name: &str, inputs: &[Param], outputs: Option<&[Param]>) -> String {
26    let parens = 2 + outputs.is_some() as usize * 2;
27    let n_outputs = outputs.map(<[_]>::len).unwrap_or(0);
28    let cap = name.len() + parens + (inputs.len() + n_outputs) * PARAM_CAP;
29    let mut sig = String::with_capacity(cap);
30    sig.push_str(name);
31    params_abi_tuple(inputs, &mut sig);
32    if let Some(outputs) = outputs {
33        params_abi_tuple(outputs, &mut sig);
34    }
35    sig
36}
37
38pub(crate) fn full_signature(
39    name: &str,
40    inputs: &[Param],
41    outputs: Option<&[Param]>,
42    state_mutability: StateMutability,
43) -> String {
44    let parens = 2 + outputs.is_some() as usize * 2;
45    let n_outputs = outputs.map(<[_]>::len).unwrap_or(0);
46    let state_mutability_str = state_mutability.as_str();
47    let cap = "function ".len()
48        + name.len()
49        + parens
50        + (inputs.len() + n_outputs) * PARAM_CAP
51        + state_mutability_str.map(|s| s.len() + 1).unwrap_or(0);
52    let mut sig = String::with_capacity(cap);
53    sig.push_str("function ");
54    sig.push_str(name);
55    params_tuple(inputs, &mut sig);
56    if let Some(state_mutability_str) = state_mutability_str {
57        sig.push(' ');
58        sig.push_str(state_mutability_str);
59    }
60    if let Some(outputs) = outputs {
61        if !outputs.is_empty() {
62            sig.push_str(" returns ");
63            params_tuple(outputs, &mut sig);
64        }
65    }
66    sig
67}
68
69/// `($($params),*)`
70pub(crate) fn params_abi_tuple(params: &[Param], s: &mut String) {
71    params_abi_tuple!(params, s);
72}
73
74pub(crate) fn params_tuple(params: &[Param], s: &mut String) {
75    s.push('(');
76    for (i, input) in params.iter().enumerate() {
77        if i > 0 {
78            s.push_str(", ");
79        }
80        input.full_selector_type_raw(s);
81        if !input.name.is_empty() {
82            s.push(' ');
83            s.push_str(&input.name);
84        }
85    }
86    s.push(')');
87}
88
89/// `$name($($inputs),*)`
90pub(crate) fn event_signature(name: &str, inputs: &[EventParam]) -> String {
91    let mut preimage = String::with_capacity(name.len() + 2 + inputs.len() * PARAM_CAP);
92    preimage.push_str(name);
93    params_abi_tuple!(inputs, &mut preimage);
94    preimage
95}
96
97/// `$name($($inputs indexed names),*)`
98pub(crate) fn event_full_signature(name: &str, inputs: &[EventParam]) -> String {
99    let mut sig = String::with_capacity("event ".len() + name.len() + 2 + inputs.len() * PARAM_CAP);
100    sig.push_str("event ");
101    sig.push_str(name);
102    sig.push('(');
103    for (i, input) in inputs.iter().enumerate() {
104        if i > 0 {
105            sig.push_str(", ");
106        }
107        input.full_selector_type_raw(&mut sig);
108        if input.indexed {
109            sig.push_str(" indexed");
110        }
111        if !input.name.is_empty() {
112            sig.push(' ');
113            sig.push_str(&input.name);
114        }
115    }
116    sig.push(')');
117    sig
118}
119
120/// `keccak256(preimage)[..4]`
121pub(crate) fn selector(preimage: &str) -> Selector {
122    alloy_primitives::keccak256(preimage.as_bytes())[..4].try_into().unwrap()
123}
124
125/// Strips `prefix` from `s` before parsing with `parser`. `prefix` must be followed by whitespace.
126pub(crate) fn parse_maybe_prefixed<F: FnOnce(&str) -> R, R>(
127    mut s: &str,
128    prefix: &str,
129    parser: F,
130) -> R {
131    if let Some(stripped) = s.strip_prefix(prefix) {
132        if stripped.starts_with(char::is_whitespace) {
133            s = stripped.trim_start();
134        }
135    }
136    parser(s)
137}
138
139#[inline]
140pub(crate) fn parse_sig<const O: bool>(s: &str) -> parser::Result<ParsedSignature<Param>> {
141    parser::utils::parse_signature::<O, _, _>(s, |p| mk_param(p.name, p.ty))
142}
143
144#[inline]
145pub(crate) fn parse_event_sig(s: &str) -> parser::Result<ParsedSignature<EventParam>> {
146    parser::utils::parse_signature::<false, _, _>(s, mk_eparam)
147}
148
149pub(crate) fn mk_param(name: Option<&str>, ty: TypeSpecifier<'_>) -> Param {
150    let name = name.unwrap_or_default().into();
151    let internal_type = None;
152    match ty.stem {
153        TypeStem::Root(s) => {
154            Param { name, ty: ty_string(s.span(), &ty.sizes), components: vec![], internal_type }
155        }
156        TypeStem::Tuple(t) => Param {
157            name,
158            ty: ty_string("tuple", &ty.sizes),
159            components: t.types.into_iter().map(|ty| mk_param(None, ty)).collect(),
160            internal_type,
161        },
162    }
163}
164
165pub(crate) fn mk_eparam(spec: ParameterSpecifier<'_>) -> EventParam {
166    let p = mk_param(spec.name, spec.ty);
167    EventParam {
168        name: p.name,
169        ty: p.ty,
170        indexed: spec.indexed,
171        components: p.components,
172        internal_type: p.internal_type,
173    }
174}
175
176fn ty_string(s: &str, sizes: &[Option<NonZeroUsize>]) -> String {
177    let mut ty = String::with_capacity(s.len() + sizes.len() * 4);
178    ty.push_str(s);
179    for size in sizes {
180        ty.push('[');
181        if let Some(size) = size {
182            write!(ty, "{size}").unwrap();
183        }
184        ty.push(']');
185    }
186    ty
187}
188
189pub(crate) fn validate_identifier<E: serde::de::Error>(name: &str) -> Result<(), E> {
190    if !name.is_empty() && !parser::is_valid_identifier(name) {
191        return Err(serde::de::Error::invalid_value(
192            serde::de::Unexpected::Str(name),
193            &"a valid Solidity identifier",
194        ));
195    }
196    Ok(())
197}
198
199#[cfg(test)]
200mod tests {
201    use super::*;
202
203    fn param(kind: &str) -> Param {
204        param2(kind, "param")
205    }
206
207    fn param2(kind: &str, name: &str) -> Param {
208        Param { ty: kind.into(), name: name.into(), internal_type: None, components: vec![] }
209    }
210
211    fn eparam(kind: &str) -> EventParam {
212        eparam_with_indexed(kind, "param", false)
213    }
214
215    fn eparam2(kind: &str, name: &str, indexed: bool) -> EventParam {
216        eparam_with_indexed(kind, name, indexed)
217    }
218
219    fn eparam_with_indexed(kind: &str, name: &str, indexed: bool) -> EventParam {
220        EventParam {
221            name: name.into(),
222            ty: kind.into(),
223            internal_type: None,
224            components: vec![],
225            indexed,
226        }
227    }
228
229    fn params(components: impl IntoIterator<Item = &'static str>) -> Param {
230        let components = components.into_iter().map(param).collect();
231        crate::Param { name: "param".into(), ty: "tuple".into(), internal_type: None, components }
232    }
233
234    fn full_signature_raw(
235        name: &str,
236        inputs: &[Param],
237        outputs: Option<&[Param]>,
238        state_mutability: StateMutability,
239    ) -> String {
240        full_signature(name, inputs, outputs, state_mutability)
241    }
242
243    fn full_signature_np(name: &str, inputs: &[Param], outputs: Option<&[Param]>) -> String {
244        full_signature_raw(name, inputs, outputs, StateMutability::NonPayable)
245    }
246
247    fn full_signature_with_sm(
248        name: &str,
249        inputs: &[Param],
250        outputs: Option<&[Param]>,
251        state_mutability: StateMutability,
252    ) -> String {
253        full_signature_raw(name, inputs, outputs, state_mutability)
254    }
255
256    #[test]
257    fn test_signature() {
258        assert_eq!(signature("foo", &[], None), "foo()");
259        assert_eq!(signature("bar", &[param("bool")], None), "bar(bool)");
260        assert_eq!(
261            signature("foo", &[param("bytes"), param("bytes32")], None),
262            "foo(bytes,bytes32)"
263        );
264        assert_eq!(
265            signature("foo", &[param("int"), params(["uint[]"]), param("string")], None),
266            "foo(int,(uint[]),string)"
267        );
268
269        assert_eq!(signature("foo", &[], Some(&[])), "foo()()");
270        assert_eq!(signature("foo", &[param("a")], Some(&[param("b")])), "foo(a)(b)");
271        assert_eq!(
272            signature("foo", &[param("a"), param("c")], Some(&[param("b"), param("d")])),
273            "foo(a,c)(b,d)"
274        );
275    }
276
277    #[test]
278    fn test_full_signature() {
279        assert_eq!(full_signature_np("foo", &[], None), "function foo()");
280        assert_eq!(full_signature_np("foo", &[], Some(&[])), "function foo()");
281        assert_eq!(full_signature_np("bar", &[param2("bool", "")], None), "function bar(bool)");
282        assert_eq!(
283            full_signature_np("bar", &[param2("bool", "")], Some(&[param2("bool", "")])),
284            "function bar(bool) returns (bool)"
285        );
286        assert_eq!(
287            full_signature_np(
288                "foo",
289                &[param2("address", "asset"), param2("uint256", "amount")],
290                None
291            ),
292            "function foo(address asset, uint256 amount)"
293        );
294        assert_eq!(
295            full_signature_np(
296                "foo",
297                &[param2("address", "asset")],
298                Some(&[param2("uint256", "amount")])
299            ),
300            "function foo(address asset) returns (uint256 amount)"
301        );
302
303        let components = vec![
304            param2("address", "pool"),
305            param2("uint256", "tokenInParam"),
306            param2("uint256", "tokenOutParam"),
307            param2("uint256", "maxPrice"),
308        ];
309        let swaps =
310            Param { name: "swaps".into(), ty: "tuple[]".into(), internal_type: None, components };
311
312        assert_eq!(
313            full_signature_with_sm(
314                "batchSwapExactIn",
315                &[
316                    swaps,
317                    param2("address", "tokenIn"),
318                    param2("address", "tokenOut"),
319                    param2("uint256", "totalAmountIn"),
320                    param2("uint256", "minTotalAmountOut"),
321                ],
322                Some(&[param2("uint256", "totalAmountOut")]),
323                StateMutability::Payable,
324            ),
325            "function batchSwapExactIn(tuple(address pool, uint256 tokenInParam, uint256 tokenOutParam, uint256 maxPrice)[] swaps, address tokenIn, address tokenOut, uint256 totalAmountIn, uint256 minTotalAmountOut) payable returns (uint256 totalAmountOut)"
326        );
327
328        assert_eq!(
329            full_signature_with_sm(
330                "name",
331                &[],
332                Some(&[param2("string", "")]),
333                StateMutability::View
334            ),
335            "function name() view returns (string)"
336        );
337
338        assert_eq!(
339            full_signature_with_sm(
340                "calculateHash",
341                &[param2("address[]", "_addresses")],
342                Some(&[param2("bytes32", "")]),
343                StateMutability::Pure,
344            ),
345            "function calculateHash(address[] _addresses) pure returns (bytes32)"
346        );
347    }
348
349    #[test]
350    fn test_event_signature() {
351        assert_eq!(event_signature("foo", &[]), "foo()");
352        assert_eq!(event_signature("foo", &[eparam("bool")]), "foo(bool)");
353        assert_eq!(event_signature("foo", &[eparam("bool"), eparam("string")]), "foo(bool,string)");
354    }
355
356    #[test]
357    fn test_event_full_signature() {
358        assert_eq!(event_full_signature("foo", &[]), "event foo()");
359        assert_eq!(
360            event_full_signature("foo", &[eparam2("bool", "confirmed", true)]),
361            "event foo(bool indexed confirmed)"
362        );
363        assert_eq!(
364            event_full_signature(
365                "foo",
366                &[eparam2("bool", "confirmed", true), eparam2("string", "message", false)]
367            ),
368            "event foo(bool indexed confirmed, string message)"
369        );
370
371        let components = vec![
372            param2("uint256", "amount"),
373            param2("uint256", "startTime"),
374            param2("uint256", "interval"),
375        ];
376        let info = EventParam {
377            name: "info".into(),
378            ty: "tuple".into(),
379            internal_type: None,
380            components,
381            indexed: false,
382        };
383        assert_eq!(
384            event_full_signature(
385                "SetupDirectDebit",
386                &[
387                    eparam2("address", "debtor", true),
388                    eparam2("address", "receiver", true),
389                    info,
390                ]            ),
391            "event SetupDirectDebit(address indexed debtor, address indexed receiver, tuple(uint256 amount, uint256 startTime, uint256 interval) info)"
392        );
393    }
394
395    #[test]
396    fn test_parse_sig() {
397        let empty_sig = |name: &str, anonymous| ParsedSignature::<Param> {
398            name: name.into(),
399            inputs: vec![],
400            outputs: vec![],
401            anonymous,
402            state_mutability: None,
403        };
404        let sig = |name: &str, inputs, outputs| ParsedSignature::<Param> {
405            name: name.into(),
406            inputs,
407            outputs,
408            anonymous: false,
409            state_mutability: None,
410        };
411
412        assert_eq!(parse_sig::<true>("foo()"), Ok(empty_sig("foo", false)));
413        assert_eq!(parse_sig::<true>("foo()()"), Ok(empty_sig("foo", false)));
414        assert_eq!(parse_sig::<true>("foo()external()"), Ok(empty_sig("foo", false)));
415        assert_eq!(parse_sig::<true>("foo() \t ()"), Ok(empty_sig("foo", false)));
416        assert_eq!(parse_sig::<true>("foo()  ()"), Ok(empty_sig("foo", false)));
417
418        assert_eq!(parse_sig::<false>("foo()"), Ok(empty_sig("foo", false)));
419        parse_sig::<false>("foo()()").unwrap_err();
420        parse_sig::<false>("foo()view external()").unwrap_err();
421        parse_sig::<false>("foo(,)()").unwrap_err();
422        parse_sig::<false>("foo(,)(,)").unwrap_err();
423
424        assert_eq!(parse_sig::<false>("foo()anonymous"), Ok(empty_sig("foo", true)));
425        assert_eq!(parse_sig::<false>("foo()\t anonymous"), Ok(empty_sig("foo", true)));
426
427        assert_eq!(parse_sig::<true>("foo()anonymous"), Ok(empty_sig("foo", true)));
428        assert_eq!(parse_sig::<true>("foo()\t anonymous"), Ok(empty_sig("foo", true)));
429
430        assert_eq!(parse_sig::<true>("foo() \t ()anonymous"), Ok(empty_sig("foo", true)));
431        assert_eq!(parse_sig::<true>("foo()()anonymous"), Ok(empty_sig("foo", true)));
432        assert_eq!(parse_sig::<true>("foo()()\t anonymous"), Ok(empty_sig("foo", true)));
433
434        assert_eq!(
435            parse_sig::<false>("foo(uint256 param)"),
436            Ok(sig("foo", vec![param("uint256")], vec![]))
437        );
438        assert_eq!(
439            parse_sig::<false>("bar(uint256 param)"),
440            Ok(sig("bar", vec![param("uint256")], vec![]))
441        );
442        assert_eq!(
443            parse_sig::<false>("baz(uint256 param, bool param)"),
444            Ok(sig("baz", vec![param("uint256"), param("bool")], vec![]))
445        );
446
447        assert_eq!(
448            parse_sig::<true>("f(a b)(c d)"),
449            Ok(sig("f", vec![param2("a", "b")], vec![param2("c", "d")]))
450        );
451
452        assert_eq!(
453            parse_sig::<true>("toString(uint number)(string s)"),
454            Ok(sig("toString", vec![param2("uint256", "number")], vec![param2("string", "s")]))
455        );
456
457        let mut sig_full = sig("toString", vec![param("uint256")], vec![param("string")]);
458        sig_full.state_mutability = Some(StateMutability::View);
459        assert_eq!(
460            parse_sig::<true>("toString(uint param) external view returns(string param)"),
461            Ok(sig_full)
462        );
463    }
464}