diff --git a/crates/json-abi/src/item.rs b/crates/json-abi/src/item.rs index dc8023642..89ed2a119 100644 --- a/crates/json-abi/src/item.rs +++ b/crates/json-abi/src/item.rs @@ -90,7 +90,7 @@ abi_items! { /// A JSON ABI function. pub struct Function: "function" { /// The name of the function. - #[serde(deserialize_with = "validate_identifier")] + #[serde(deserialize_with = "validated_identifier")] pub name: String, /// The input types of the function. May be empty. pub inputs: Vec, @@ -104,7 +104,7 @@ abi_items! { /// A JSON ABI event. pub struct Event: "event" { /// The name of the event. - #[serde(deserialize_with = "validate_identifier")] + #[serde(deserialize_with = "validated_identifier")] pub name: String, /// A list of the event's inputs, in order. pub inputs: Vec, @@ -117,7 +117,7 @@ abi_items! { /// A JSON ABI error. pub struct Error: "error" { /// The name of the error. - #[serde(deserialize_with = "validate_identifier")] + #[serde(deserialize_with = "validated_identifier")] pub name: String, /// A list of the error's components, in order. pub inputs: Vec, @@ -125,9 +125,9 @@ abi_items! { } #[inline] -fn validate_identifier<'de, D: Deserializer<'de>>(deserializer: D) -> Result { +fn validated_identifier<'de, D: Deserializer<'de>>(deserializer: D) -> Result { let s = String::deserialize(deserializer)?; - validate_identifier!(&s); + validate_identifier(&s)?; Ok(s) } diff --git a/crates/json-abi/src/param.rs b/crates/json-abi/src/param.rs index 8e0334177..b44e13771 100644 --- a/crates/json-abi/src/param.rs +++ b/crates/json-abi/src/param.rs @@ -223,7 +223,7 @@ impl Param { if self.components.is_empty() { s.push_str(&self.ty); } else { - crate::utils::signature_raw(&self.components, s); + crate::utils::params_abi_tuple(&self.components, s); // checked during deserialization, but might be invalid from a user if let Some(suffix) = self.ty.strip_prefix("tuple") { s.push_str(suffix); @@ -239,7 +239,7 @@ impl Param { s.push_str(&self.ty); } else { s.push_str("tuple"); - crate::utils::full_signature_raw(&self.components, s); + crate::utils::params_tuple(&self.components, s); // checked during deserialization, but might be invalid from a user if let Some(suffix) = self.ty.strip_prefix("tuple") { s.push_str(suffix); @@ -516,7 +516,7 @@ impl EventParam { if self.components.is_empty() { s.push_str(&self.ty); } else { - crate::utils::signature_raw(&self.components, s); + crate::utils::params_abi_tuple(&self.components, s); // checked during deserialization, but might be invalid from a user if let Some(suffix) = self.ty.strip_prefix("tuple") { s.push_str(suffix); @@ -532,7 +532,7 @@ impl EventParam { s.push_str(&self.ty); } else { s.push_str("tuple"); - crate::utils::full_signature_raw(&self.components, s); + crate::utils::params_tuple(&self.components, s); // checked during deserialization, but might be invalid from a user if let Some(suffix) = self.ty.strip_prefix("tuple") { s.push_str(suffix); @@ -626,7 +626,7 @@ struct BorrowedParamInner<'a> { impl BorrowedParamInner<'_> { fn validate_fields(&self) -> Result<(), E> { - validate_identifier!(self.name); + validate_identifier(self.name)?; // any components means type is "tuple" + maybe brackets, so we can skip // parsing with TypeSpecifier diff --git a/crates/json-abi/src/utils.rs b/crates/json-abi/src/utils.rs index 8c2197b5c..468822768 100644 --- a/crates/json-abi/src/utils.rs +++ b/crates/json-abi/src/utils.rs @@ -1,74 +1,23 @@ use crate::{EventParam, Param, StateMutability}; -use alloc::string::{String, ToString}; +use alloc::string::String; use alloy_primitives::Selector; use core::{fmt::Write, num::NonZeroUsize}; use parser::{utils::ParsedSignature, ParameterSpecifier, TypeSpecifier, TypeStem}; /// Capacity to allocate per [Param]. -const PARAM: usize = 32; - -macro_rules! validate_identifier { - ($name:expr) => { - if !$name.is_empty() && !parser::is_valid_identifier($name) { - return Err(serde::de::Error::invalid_value( - serde::de::Unexpected::Str($name), - &"a valid Solidity identifier", - )); - } - }; -} -pub(crate) use validate_identifier; +const PARAM_CAP: usize = 32; /// `($($params),*)` -macro_rules! signature { - ($inputs:expr, $preimage:expr) => { - $preimage.push('('); - for (i, input) in $inputs.iter().enumerate() { - if i > 0 { - $preimage.push(','); - } - input.selector_type_raw($preimage); - } - $preimage.push(')'); - }; -} - -macro_rules! event_full_signature { - ($inputs:expr, $preimage:expr) => { - $preimage.push('('); +macro_rules! params_abi_tuple { + ($inputs:expr, $s:expr) => { + $s.push('('); for (i, input) in $inputs.iter().enumerate() { if i > 0 { - $preimage.push(','); - $preimage.push(' '); - } - input.full_selector_type_raw($preimage); - if input.indexed { - $preimage.push_str(" indexed"); - } - if !input.name.is_empty() { - $preimage.push(' '); - $preimage.push_str(&input.name); - } - } - $preimage.push(')'); - }; -} - -macro_rules! full_signature { - ($inputs:expr, $preimage:expr) => { - $preimage.push('('); - for (i, input) in $inputs.iter().enumerate() { - if i > 0 { - $preimage.push(','); - $preimage.push(' '); - } - input.full_selector_type_raw($preimage); - if !input.name.is_empty() { - $preimage.push(' '); - $preimage.push_str(&input.name); + $s.push(','); } + input.selector_type_raw($s); } - $preimage.push(')'); + $s.push(')'); }; } @@ -76,14 +25,14 @@ macro_rules! full_signature { pub(crate) fn signature(name: &str, inputs: &[Param], outputs: Option<&[Param]>) -> String { let parens = 2 + outputs.is_some() as usize * 2; let n_outputs = outputs.map(<[_]>::len).unwrap_or(0); - let cap = name.len() + parens + (inputs.len() + n_outputs) * PARAM; - let mut preimage = String::with_capacity(cap); - preimage.push_str(name); - signature_raw(inputs, &mut preimage); + let cap = name.len() + parens + (inputs.len() + n_outputs) * PARAM_CAP; + let mut sig = String::with_capacity(cap); + sig.push_str(name); + params_abi_tuple(inputs, &mut sig); if let Some(outputs) = outputs { - signature_raw(outputs, &mut preimage); + params_abi_tuple(outputs, &mut sig); } - preimage + sig } pub(crate) fn full_signature( @@ -94,67 +43,83 @@ pub(crate) fn full_signature( ) -> String { let parens = 2 + outputs.is_some() as usize * 2; let n_outputs = outputs.map(<[_]>::len).unwrap_or(0); - let mut state_mutability_str = format!(" {}", state_mutability.as_str().unwrap_or_default()); - if state_mutability_str.trim().is_empty() { - state_mutability_str = "".to_string(); - } + let state_mutability_str = state_mutability.as_str(); let cap = "function ".len() + name.len() + parens - + (inputs.len() + n_outputs) * PARAM - + state_mutability_str.len(); - let mut preimage = String::with_capacity(cap); - - preimage.push_str("function "); - preimage.push_str(name); - full_signature_raw(inputs, &mut preimage); - preimage.push_str(&state_mutability_str); + + (inputs.len() + n_outputs) * PARAM_CAP + + state_mutability_str.map(|s| s.len() + 1).unwrap_or(0); + let mut sig = String::with_capacity(cap); + sig.push_str("function "); + sig.push_str(name); + params_tuple(inputs, &mut sig); + if let Some(state_mutability_str) = state_mutability_str { + sig.push(' '); + sig.push_str(state_mutability_str); + } if let Some(outputs) = outputs { if !outputs.is_empty() { - preimage.push_str(" returns "); - full_signature_raw(outputs, &mut preimage); + sig.push_str(" returns "); + params_tuple(outputs, &mut sig); } } - preimage + sig } /// `($($params),*)` -pub(crate) fn signature_raw(params: &[Param], preimage: &mut String) { - signature!(params, preimage); +pub(crate) fn params_abi_tuple(params: &[Param], s: &mut String) { + params_abi_tuple!(params, s); } -pub(crate) fn full_signature_raw(params: &[Param], preimage: &mut String) { - full_signature!(params, preimage); +pub(crate) fn params_tuple(params: &[Param], s: &mut String) { + s.push('('); + for (i, input) in params.iter().enumerate() { + if i > 0 { + s.push_str(", "); + } + input.full_selector_type_raw(s); + if !input.name.is_empty() { + s.push(' '); + s.push_str(&input.name); + } + } + s.push(')'); } /// `$name($($inputs),*)` pub(crate) fn event_signature(name: &str, inputs: &[EventParam]) -> String { - let mut preimage = String::with_capacity(name.len() + 2 + inputs.len() * PARAM); + let mut preimage = String::with_capacity(name.len() + 2 + inputs.len() * PARAM_CAP); preimage.push_str(name); - signature!(inputs, &mut preimage); + params_abi_tuple!(inputs, &mut preimage); preimage } /// `$name($($inputs indexed names),*)` pub(crate) fn event_full_signature(name: &str, inputs: &[EventParam]) -> String { - let mut preimage = - String::with_capacity("event ".len() + name.len() + 2 + inputs.len() * PARAM); - preimage.push_str("event "); - preimage.push_str(name); - event_full_signature!(inputs, &mut preimage); - preimage + let mut sig = String::with_capacity("event ".len() + name.len() + 2 + inputs.len() * PARAM_CAP); + sig.push_str("event "); + sig.push_str(name); + sig.push('('); + for (i, input) in inputs.iter().enumerate() { + if i > 0 { + sig.push_str(", "); + } + input.full_selector_type_raw(&mut sig); + if input.indexed { + sig.push_str(" indexed"); + } + if !input.name.is_empty() { + sig.push(' '); + sig.push_str(&input.name); + } + } + sig.push(')'); + sig } /// `keccak256(preimage)[..4]` pub(crate) fn selector(preimage: &str) -> Selector { - // SAFETY: splitting an array - unsafe { - alloy_primitives::keccak256(preimage.as_bytes()) - .0 - .get_unchecked(..4) - .try_into() - .unwrap_unchecked() - } + alloy_primitives::keccak256(preimage.as_bytes())[..4].try_into().unwrap() } /// Strips `prefix` from `s` before parsing with `parser`. `prefix` must be followed by whitespace. @@ -221,6 +186,16 @@ fn ty_string(s: &str, sizes: &[Option]) -> String { ty } +pub(crate) fn validate_identifier(name: &str) -> Result<(), E> { + if !name.is_empty() && !parser::is_valid_identifier(name) { + return Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Str(name), + &"a valid Solidity identifier", + )); + } + Ok(()) +} + #[cfg(test)] mod tests { use super::*;