Skip to content

Commit

Permalink
chore(json-abi): clean up utils (#794)
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniPopes authored Oct 30, 2024
1 parent 6e962a8 commit 0cc2710
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 110 deletions.
10 changes: 5 additions & 5 deletions crates/json-abi/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Param>,
Expand All @@ -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<EventParam>,
Expand All @@ -117,17 +117,17 @@ 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<Param>,
}
}

#[inline]
fn validate_identifier<'de, D: Deserializer<'de>>(deserializer: D) -> Result<String, D::Error> {
fn validated_identifier<'de, D: Deserializer<'de>>(deserializer: D) -> Result<String, D::Error> {
let s = String::deserialize(deserializer)?;
validate_identifier!(&s);
validate_identifier(&s)?;
Ok(s)
}

Expand Down
10 changes: 5 additions & 5 deletions crates/json-abi/src/param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -626,7 +626,7 @@ struct BorrowedParamInner<'a> {

impl BorrowedParamInner<'_> {
fn validate_fields<E: serde::de::Error>(&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
Expand Down
175 changes: 75 additions & 100 deletions crates/json-abi/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,89 +1,38 @@
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(')');
};
}

/// `$name($($inputs),*)($($outputs),*)`
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(
Expand All @@ -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.
Expand Down Expand Up @@ -221,6 +186,16 @@ fn ty_string(s: &str, sizes: &[Option<NonZeroUsize>]) -> String {
ty
}

pub(crate) fn validate_identifier<E: serde::de::Error>(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::*;
Expand Down

0 comments on commit 0cc2710

Please sign in to comment.