Skip to content

Commit

Permalink
feat: add parsing support for JSON items
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniPopes committed Oct 3, 2023
1 parent 1077e96 commit b85006f
Show file tree
Hide file tree
Showing 24 changed files with 871 additions and 177 deletions.
2 changes: 1 addition & 1 deletion crates/dyn-abi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "alloy-dyn-abi"
description = "Run-time ABI and EIP-712 implementations"
keywords = ["ethereum", "abi", "encoding", "EVM", "solidity"]
keywords = ["ethereum", "abi", "encoding", "evm", "solidity"]
categories = ["no-std", "encoding", "cryptography::cryptocurrencies"]
homepage = "https://github.com/alloy-rs/core/tree/main/crates/dyn-abi"

Expand Down
53 changes: 38 additions & 15 deletions crates/dyn-abi/src/eip712/parser.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! EIP-712 specific parsing structures.

// TODO: move to `sol-type-parser`

use crate::{
eip712::resolver::{PropertyDef, TypeDef},
Error,
Expand Down Expand Up @@ -27,7 +29,15 @@ impl PropDef<'_> {
impl<'a> TryFrom<&'a str> for PropDef<'a> {
type Error = Error;

#[inline]
fn try_from(input: &'a str) -> Result<Self, Self::Error> {
Self::parse(input)
}
}

impl<'a> PropDef<'a> {
/// Parse a string into property definition.
pub fn parse(input: &'a str) -> Result<Self, Error> {
let (ty, name) = input
.rsplit_once(' ')
.ok_or_else(|| Error::invalid_property_def(input))?;
Expand All @@ -51,22 +61,18 @@ pub struct ComponentType<'a> {
pub props: Vec<PropDef<'a>>,
}

impl ComponentType<'_> {
/// Convert to an owned TypeDef.
pub fn to_owned(&self) -> TypeDef {
TypeDef::new(
self.type_name,
self.props.iter().map(|p| p.to_owned()).collect(),
)
.unwrap()
}
}

// This impl handles
impl<'a> TryFrom<&'a str> for ComponentType<'a> {
type Error = Error;

#[inline]
fn try_from(input: &'a str) -> Result<Self, Self::Error> {
Self::parse(input)
}
}

impl<'a> ComponentType<'a> {
/// Parse a string into a component type.
pub fn parse(input: &'a str) -> Result<Self, Error> {
let (name, props_str) = input
.split_once('(')
.ok_or_else(|| Error::TypeParser(TypeParserError::invalid_type_string(input)))?;
Expand Down Expand Up @@ -102,6 +108,15 @@ impl<'a> TryFrom<&'a str> for ComponentType<'a> {
props,
})
}

/// Convert to an owned TypeDef.
pub fn to_owned(&self) -> TypeDef {
TypeDef::new(
self.type_name,
self.props.iter().map(|p| p.to_owned()).collect(),
)
.unwrap()
}
}

/// Represents a list of component types in an EIP-712 `encodeType` type string.
Expand All @@ -114,11 +129,19 @@ pub struct EncodeType<'a> {
impl<'a> TryFrom<&'a str> for EncodeType<'a> {
type Error = Error;

#[inline]
fn try_from(input: &'a str) -> Result<Self, Self::Error> {
Self::parse(input)
}
}

