alloy_json_abi/
item.rs

1use crate::{param::Param, serde_state_mutability_compat, utils::*, EventParam, StateMutability};
2use alloc::{borrow::Cow, string::String, vec::Vec};
3use alloy_primitives::{keccak256, Selector, B256};
4use core::str::FromStr;
5use parser::utils::ParsedSignature;
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7
8/// Declares all JSON ABI items.
9macro_rules! abi_items {
10    ($(
11        $(#[$attr:meta])*
12        $vis:vis struct $name:ident : $name_lower:literal {$(
13            $(#[$fattr:meta])*
14            $fvis:vis $field:ident : $type:ty,
15        )*}
16    )*) => {
17        $(
18            $(#[$attr])*
19            #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
20            #[serde(rename = $name_lower, rename_all = "camelCase", tag = "type")]
21            $vis struct $name {$(
22                $(#[$fattr])*
23                $fvis $field: $type,
24            )*}
25
26            impl From<$name> for AbiItem<'_> {
27                #[inline]
28                fn from(item: $name) -> Self {
29                    AbiItem::$name(Cow::Owned(item))
30                }
31            }
32
33            impl<'a> From<&'a $name> for AbiItem<'a> {
34                #[inline]
35                fn from(item: &'a $name) -> Self {
36                    AbiItem::$name(Cow::Borrowed(item))
37                }
38            }
39        )*
40
41        // Note: `AbiItem` **must not** derive `Serialize`, since we use `tag`
42        // only for deserialization, while we treat it as `untagged` for serialization.
43        // This is because the individual item structs are already tagged, and
44        // deriving `Serialize` would emit the tag field twice.
45
46        /// A JSON ABI item.
47        #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize)]
48        #[serde(tag = "type", rename_all = "camelCase")]
49        pub enum AbiItem<'a> {$(
50            #[doc = concat!("A JSON ABI [`", stringify!($name), "`].")]
51            $name(Cow<'a, $name>),
52        )*}
53
54        impl Serialize for AbiItem<'_> {
55            fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
56                match self {$(
57                    Self::$name(item) => item.serialize(serializer),
58                )*}
59            }
60        }
61
62        impl AbiItem<'_> {
63            /// Returns the JSON type of the item as a string.
64            ///
65            /// # Examples
66            ///
67            /// ```
68            /// # use alloy_json_abi::AbiItem;
69            /// let item = AbiItem::parse("function f()")?;
70            /// assert_eq!(item.json_type(), "function");
71            /// # Ok::<_, alloy_json_abi::parser::Error>(())
72            /// ```
73            #[inline]
74            pub const fn json_type(&self) -> &'static str {
75                match self {$(
76                    Self::$name(_) => $name_lower,
77                )*}
78            }
79        }
80    };
81}
82
83abi_items! {
84    /// A JSON ABI constructor function.
85    pub struct Constructor: "constructor" {
86        /// The input types of the constructor. May be empty.
87        pub inputs: Vec<Param>,
88        /// The state mutability of the constructor.
89        #[serde(default, flatten, with = "serde_state_mutability_compat")]
90        pub state_mutability: StateMutability,
91    }
92
93    /// A JSON ABI fallback function.
94    #[derive(Copy)]
95    pub struct Fallback: "fallback" {
96        /// The state mutability of the fallback function.
97        #[serde(default, flatten, with = "serde_state_mutability_compat")]
98        pub state_mutability: StateMutability,
99    }
100
101    /// A JSON ABI receive function.
102    #[derive(Copy)]
103    pub struct Receive: "receive" {
104        /// The state mutability of the receive function.
105        #[serde(default, flatten, with = "serde_state_mutability_compat")]
106        pub state_mutability: StateMutability,
107    }
108
109    /// A JSON ABI function.
110    pub struct Function: "function" {
111        /// The name of the function.
112        #[serde(deserialize_with = "validated_identifier")]
113        pub name: String,
114        /// The input types of the function. May be empty.
115        pub inputs: Vec<Param>,
116        /// The output types of the function. May be empty.
117        pub outputs: Vec<Param>,
118        /// The state mutability of the function.
119        #[serde(default, flatten, with = "serde_state_mutability_compat")]
120        pub state_mutability: StateMutability,
121    }
122
123    /// A JSON ABI event.
124    pub struct Event: "event" {
125        /// The name of the event.
126        #[serde(deserialize_with = "validated_identifier")]
127        pub name: String,
128        /// A list of the event's inputs, in order.
129        pub inputs: Vec<EventParam>,
130        /// Whether the event is anonymous. Anonymous events do not have their
131        /// signature included in the topic 0. Instead, the indexed arguments
132        /// are 0-indexed.
133        pub anonymous: bool,
134    }
135
136    /// A JSON ABI error.
137    pub struct Error: "error" {
138        /// The name of the error.
139        #[serde(deserialize_with = "validated_identifier")]
140        pub name: String,
141        /// A list of the error's components, in order.
142        pub inputs: Vec<Param>,
143    }
144}
145
146#[inline]
147fn validated_identifier<'de, D: Deserializer<'de>>(deserializer: D) -> Result<String, D::Error> {
148    let s = String::deserialize(deserializer)?;
149    validate_identifier(&s)?;
150    Ok(s)
151}
152
153impl FromStr for AbiItem<'_> {
154    type Err = parser::Error;
155
156    #[inline]
157    fn from_str(s: &str) -> Result<Self, Self::Err> {
158        Self::parse(s)
159    }
160}
161
162impl AbiItem<'_> {
163    /// Parses a single [Human-Readable ABI] string into an ABI item.
164    ///
165    /// [Human-Readable ABI]: https://docs.ethers.org/v5/api/utils/abi/formats/#abi-formats--human-readable-abi
166    ///
167    /// # Examples
168    ///
169    /// ```
170    /// # use alloy_json_abi::{AbiItem, Function, Param};
171    /// assert_eq!(
172    ///     AbiItem::parse("function foo(bool bar)"),
173    ///     Ok(AbiItem::from(Function::parse("foo(bool bar)").unwrap()).into()),
174    /// );
175    /// ```
176    pub fn parse(mut input: &str) -> parser::Result<Self> {
177        // Need this copy for Constructor, since the keyword is also the name of the function.
178        let copy = input;
179        match parser::utils::parse_item_keyword(&mut input)? {
180            "constructor" => Constructor::parse(copy).map(Into::into),
181            "function" => Function::parse(input).map(Into::into),
182            "error" => Error::parse(input).map(Into::into),
183            "event" => Event::parse(input).map(Into::into),
184            keyword => Err(parser::Error::new(format_args!(
185                "invalid AbiItem keyword: {keyword:?}, \
186                 expected one of \"constructor\", \"function\", \"error\", or \"event\""
187            ))),
188        }
189    }
190
191    /// Returns the debug name of the item.
192    #[inline]
193    pub const fn debug_name(&self) -> &'static str {
194        match self {
195            AbiItem::Constructor(_) => "Constructor",
196            AbiItem::Fallback(_) => "Fallback",
197            AbiItem::Receive(_) => "Receive",
198            AbiItem::Function(_) => "Function",
199            AbiItem::Event(_) => "Event",
200            AbiItem::Error(_) => "Error",
201        }
202    }
203
204    /// Returns an immutable reference to the name of the item.
205    #[inline]
206    pub fn name(&self) -> Option<&String> {
207        match self {
208            Self::Event(item) => Some(&item.name),
209            Self::Error(item) => Some(&item.name),
210            Self::Function(item) => Some(&item.name),
211            Self::Constructor(_) | Self::Fallback(_) | Self::Receive(_) => None,
212        }
213    }
214
215    /// Returns a mutable reference to the name of the item.
216    ///
217    /// Clones the item if it is not already owned.
218    #[inline]
219    pub fn name_mut(&mut self) -> Option<&mut String> {
220        match self {
221            Self::Event(item) => Some(&mut item.to_mut().name),
222            Self::Error(item) => Some(&mut item.to_mut().name),
223            Self::Function(item) => Some(&mut item.to_mut().name),
224            Self::Constructor(_) | Self::Fallback(_) | Self::Receive(_) => None,
225        }
226    }
227
228    /// Returns the state mutability of the item.
229    #[inline]
230    pub fn state_mutability(&self) -> Option<StateMutability> {
231        match self {
232            Self::Constructor(item) => Some(item.state_mutability),
233            Self::Fallback(item) => Some(item.state_mutability),
234            Self::Receive(item) => Some(item.state_mutability),
235            Self::Function(item) => Some(item.state_mutability),
236            Self::Event(_) | Self::Error(_) => None,
237        }
238    }
239
240    /// Returns a mutable reference to the state mutability of the item.
241    ///
242    /// Clones the item if it is not already owned.
243    #[inline]
244    pub fn state_mutability_mut(&mut self) -> Option<&mut StateMutability> {
245        match self {
246            Self::Constructor(item) => Some(&mut item.to_mut().state_mutability),
247            Self::Fallback(item) => Some(&mut item.to_mut().state_mutability),
248            Self::Receive(item) => Some(&mut item.to_mut().state_mutability),
249            Self::Function(item) => Some(&mut item.to_mut().state_mutability),
250            Self::Event(_) | Self::Error(_) => None,
251        }
252    }
253
254    /// Returns an immutable reference to the inputs of the item.
255    ///
256    /// Use [`event_inputs`](Self::event_inputs) for events instead.
257    #[inline]
258    pub fn inputs(&self) -> Option<&Vec<Param>> {
259        match self {
260            Self::Error(item) => Some(&item.inputs),
261            Self::Constructor(item) => Some(&item.inputs),
262            Self::Function(item) => Some(&item.inputs),
263            Self::Event(_) | Self::Fallback(_) | Self::Receive(_) => None,
264        }
265    }
266
267    /// Returns a mutable reference to the inputs of the item.
268    ///
269    /// Clones the item if it is not already owned.
270    ///
271    /// Use [`event_inputs`](Self::event_inputs) for events instead.
272    #[inline]
273    pub fn inputs_mut(&mut self) -> Option<&mut Vec<Param>> {
274        match self {
275            Self::Error(item) => Some(&mut item.to_mut().inputs),
276            Self::Constructor(item) => Some(&mut item.to_mut().inputs),
277            Self::Function(item) => Some(&mut item.to_mut().inputs),
278            Self::Event(_) | Self::Fallback(_) | Self::Receive(_) => None,
279        }
280    }
281
282    /// Returns an immutable reference to the event inputs of the item.
283    ///
284    /// Use [`inputs`](Self::inputs) for other items instead.
285    #[inline]
286    pub fn event_inputs(&self) -> Option<&Vec<EventParam>> {
287        match self {
288            Self::Event(item) => Some(&item.inputs),
289            Self::Constructor(_)
290            | Self::Fallback(_)
291            | Self::Receive(_)
292            | Self::Error(_)
293            | Self::Function(_) => None,
294        }
295    }
296
297    /// Returns a mutable reference to the event inputs of the item.
298    ///
299    /// Clones the item if it is not already owned.
300    ///
301    /// Use [`inputs`](Self::inputs) for other items instead.
302    #[inline]
303    pub fn event_inputs_mut(&mut self) -> Option<&mut Vec<EventParam>> {
304        match self {
305            Self::Event(item) => Some(&mut item.to_mut().inputs),
306            Self::Constructor(_)
307            | Self::Fallback(_)
308            | Self::Receive(_)
309            | Self::Error(_)
310            | Self::Function(_) => None,
311        }
312    }
313
314    /// Returns an immutable reference to the outputs of the item.
315    #[inline]
316    pub fn outputs(&self) -> Option<&Vec<Param>> {
317        match self {
318            Self::Function(item) => Some(&item.outputs),
319            Self::Constructor(_)
320            | Self::Fallback(_)
321            | Self::Receive(_)
322            | Self::Error(_)
323            | Self::Event(_) => None,
324        }
325    }
326
327    /// Returns an immutable reference to the outputs of the item.
328    #[inline]
329    pub fn outputs_mut(&mut self) -> Option<&mut Vec<Param>> {
330        match self {
331            Self::Function(item) => Some(&mut item.to_mut().outputs),
332            Self::Constructor(_)
333            | Self::Fallback(_)
334            | Self::Receive(_)
335            | Self::Error(_)
336            | Self::Event(_) => None,
337        }
338    }
339}
340
341impl FromStr for Constructor {
342    type Err = parser::Error;
343
344    #[inline]
345    fn from_str(s: &str) -> Result<Self, Self::Err> {
346        Self::parse(s)
347    }
348}
349
350impl Constructor {
351    /// Parses a Solidity constructor string:
352    /// `constructor($($inputs),*) [visibility] [s_mutability]`
353    ///
354    /// Note:
355    /// - the name must always be `constructor`
356    /// - visibility is ignored
357    ///
358    /// # Examples
359    ///
360    /// ```
361    /// # use alloy_json_abi::{Constructor, Param, StateMutability};
362    /// assert_eq!(
363    ///     Constructor::parse("constructor(uint foo, address bar)"),
364    ///     Ok(Constructor {
365    ///         inputs: vec![Param::parse("uint foo").unwrap(), Param::parse("address bar").unwrap()],
366    ///         state_mutability: StateMutability::NonPayable,
367    ///     }),
368    /// );
369    /// ```
370    #[inline]
371    pub fn parse(s: &str) -> parser::Result<Self> {
372        parse_sig::<false>(s).and_then(Self::parsed)
373    }
374
375    fn parsed(sig: ParsedSignature<Param>) -> parser::Result<Self> {
376        let ParsedSignature { name, inputs, outputs, anonymous, state_mutability } = sig;
377        if name != "constructor" {
378            return Err(parser::Error::new("constructors' name must be exactly \"constructor\""));
379        }
380        if !outputs.is_empty() {
381            return Err(parser::Error::new("constructors cannot have outputs"));
382        }
383        if anonymous {
384            return Err(parser::Error::new("constructors cannot be anonymous"));
385        }
386        Ok(Self { inputs, state_mutability: state_mutability.unwrap_or_default() })
387    }
388}
389
390impl FromStr for Error {
391    type Err = parser::Error;
392
393    #[inline]
394    fn from_str(s: &str) -> Result<Self, Self::Err> {
395        Self::parse(s)
396    }
397}
398
399impl Error {
400    /// Parses a Solidity error signature string: `$(error)? $name($($inputs),*)`
401    ///
402    /// If you want to parse a generic [Human-Readable ABI] string, use [`AbiItem::parse`].
403    ///
404    /// [Human-Readable ABI]: https://docs.ethers.org/v5/api/utils/abi/formats/#abi-formats--human-readable-abi
405    ///
406    /// # Examples
407    ///
408    /// Basic usage:
409    ///
410    /// ```
411    /// # use alloy_json_abi::{Error, Param, StateMutability};
412    /// assert_eq!(
413    ///     Error::parse("foo(bool bar)"),
414    ///     Ok(Error { name: "foo".to_string(), inputs: vec![Param::parse("bool bar").unwrap()] }),
415    /// );
416    /// ```
417    #[inline]
418    pub fn parse(s: &str) -> parser::Result<Self> {
419        parse_maybe_prefixed(s, "error", parse_sig::<false>).and_then(Self::parsed)
420    }
421
422    fn parsed(sig: ParsedSignature<Param>) -> parser::Result<Self> {
423        let ParsedSignature { name, inputs, outputs, anonymous, state_mutability } = sig;
424        if !outputs.is_empty() {
425            return Err(parser::Error::new("errors cannot have outputs"));
426        }
427        if anonymous {
428            return Err(parser::Error::new("errors cannot be anonymous"));
429        }
430        if state_mutability.is_some() {
431            return Err(parser::Error::new("errors cannot have mutability"));
432        }
433        Ok(Self { name, inputs })
434    }
435
436    /// Computes this error's signature: `$name($($inputs),*)`.
437    ///
438    /// This is the preimage input used to [compute the selector](Self::selector).
439    #[inline]
440    pub fn signature(&self) -> String {
441        signature(&self.name, &self.inputs, None)
442    }
443
444    /// Computes this error's selector: `keccak256(self.signature())[..4]`
445    #[inline]
446    pub fn selector(&self) -> Selector {
447        selector(&self.signature())
448    }
449}
450
451impl FromStr for Function {
452    type Err = parser::Error;
453
454    #[inline]
455    fn from_str(s: &str) -> Result<Self, Self::Err> {
456        Self::parse(s)
457    }
458}
459
460impl Function {
461    /// Parses a Solidity function signature string:
462    /// `$(function)? $name($($inputs),*) [visibility] [s_mutability] $(returns ($($outputs),+))?`
463    ///
464    /// Note:
465    /// - visibility is ignored
466    ///
467    /// If you want to parse a generic [Human-Readable ABI] string, use [`AbiItem::parse`].
468    ///
469    /// [Human-Readable ABI]: https://docs.ethers.org/v5/api/utils/abi/formats/#abi-formats--human-readable-abi
470    ///
471    /// # Examples
472    ///
473    /// Basic usage:
474    ///
475    /// ```
476    /// # use alloy_json_abi::{Function, Param, StateMutability};
477    /// assert_eq!(
478    ///     Function::parse("foo(bool bar)"),
479    ///     Ok(Function {
480    ///         name: "foo".to_string(),
481    ///         inputs: vec![Param::parse("bool bar").unwrap()],
482    ///         outputs: vec![],
483    ///         state_mutability: StateMutability::NonPayable,
484    ///     }),
485    /// );
486    /// ```
487    ///
488    /// [Function]s also support parsing output parameters:
489    ///
490    /// ```
491    /// # use alloy_json_abi::{Function, Param, StateMutability};
492    /// assert_eq!(
493    ///     Function::parse("function toString(uint number) external view returns (string s)"),
494    ///     Ok(Function {
495    ///         name: "toString".to_string(),
496    ///         inputs: vec![Param::parse("uint number").unwrap()],
497    ///         outputs: vec![Param::parse("string s").unwrap()],
498    ///         state_mutability: StateMutability::View,
499    ///     }),
500    /// );
501    /// ```
502    #[inline]
503    pub fn parse(s: &str) -> parser::Result<Self> {
504        parse_maybe_prefixed(s, "function", parse_sig::<true>).and_then(Self::parsed)
505    }
506
507    fn parsed(sig: ParsedSignature<Param>) -> parser::Result<Self> {
508        let ParsedSignature { name, inputs, outputs, anonymous, state_mutability } = sig;
509        if anonymous {
510            return Err(parser::Error::new("functions cannot be anonymous"));
511        }
512        Ok(Self { name, inputs, outputs, state_mutability: state_mutability.unwrap_or_default() })
513    }
514
515    /// Returns this function's signature: `$name($($inputs),*)`.
516    ///
517    /// This is the preimage input used to [compute the selector](Self::selector).
518    #[inline]
519    pub fn signature(&self) -> String {
520        signature(&self.name, &self.inputs, None)
521    }
522
523    /// Returns this function's full signature:
524    /// `$name($($inputs),*)($(outputs),*)`.
525    ///
526    /// This is the same as [`signature`](Self::signature), but also includes
527    /// the output types.
528    #[inline]
529    pub fn signature_with_outputs(&self) -> String {
530        signature(&self.name, &self.inputs, Some(&self.outputs))
531    }
532
533    /// Returns this function's full signature including names of params:
534    /// `function $name($($inputs $names),*) state_mutability returns ($($outputs $names),*)`.
535    ///
536    /// This is a full human-readable string, including all parameter names, any optional modifiers
537    /// (e.g. view, payable, pure) and white-space to aid in human readability. This is useful for
538    /// storing a string which can still fully reconstruct the original Fragment
539    #[inline]
540    pub fn full_signature(&self) -> String {
541        full_signature(&self.name, &self.inputs, Some(&self.outputs), self.state_mutability)
542    }
543
544    /// Computes this error's selector: `keccak256(self.signature())[..4]`
545    #[inline]
546    pub fn selector(&self) -> Selector {
547        selector(&self.signature())
548    }
549}
550
551impl FromStr for Event {
552    type Err = parser::Error;
553
554    #[inline]
555    fn from_str(s: &str) -> Result<Self, Self::Err> {
556        Self::parse(s)
557    }
558}
559
560impl Event {
561    /// Parses a Solidity event signature string: `$(event)? $name($($inputs),*) $(anonymous)?`
562    ///
563    /// If you want to parse a generic [Human-Readable ABI] string, use [`AbiItem::parse`].
564    ///
565    /// [Human-Readable ABI]: https://docs.ethers.org/v5/api/utils/abi/formats/#abi-formats--human-readable-abi
566    ///
567    /// # Examples
568    ///
569    /// ```
570    /// # use alloy_json_abi::{Event, EventParam};
571    /// assert_eq!(
572    ///     Event::parse("event foo(bool bar, uint indexed baz)"),
573    ///     Ok(Event {
574    ///         name: "foo".to_string(),
575    ///         inputs: vec![
576    ///             EventParam::parse("bool bar").unwrap(),
577    ///             EventParam::parse("uint indexed baz").unwrap()
578    ///         ],
579    ///         anonymous: false,
580    ///     }),
581    /// );
582    /// ```
583    #[inline]
584    pub fn parse(s: &str) -> parser::Result<Self> {
585        parse_maybe_prefixed(s, "event", parse_event_sig).and_then(Self::parsed)
586    }
587
588    fn parsed(sig: ParsedSignature<EventParam>) -> parser::Result<Self> {
589        let ParsedSignature { name, inputs, outputs, anonymous, state_mutability } = sig;
590        if !outputs.is_empty() {
591            return Err(parser::Error::new("events cannot have outputs"));
592        }
593        if state_mutability.is_some() {
594            return Err(parser::Error::new("events cannot have state mutability"));
595        }
596        Ok(Self { name, inputs, anonymous })
597    }
598
599    /// Returns this event's signature: `$name($($inputs),*)`.
600    ///
601    /// This is the preimage input used to [compute the selector](Self::selector).
602    #[inline]
603    pub fn signature(&self) -> String {
604        event_signature(&self.name, &self.inputs)
605    }
606
607    /// Returns this event's full signature
608    /// `event $name($($inputs indexed $names),*)`.
609    ///
610    /// This is a full human-readable string, including all parameter names, any optional modifiers
611    /// (e.g. indexed) and white-space to aid in human readability. This is useful for
612    /// storing a string which can still fully reconstruct the original Fragment
613    #[inline]
614    pub fn full_signature(&self) -> String {
615        event_full_signature(&self.name, &self.inputs)
616    }
617
618    /// Computes this event's selector: `keccak256(self.signature())`
619    #[inline]
620    pub fn selector(&self) -> B256 {
621        keccak256(self.signature().as_bytes())
622    }
623
624    /// Computes the number of this event's indexed topics.
625    #[inline]
626    pub fn num_topics(&self) -> usize {
627        !self.anonymous as usize + self.inputs.iter().filter(|input| input.indexed).count()
628    }
629}
630
631#[cfg(test)]
632mod tests {
633    use super::*;
634
635    // fn param(kind: &str) -> Param {
636    //     param2(kind, "param")
637    // }
638
639    fn param2(kind: &str, name: &str) -> Param {
640        Param { ty: kind.into(), name: name.into(), internal_type: None, components: vec![] }
641    }
642
643    #[test]
644    fn parse_prefixes() {
645        for prefix in ["function", "error", "event"] {
646            let name = "foo";
647            let name1 = format!("{prefix} {name}");
648            let name2 = format!("{prefix}{name}");
649            assert_eq!(AbiItem::parse(&format!("{name1}()")).unwrap().name().unwrap(), name);
650            assert!(AbiItem::parse(&format!("{name2}()")).is_err());
651        }
652    }
653
654    #[test]
655    fn parse_function_prefix() {
656        let new = |name: &str| Function {
657            name: name.into(),
658            inputs: vec![],
659            outputs: vec![],
660            state_mutability: StateMutability::NonPayable,
661        };
662        assert_eq!(Function::parse("foo()"), Ok(new("foo")));
663        assert_eq!(Function::parse("function foo()"), Ok(new("foo")));
664        assert_eq!(Function::parse("functionfoo()"), Ok(new("functionfoo")));
665        assert_eq!(Function::parse("function functionfoo()"), Ok(new("functionfoo")));
666    }
667
668    #[test]
669    fn parse_event_prefix() {
670        let new = |name: &str| Event { name: name.into(), inputs: vec![], anonymous: false };
671        assert_eq!(Event::parse("foo()"), Ok(new("foo")));
672        assert_eq!(Event::parse("event foo()"), Ok(new("foo")));
673        assert_eq!(Event::parse("eventfoo()"), Ok(new("eventfoo")));
674        assert_eq!(Event::parse("event eventfoo()"), Ok(new("eventfoo")));
675    }
676
677    #[test]
678    fn parse_error_prefix() {
679        let new = |name: &str| Error { name: name.into(), inputs: vec![] };
680        assert_eq!(Error::parse("foo()"), Ok(new("foo")));
681        assert_eq!(Error::parse("error foo()"), Ok(new("foo")));
682        assert_eq!(Error::parse("errorfoo()"), Ok(new("errorfoo")));
683        assert_eq!(Error::parse("error errorfoo()"), Ok(new("errorfoo")));
684    }
685
686    #[test]
687    fn parse_full() {
688        // https://github.com/alloy-rs/core/issues/389
689        assert_eq!(
690            Function::parse("function foo(uint256 a, uint256 b) external returns (uint256)"),
691            Ok(Function {
692                name: "foo".into(),
693                inputs: vec![param2("uint256", "a"), param2("uint256", "b")],
694                outputs: vec![param2("uint256", "")],
695                state_mutability: StateMutability::NonPayable,
696            })
697        );
698
699        // https://github.com/alloy-rs/core/issues/681
700        assert_eq!(
701            Function::parse("function balanceOf(address owner) view returns (uint256 balance)"),
702            Ok(Function {
703                name: "balanceOf".into(),
704                inputs: vec![param2("address", "owner")],
705                outputs: vec![param2("uint256", "balance")],
706                state_mutability: StateMutability::View,
707            })
708        );
709    }
710
711    // https://github.com/alloy-rs/core/issues/702
712    #[test]
713    fn parse_stack_overflow() {
714        let s = "error  J((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((";
715        AbiItem::parse(s).unwrap_err();
716    }
717}