Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(json-abi): clean up utils #794

Merged
merged 1 commit into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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