impl<'a> EncodeType<'a> {
/// Parse a string into a list of component types.
pub fn parse(input: &'a str) -> Result<Self, Error> {
let mut types = vec![];
let mut remaining = input;

while let Ok(t) = ComponentType::try_from(remaining) {
while let Ok(t) = ComponentType::parse(remaining) {
remaining = &remaining[t.span.len()..];
types.push(t);
}
Expand All @@ -136,7 +159,7 @@ mod tests {
#[test]
fn test_component_type() {
assert_eq!(
ComponentType::try_from("Transaction(Person from,Person to,Asset tx)"),
ComponentType::parse("Transaction(Person from,Person to,Asset tx)"),
Ok(ComponentType {
span: "Transaction(Person from,Person to,Asset tx)",
type_name: "Transaction",
Expand All @@ -152,7 +175,7 @@ mod tests {
#[test]
fn test_encode_type() {
assert_eq!(
EncodeType::try_from(EXAMPLE),
EncodeType::parse(EXAMPLE),
Ok(EncodeType {
types: vec![
"Transaction(Person from,Person to,Asset tx)"
Expand Down
4 changes: 2 additions & 2 deletions crates/dyn-abi/src/eip712/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl PropertyDef {
N: Into<String>,
{
let type_name = type_name.into();
TypeSpecifier::try_from(type_name.as_str())?;
TypeSpecifier::parse(type_name.as_str())?;
Ok(Self::new_unchecked(type_name, name))
}

Expand Down Expand Up @@ -121,7 +121,7 @@ impl TypeDef {
#[inline]
pub fn new<S: Into<String>>(type_name: S, props: Vec<PropertyDef>) -> Result<Self> {
let type_name = type_name.into();
RootType::try_from(type_name.as_str())?;
RootType::parse(type_name.as_str())?;
Ok(Self { type_name, props })
}

Expand Down
100 changes: 33 additions & 67 deletions crates/dyn-abi/src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ use crate::{DynSolType, Result};
use alloc::vec::Vec;
use alloy_json_abi::{EventParam, Param};
use alloy_sol_type_parser::{
Error as TypeStrError, RootType, TupleSpecifier, TypeSpecifier, TypeStem,
Error as ParserError, ParameterSpecifier, Parameters, RootType, TupleSpecifier, TypeSpecifier,
TypeStem,
};

#[cfg(feature = "eip712")]
use alloy_json_abi::InternalType;

/// Types that can be resolved into a [`DynSolType`].
///
/// ABI and related systems have many different ways of encoding solidity types.
/// ABI and related systems have many different ways of encoding Solidity types.
/// This trait provides a single pattern for resolving those encodings into
/// solidity types.
/// Solidity types.
///
/// This trait is implemented for all the [`alloy_sol_type_parser`] types, the
/// [`Param`] and [`EventParam`] structs, and [`str`].
Expand All @@ -28,10 +29,10 @@ use alloy_json_abi::InternalType;
/// ```
/// # use alloy_dyn_abi::{DynSolType, ResolveSolType};
/// # use alloy_sol_type_parser::{RootType, TypeSpecifier};
/// let my_ty = TypeSpecifier::try_from("bool")?.resolve()?;
/// let my_ty = TypeSpecifier::parse("bool")?.resolve()?;
/// assert_eq!(my_ty, DynSolType::Bool);
///
/// let my_ty = RootType::try_from("uint256")?.resolve()?;
/// let my_ty = RootType::parse("uint256")?.resolve()?;
/// assert_eq!(my_ty, DynSolType::Uint(256));
///
/// assert_eq!("bytes32".resolve()?, DynSolType::FixedBytes(32));
Expand Down Expand Up @@ -68,7 +69,7 @@ impl ResolveSolType for RootType<'_> {
return Ok(DynSolType::FixedBytes(sz))
}
}
return Err(TypeStrError::invalid_size(name).into())
return Err(ParserError::invalid_size(name).into())
}

// fast path both integer types
Expand All @@ -88,9 +89,9 @@ impl ResolveSolType for RootType<'_> {
}
}
}
Err(TypeStrError::invalid_size(name).into())
Err(ParserError::invalid_size(name).into())
} else {
Err(TypeStrError::invalid_type_string(name).into())
Err(ParserError::invalid_type_string(name).into())
}
}
}
Expand All @@ -100,11 +101,7 @@ impl ResolveSolType for RootType<'_> {
impl ResolveSolType for TupleSpecifier<'_> {
#[inline]
fn resolve(&self) -> Result<DynSolType> {
self.types
.iter()
.map(TypeSpecifier::resolve)
.collect::<Result<Vec<_>, _>>()
.map(DynSolType::Tuple)
tuple(&self.types).map(DynSolType::Tuple)
}
}

Expand All @@ -126,6 +123,20 @@ impl ResolveSolType for TypeSpecifier<'_> {
}
}

impl ResolveSolType for ParameterSpecifier<'_> {
#[inline]
fn resolve(&self) -> Result<DynSolType> {
self.ty.resolve()
}
}

impl ResolveSolType for Parameters<'_> {
#[inline]
fn resolve(&self) -> Result<DynSolType> {
tuple(&self.params).map(DynSolType::Tuple)
}
}

impl ResolveSolType for Param {
#[inline]
fn resolve(&self) -> Result<DynSolType> {
Expand Down Expand Up @@ -163,10 +174,7 @@ fn resolve_param(
}

// type is complex
let tuple = components
.iter()
.map(Param::resolve)
.collect::<Result<Vec<_>, _>>()?;
let tuple = tuple(components)?;

#[cfg(feature = "eip712")]
let resolved = if let Some((_, name)) = it.and_then(|i| i.as_struct()) {
Expand All @@ -186,6 +194,14 @@ fn resolve_param(
Ok(resolved.array_wrap_from_iter(ty.sizes))
}

fn tuple<T: ResolveSolType>(slice: &[T]) -> Result<Vec<DynSolType>> {
let mut types = Vec::with_capacity(slice.len());
for ty in slice {
types.push(ty.resolve()?);
}
Ok(types)
}

macro_rules! deref_impl {
($($(#[$attr:meta])* [$($gen:tt)*] $t:ty),+ $(,)?) => {$(
$(#[$attr])*
Expand Down Expand Up @@ -348,54 +364,4 @@ mod tests {
))
);
}

#[test]
fn try_basic_solidity() {
assert_eq!(
TypeSpecifier::try_from("uint256")
.unwrap()
.try_basic_solidity(),
Ok(())
);
assert_eq!(
TypeSpecifier::try_from("uint256[]")
.unwrap()
.try_basic_solidity(),
Ok(())
);
assert_eq!(
TypeSpecifier::try_from("(uint256,uint256)")
.unwrap()
.try_basic_solidity(),
Ok(())
);
assert_eq!(
TypeSpecifier::try_from("(uint256,uint256)[2]")
.unwrap()
.try_basic_solidity(),
Ok(())
);
assert_eq!(
TypeSpecifier::try_from("tuple(uint256,uint256)")
.unwrap()
.try_basic_solidity(),
Ok(())
);
assert_eq!(
TypeSpecifier::try_from("tuple(address,bytes,(bool,(string,uint256)[][3]))[2]")
.unwrap()
.try_basic_solidity(),
Ok(())
);
}

#[test]
fn not_basic_solidity() {
assert_eq!(
TypeSpecifier::try_from("MyStruct")
.unwrap()
.try_basic_solidity(),
Err(TypeStrError::invalid_type_string("MyStruct"))
);
}
}
2 changes: 1 addition & 1 deletion crates/dyn-abi/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ impl<'a> DynToken<'a> {
return Ok(())
}

// This appears to be an unclarity in the solidity spec. The
// This appears to be an unclarity in the Solidity spec. The
// spec specifies that offsets are relative to the beginning of
// `enc(X)`. But known-good test vectors have it relative to the
// word AFTER the array size
Expand Down
4 changes: 2 additions & 2 deletions crates/dyn-abi/src/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ impl DynSolType {
/// ```
#[inline]
pub fn parse(s: &str) -> Result<Self> {
TypeSpecifier::try_from(s)
TypeSpecifier::parse(s)
.map_err(Error::TypeParser)
.and_then(|t| t.resolve())
}
Expand Down Expand Up @@ -439,7 +439,7 @@ impl DynSolType {
}
}

/// The Solidity type name. This returns the solidity type corresponding to
/// The Solidity type name. This returns the Solidity type corresponding to
/// this value, if it is known. A type will not be known if the value
/// contains an empty sequence, e.g. `T[0]`.
pub fn sol_type_name(&self) -> Cow<'_, str> {
Expand Down
4 changes: 2 additions & 2 deletions crates/dyn-abi/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ impl From<U256> for DynSolValue {
}

impl DynSolValue {
/// The Solidity type. This returns the solidity type corresponding to this
/// The Solidity type. This returns the Solidity type corresponding to this
/// value, if it is known. A type will not be known if the value contains
/// an empty sequence, e.g. `T[0]`.
pub fn as_type(&self) -> Option<DynSolType> {
Expand Down Expand Up @@ -297,7 +297,7 @@ impl DynSolValue {
true
}

/// The Solidity type name. This returns the solidity type corresponding to
/// The Solidity type name. This returns the Solidity type corresponding to
/// this value, if it is known. A type will not be known if the value
/// contains an empty sequence, e.g. `T[0]`.
pub fn sol_type_name(&self) -> Option<Cow<'_, str>> {
Expand Down
2 changes: 1 addition & 1 deletion crates/json-abi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "alloy-json-abi"
description = "Full Ethereum JSON-ABI implementation"
keywords = ["ethereum", "abi", "serialization"]
keywords = ["ethereum", "abi", "json", "serde", "serialization"]
categories = ["encoding", "cryptography::cryptocurrencies"]
homepage = "https://github.com/alloy-rs/core/tree/main/crates/json-abi"

Expand Down
Loading

0 comments on commit b85006f

Please sign in to comment.