diff --git a/crates/dyn-abi/src/arbitrary.rs b/crates/dyn-abi/src/arbitrary.rs index 7baa07032f..a27219f8cf 100644 --- a/crates/dyn-abi/src/arbitrary.rs +++ b/crates/dyn-abi/src/arbitrary.rs @@ -10,7 +10,7 @@ #![allow(clippy::arc_with_non_send_sync)] use crate::{DynSolType, DynSolValue}; -use alloy_primitives::{Address, B256, I256, U256}; +use alloy_primitives::{Address, Function, B256, I256, U256}; use arbitrary::{size_hint, Unstructured}; use core::ops::RangeInclusive; use proptest::{ @@ -133,6 +133,7 @@ enum Choice { Int, Uint, Address, + Function, FixedBytes, Bytes, String, @@ -151,6 +152,7 @@ impl<'a> arbitrary::Arbitrary<'a> for DynSolType { Choice::Int => u.arbitrary().map(int_size).map(Self::Int), Choice::Uint => u.arbitrary().map(int_size).map(Self::Uint), Choice::Address => Ok(Self::Address), + Choice::Function => Ok(Self::Function), Choice::FixedBytes => Ok(Self::FixedBytes(u.int_in_range(1..=32)?)), Choice::Bytes => Ok(Self::Bytes), Choice::String => Ok(Self::String), @@ -359,6 +361,7 @@ impl DynSolValue { match ty { DynSolType::Bool => u.arbitrary().map(Self::Bool), DynSolType::Address => u.arbitrary().map(Self::Address), + DynSolType::Function => u.arbitrary().map(Self::Function), &DynSolType::Int(sz) => u.arbitrary().map(|x| Self::Int(x, sz)), &DynSolType::Uint(sz) => u.arbitrary().map(|x| Self::Uint(x, sz)), &DynSolType::FixedBytes(sz) => u.arbitrary().map(|x| Self::FixedBytes(x, sz)), @@ -410,6 +413,7 @@ impl DynSolValue { match ty { DynSolType::Bool => any::().prop_map(Self::Bool).sboxed(), DynSolType::Address => any::
().prop_map(Self::Address).sboxed(), + DynSolType::Function => any::().prop_map(Self::Function).sboxed(), &DynSolType::Int(sz) => any::().prop_map(move |x| Self::Int(x, sz)).sboxed(), &DynSolType::Uint(sz) => any::().prop_map(move |x| Self::Uint(x, sz)).sboxed(), &DynSolType::FixedBytes(sz) => any::() diff --git a/crates/dyn-abi/src/eip712/coerce.rs b/crates/dyn-abi/src/eip712/coerce.rs index 014d80f005..bff239c2a8 100644 --- a/crates/dyn-abi/src/eip712/coerce.rs +++ b/crates/dyn-abi/src/eip712/coerce.rs @@ -4,13 +4,14 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; -use alloy_primitives::{Address, I256, U256}; +use alloy_primitives::{Address, Function, I256, U256}; impl DynSolType { /// Coerce a [`serde_json::Value`] to a [`DynSolValue`] via this type. pub fn coerce(&self, value: &serde_json::Value) -> DynAbiResult { match self { DynSolType::Address => address(value), + DynSolType::Function => function(value), DynSolType::Bool => bool(value), DynSolType::Int(n) => int(*n, value), DynSolType::Uint(n) => uint(*n, value), @@ -41,6 +42,18 @@ fn address(value: &serde_json::Value) -> DynAbiResult { Ok(DynSolValue::Address(address)) } +fn function(value: &serde_json::Value) -> DynAbiResult { + let function = value + .as_str() + .map(|s| { + s.parse::() + .map_err(|_| DynAbiError::type_mismatch(DynSolType::Function, value)) + }) + .ok_or_else(|| DynAbiError::type_mismatch(DynSolType::Function, value))??; + + Ok(DynSolValue::Function(function)) +} + fn bool(value: &serde_json::Value) -> DynAbiResult { if let Some(bool) = value.as_bool() { return Ok(DynSolValue::Bool(bool)) diff --git a/crates/dyn-abi/src/resolve.rs b/crates/dyn-abi/src/resolve.rs index f9085c7f61..4223c319be 100644 --- a/crates/dyn-abi/src/resolve.rs +++ b/crates/dyn-abi/src/resolve.rs @@ -51,6 +51,7 @@ impl ResolveSolType for RootType<'_> { fn resolve(&self) -> DynAbiResult { match self.span() { "address" => Ok(DynSolType::Address), + "function" => Ok(DynSolType::Function), "bool" => Ok(DynSolType::Bool), "string" => Ok(DynSolType::String), "bytes" => Ok(DynSolType::Bytes), diff --git a/crates/dyn-abi/src/ty.rs b/crates/dyn-abi/src/ty.rs index 31a395feca..1bb3960fd9 100644 --- a/crates/dyn-abi/src/ty.rs +++ b/crates/dyn-abi/src/ty.rs @@ -85,6 +85,8 @@ struct StructProp { pub enum DynSolType { /// Address. Address, + /// Function. + Function, /// Boolean. Bool, /// Signed Integer. @@ -222,6 +224,7 @@ impl DynSolType { pub fn matches(&self, value: &DynSolValue) -> bool { match self { Self::Address => matches!(value, DynSolValue::Address(_)), + Self::Function => matches!(value, DynSolValue::Function(_)), Self::Bytes => matches!(value, DynSolValue::Bytes(_)), Self::Int(size) => matches!(value, DynSolValue::Int(_, s) if s == size), Self::Uint(size) => matches!(value, DynSolValue::Uint(_, s) if s == size), @@ -272,6 +275,9 @@ impl DynSolType { (Self::Address, DynToken::Word(word)) => Ok(DynSolValue::Address( sol_data::Address::detokenize(word.into()), )), + (Self::Function, DynToken::Word(word)) => Ok(DynSolValue::Function( + sol_data::Function::detokenize(word.into()), + )), (Self::Bool, DynToken::Word(word)) => { Ok(DynSolValue::Bool(sol_data::Bool::detokenize(word.into()))) } @@ -362,6 +368,7 @@ impl DynSolType { fn sol_type_name_simple(&self) -> Option<&str> { match self { Self::Address => Some("address"), + Self::Function => Some("function"), Self::Bool => Some("bool"), Self::Bytes => Some("bytes"), Self::String => Some("string"), @@ -375,11 +382,16 @@ impl DynSolType { fn sol_type_name_raw(&self, out: &mut String) { match self { #[cfg(feature = "eip712")] - Self::Address | Self::Bool | Self::Bytes | Self::String | Self::CustomStruct { .. } => { + Self::Address + | Self::Function + | Self::Bool + | Self::Bytes + | Self::String + | Self::CustomStruct { .. } => { out.push_str(unsafe { self.sol_type_name_simple().unwrap_unchecked() }); } #[cfg(not(feature = "eip712"))] - Self::Address | Self::Bool | Self::Bytes | Self::String => { + Self::Address | Self::Function | Self::Bool | Self::Bytes | Self::String => { out.push_str(unsafe { self.sol_type_name_simple().unwrap_unchecked() }); } @@ -450,9 +462,12 @@ impl DynSolType { /// Instantiate an empty dyn token, to be decoded into. pub(crate) fn empty_dyn_token(&self) -> DynToken<'_> { match self { - Self::Address | Self::Bool | Self::FixedBytes(_) | Self::Int(_) | Self::Uint(_) => { - DynToken::Word(Word::ZERO) - } + Self::Address + | Self::Function + | Self::Bool + | Self::FixedBytes(_) + | Self::Int(_) + | Self::Uint(_) => DynToken::Word(Word::ZERO), Self::Bytes | Self::String => DynToken::PackedSeq(&[]), diff --git a/crates/dyn-abi/src/value.rs b/crates/dyn-abi/src/value.rs index 27eee85118..3c68addd9c 100644 --- a/crates/dyn-abi/src/value.rs +++ b/crates/dyn-abi/src/value.rs @@ -1,6 +1,6 @@ use crate::{DynSolType, DynToken, Word}; use alloc::{borrow::Cow, boxed::Box, string::String, vec::Vec}; -use alloy_primitives::{Address, I256, U256}; +use alloy_primitives::{Address, Function, I256, U256}; use alloy_sol_types::{utils::words_for_len, Encoder}; #[cfg(feature = "eip712")] @@ -39,6 +39,8 @@ macro_rules! as_fixed_seq { pub enum DynSolValue { /// An address. Address(Address), + /// A function pointer. + Function(Function), /// A boolean. Bool(bool), /// A signed integer. @@ -172,6 +174,7 @@ impl DynSolValue { pub fn as_type(&self) -> Option { let ty = match self { Self::Address(_) => DynSolType::Address, + Self::Function(_) => DynSolType::Function, Self::Bool(_) => DynSolType::Bool, Self::Bytes(_) => DynSolType::Bytes, Self::FixedBytes(_, size) => DynSolType::FixedBytes(*size), @@ -211,6 +214,7 @@ impl DynSolValue { fn sol_type_name_simple(&self) -> Option<&str> { match self { Self::Address(_) => Some("address"), + Self::Function(_) => Some("function"), Self::Bool(_) => Some("bool"), Self::Bytes(_) => Some("bytes"), Self::String(_) => Some("string"), @@ -224,11 +228,16 @@ impl DynSolValue { fn sol_type_name_raw(&self, out: &mut String) -> bool { match self { #[cfg(not(feature = "eip712"))] - Self::Address(_) | Self::Bool(_) | Self::Bytes(_) | Self::String(_) => { + Self::Address(_) + | Self::Function(_) + | Self::Bool(_) + | Self::Bytes(_) + | Self::String(_) => { out.push_str(unsafe { self.sol_type_name_simple().unwrap_unchecked() }); } #[cfg(feature = "eip712")] Self::Address(_) + | Self::Function(_) | Self::Bool(_) | Self::Bytes(_) | Self::String(_) @@ -326,6 +335,7 @@ impl DynSolValue { pub fn as_word(&self) -> Option { match *self { Self::Address(a) => Some(a.into_word()), + Self::Function(f) => Some(f.into_word()), Self::Bool(b) => Some(Word::with_last_byte(b as u8)), Self::FixedBytes(w, _) => Some(w), Self::Int(i, _) => Some(i.into()), @@ -490,6 +500,7 @@ impl DynSolValue { pub fn is_dynamic(&self) -> bool { match self { Self::Address(_) + | Self::Function(_) | Self::Bool(_) | Self::Int(..) | Self::Uint(..) @@ -527,6 +538,7 @@ impl DynSolValue { match self { // `self.is_word()` Self::Address(_) + | Self::Function(_) | Self::Bool(_) | Self::FixedBytes(..) | Self::Int(..) @@ -570,6 +582,7 @@ impl DynSolValue { pub fn head_append(&self, enc: &mut Encoder) { match self { Self::Address(_) + | Self::Function(_) | Self::Bool(_) | Self::FixedBytes(..) | Self::Int(..) @@ -592,6 +605,7 @@ impl DynSolValue { pub fn tail_append(&self, enc: &mut Encoder) { match self { Self::Address(_) + | Self::Function(_) | Self::Bool(_) | Self::FixedBytes(..) | Self::Int(..) @@ -617,6 +631,7 @@ impl DynSolValue { pub fn encode_packed_to(&self, buf: &mut Vec) { match self { Self::Address(addr) => buf.extend_from_slice(addr.as_slice()), + Self::Function(func) => buf.extend_from_slice(func.as_slice()), Self::Bool(b) => buf.push(*b as u8), Self::String(s) => buf.extend_from_slice(s.as_bytes()), Self::Bytes(bytes) => buf.extend_from_slice(bytes), @@ -653,6 +668,7 @@ impl DynSolValue { pub fn tokenize(&self) -> DynToken<'_> { match self { Self::Address(a) => a.into_word().into(), + Self::Function(f) => f.into_word().into(), Self::Bool(b) => Word::with_last_byte(*b as u8).into(), Self::Bytes(buf) => DynToken::PackedSeq(buf), Self::FixedBytes(buf, _) => (*buf).into(), @@ -669,11 +685,15 @@ impl DynSolValue { let head_words = contents.iter().map(Self::head_words).sum::(); enc.push_offset(head_words as u32); - contents.iter().for_each(|t| { + for t in contents { t.head_append(enc); enc.bump_offset(t.tail_words() as u32); - }); - contents.iter().for_each(|t| t.tail_append(enc)); + } + + for t in contents { + t.tail_append(enc); + } + enc.pop_offset(); } diff --git a/crates/primitives/src/bits/address.rs b/crates/primitives/src/bits/address.rs index 21eea91d33..520cb12f23 100644 --- a/crates/primitives/src/bits/address.rs +++ b/crates/primitives/src/bits/address.rs @@ -16,13 +16,22 @@ pub enum AddressError { } impl From for AddressError { + #[inline] fn from(value: hex::FromHexError) -> Self { Self::Hex(value) } } #[cfg(feature = "std")] -impl std::error::Error for AddressError {} +impl std::error::Error for AddressError { + #[inline] + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Hex(err) => Some(err), + Self::InvalidChecksum => None, + } + } +} impl fmt::Display for AddressError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -173,23 +182,21 @@ impl Address { s: S, chain_id: Option, ) -> Result { - fn inner(s: &str, chain_id: Option) -> Result { + fn parse_checksummed(s: &str, chain_id: Option) -> Result { // checksummed addresses always start with the "0x" prefix if !s.starts_with("0x") { return Err(AddressError::Hex(hex::FromHexError::InvalidStringLength)) } let address: Address = s.parse()?; - let buf = &mut [0; 42]; - let expected = address.to_checksum_raw(buf, chain_id); - if s == expected { + if s == address.to_checksum_raw(&mut [0; 42], chain_id) { Ok(address) } else { Err(AddressError::InvalidChecksum) } } - inner(s.as_ref(), chain_id) + parse_checksummed(s.as_ref(), chain_id) } /// Encodes an Ethereum address to its [EIP-55] checksum. @@ -292,8 +299,7 @@ impl Address { #[inline] #[must_use] pub fn to_checksum(&self, chain_id: Option) -> String { - let mut buf = [0u8; 42]; - self.to_checksum_raw(&mut buf, chain_id).to_string() + self.to_checksum_raw(&mut [0u8; 42], chain_id).to_string() } /// Computes the `create` address for this address and nonce: diff --git a/crates/primitives/src/bits/function.rs b/crates/primitives/src/bits/function.rs new file mode 100644 index 0000000000..3fb253c96b --- /dev/null +++ b/crates/primitives/src/bits/function.rs @@ -0,0 +1,59 @@ +use crate::FixedBytes; +use core::borrow::Borrow; + +wrap_fixed_bytes! { + /// An Ethereum ABI function pointer, 24 bytes in length. + /// + /// An address (20 bytes), followed by a function selector (4 bytes). + /// Encoded identical to `bytes24`. + pub struct Function<24>; +} + +impl From<(A, S)> for Function +where + A: Borrow<[u8; 20]>, + S: Borrow<[u8; 4]>, +{ + #[inline] + fn from((address, selector): (A, S)) -> Self { + Self::from_address_and_selector(address, selector) + } +} + +impl Function { + /// Creates an Ethereum function from an EVM word's lower 24 bytes + /// (`word[..24]`). + /// + /// Note that this is different from `Address::from_word`, which uses the + /// upper 20 bytes. + #[inline] + #[must_use] + pub fn from_word(word: FixedBytes<32>) -> Self { + Self(FixedBytes(word[..24].try_into().unwrap())) + } + + /// Right-pads the function to 32 bytes (EVM word size). + /// + /// Note that this is different from `Address::into_word`, which left-pads + /// the address. + #[inline] + #[must_use] + pub fn into_word(&self) -> FixedBytes<32> { + let mut word = [0; 32]; + word[..24].copy_from_slice(self.as_slice()); + FixedBytes(word) + } + + /// Creates an Ethereum function from an address and selector. + #[inline] + pub fn from_address_and_selector(address: A, selector: S) -> Self + where + A: Borrow<[u8; 20]>, + S: Borrow<[u8; 4]>, + { + let mut bytes = [0; 24]; + bytes[..20].copy_from_slice(address.borrow()); + bytes[20..].copy_from_slice(selector.borrow()); + Self(FixedBytes(bytes)) + } +} diff --git a/crates/primitives/src/bits/mod.rs b/crates/primitives/src/bits/mod.rs index 85071a1e6b..28e5a91f05 100644 --- a/crates/primitives/src/bits/mod.rs +++ b/crates/primitives/src/bits/mod.rs @@ -10,6 +10,9 @@ pub use bloom::{Bloom, BloomInput, BLOOM_BITS_PER_ITEM, BLOOM_SIZE_BITS, BLOOM_S mod fixed; pub use fixed::FixedBytes; +mod function; +pub use function::Function; + #[cfg(feature = "rlp")] mod rlp; diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index ee0214309a..1331638679 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -33,8 +33,8 @@ pub use aliases::{ mod bits; pub use bits::{ - Address, AddressError, Bloom, BloomInput, FixedBytes, BLOOM_BITS_PER_ITEM, BLOOM_SIZE_BITS, - BLOOM_SIZE_BYTES, + Address, AddressError, Bloom, BloomInput, FixedBytes, Function, BLOOM_BITS_PER_ITEM, + BLOOM_SIZE_BITS, BLOOM_SIZE_BYTES, }; mod bytes; diff --git a/crates/sol-macro/src/expand/ty.rs b/crates/sol-macro/src/expand/ty.rs index f37ea50570..5b550a4e34 100644 --- a/crates/sol-macro/src/expand/ty.rs +++ b/crates/sol-macro/src/expand/ty.rs @@ -112,8 +112,13 @@ fn rec_expand_type(ty: &Type, tokens: &mut TokenStream) { } } } - Type::Function(ref function) => todo!("function types: {function:?}"), - Type::Mapping(ref mapping) => todo!("mapping types: {mapping:?}"), + Type::Function(ref function) => { + let span = function.span(); + quote_spanned! {span=> + ::alloy_sol_types::sol_data::Function + } + } + Type::Mapping(ref mapping) => panic!("mapping types are unsupported: {mapping:?}"), Type::Custom(ref custom) => return custom.to_tokens(tokens), }; tokens.extend(tts); diff --git a/crates/sol-type-parser/src/root.rs b/crates/sol-type-parser/src/root.rs index 3bbd3af68c..2336f0e76f 100644 --- a/crates/sol-type-parser/src/root.rs +++ b/crates/sol-type-parser/src/root.rs @@ -76,7 +76,7 @@ impl<'a> RootType<'a> { #[inline] pub fn try_basic_solidity(self) -> Result<()> { match self.0 { - "address" | "bool" | "string" | "bytes" | "uint" | "int" => Ok(()), + "address" | "bool" | "string" | "bytes" | "uint" | "int" | "function" => Ok(()), name => { if let Some(sz) = name.strip_prefix("bytes") { if let Ok(sz) = sz.parse::() { @@ -96,10 +96,10 @@ impl<'a> RootType<'a> { return Ok(()) } } - Err(Error::invalid_size(name)) - } else { - Err(Error::invalid_type_string(name)) + return Err(Error::invalid_size(name)) } + + Err(Error::invalid_type_string(name)) } } } diff --git a/crates/sol-types/src/types/data_type.rs b/crates/sol-types/src/types/data_type.rs index 8648d971ca..5d87e3bbba 100644 --- a/crates/sol-types/src/types/data_type.rs +++ b/crates/sol-types/src/types/data_type.rs @@ -8,7 +8,7 @@ use crate::{token::*, utils, Encodable, Result, SolType, Word}; use alloc::{borrow::Cow, string::String as RustString, vec::Vec}; -use alloy_primitives::{keccak256, Address as RustAddress, I256, U256}; +use alloy_primitives::{keccak256, Address as RustAddress, Function as RustFunction, I256, U256}; use core::{borrow::Borrow, fmt::*, hash::Hash, marker::PhantomData, ops::*}; /// Bool - `bool` @@ -209,7 +209,51 @@ impl SolType for Address { #[inline] fn encode_packed_to(rust: &Self::RustType, out: &mut Vec) { - out.extend_from_slice(rust.as_ref()); + out.extend_from_slice(rust.as_slice()); + } +} + +/// Function - `function` +pub struct Function; + +impl> Encodable for T { + #[inline] + fn to_tokens(&self) -> WordToken { + WordToken(RustFunction::new(*self.borrow()).into_word()) + } +} + +impl SolType for Function { + type RustType = RustFunction; + type TokenType<'a> = WordToken; + + #[inline] + fn sol_type_name() -> Cow<'static, str> { + "function".into() + } + + #[inline] + fn detokenize(token: Self::TokenType<'_>) -> Self::RustType { + RustFunction::from_word(token.0) + } + + #[inline] + fn type_check(token: &Self::TokenType<'_>) -> Result<()> { + if utils::check_zeroes(&token.0[24..]) { + Ok(()) + } else { + Err(Self::type_check_fail(token.as_slice())) + } + } + + #[inline] + fn eip712_data_word(rust: &Self::RustType) -> Word { + rust.into_word() + } + + #[inline] + fn encode_packed_to(rust: &Self::RustType, out: &mut Vec) { + out.extend_from_slice(rust.as_slice()); } } diff --git a/crates/sol-types/src/types/event/topic.rs b/crates/sol-types/src/types/event/topic.rs index 38abf5b0dc..af2333a97f 100644 --- a/crates/sol-types/src/types/event/topic.rs +++ b/crates/sol-types/src/types/event/topic.rs @@ -40,7 +40,7 @@ pub trait EventTopic: SolType { // Single word types: encoded as just the single word macro_rules! word_impl { - ($t:ty) => { + () => { #[inline] fn topic_preimage_length(_: &Self::RustType) -> usize { 32 @@ -48,48 +48,52 @@ macro_rules! word_impl { #[inline] fn encode_topic_preimage(rust: &Self::RustType, out: &mut Vec) { - out.extend($crate::Encodable::<$t>::to_tokens(rust).0 .0); + out.extend($crate::Encodable::::to_tokens(rust).0 .0); } #[inline] fn encode_topic(rust: &Self::RustType) -> WordToken { - $crate::Encodable::<$t>::to_tokens(rust) + $crate::Encodable::::to_tokens(rust) } }; } impl EventTopic for Address { - word_impl!(Address); + word_impl!(); +} + +impl EventTopic for Function { + word_impl!(); } impl EventTopic for Bool { - word_impl!(Bool); + word_impl!(); } impl EventTopic for Int where IntBitCount: SupportedInt, { - word_impl!(Int); + word_impl!(); } impl EventTopic for Uint where IntBitCount: SupportedInt, { - word_impl!(Uint); + word_impl!(); } impl EventTopic for FixedBytes where ByteCount: SupportedFixedBytes, { - word_impl!(FixedBytes); + word_impl!(); } // Bytes-like types - preimage encoding: bytes padded to 32; hash: the bytes macro_rules! bytes_impl { - ($t:ty) => { + () => { #[inline] fn topic_preimage_length(rust: &Self::RustType) -> usize { crate::utils::next_multiple_of_32(rust.len()) @@ -108,11 +112,11 @@ macro_rules! bytes_impl { } impl EventTopic for String { - bytes_impl!(String); + bytes_impl!(); } impl EventTopic for Bytes { - bytes_impl!(Bytes); + bytes_impl!(); } // Complex types - preimage encoding and hash: iter each element diff --git a/crates/sol-types/tests/ui/type.rs b/crates/sol-types/tests/ui/type.rs index 9a1a8ee1c1..046c60014d 100644 --- a/crates/sol-types/tests/ui/type.rs +++ b/crates/sol-types/tests/ui/type.rs @@ -734,4 +734,22 @@ sol! { } } +sol! { + struct Mappings { + mapping(mapping(a b => c d) e => mapping(f g => h i) j) map; + } +} + +sol! { + function mappings(mapping(uint256 a => bool b), mapping(bool => bool) x); +} + +sol! { + struct FunctionTypes { + function(function(bool) external pure returns (function(function())) f) external returns (function()) c; + } + + function functionTypes(FunctionTypes f) returns (function(function(function(), function())), function(function(), function())); +} + fn main() {} diff --git a/crates/sol-types/tests/ui/type.stderr b/crates/sol-types/tests/ui/type.stderr index bb40b476b6..eb68fd074e 100644 --- a/crates/sol-types/tests/ui/type.stderr +++ b/crates/sol-types/tests/ui/type.stderr @@ -64,6 +64,28 @@ error: enum has too many variants 476 | enum TooBigEnum { | ^^^^^^^^^^ +error: proc macro panicked + --> tests/ui/type.rs:737:1 + | +737 | / sol! { +738 | | struct Mappings { +739 | | mapping(mapping(a b => c d) e => mapping(f g => h i) j) map; +740 | | } +741 | | } + | |_^ + | + = help: message: mapping types are unsupported: TypeMapping { key: TypeMapping { key: Custom([SolIdent("a")]), key_name: Some(SolIdent("b")), value: Custom([SolIdent("c")]), value_name: Some(SolIdent("d")) }, key_name: Some(SolIdent("e")), value: TypeMapping { key: Custom([SolIdent("f")]), key_name: Some(SolIdent("g")), value: Custom([SolIdent("h")]), value_name: Some(SolIdent("i")) }, value_name: Some(SolIdent("j")) } + +error: proc macro panicked + --> tests/ui/type.rs:743:1 + | +743 | / sol! { +744 | | function mappings(mapping(uint256 a => bool b), mapping(bool => bool) x); +745 | | } + | |_^ + | + = help: message: mapping types are unsupported: TypeMapping { key: Uint(Some(256)), key_name: Some(SolIdent("a")), value: Bool, value_name: Some(SolIdent("b")) } + error[E0412]: cannot find type `bytes_` in this scope --> tests/ui/type.rs:205:9 | @@ -117,6 +139,7 @@ error[E0277]: the trait bound `(Address, Address, alloy_sol_types::sol_data::Str ArrayTypes TupleTypes CustomTypes + FunctionTypes = note: required for `(Address, ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ...)` to implement `SolType` note: required by a bound in `Encodable` --> src/types/ty.rs @@ -142,5 +165,6 @@ error[E0277]: the trait bound `(Address, Address, alloy_sol_types::sol_data::Str ArrayTypes TupleTypes CustomTypes + FunctionTypes = note: required for `(Address, ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ...)` to implement `SolType` = note: this error originates in the macro `sol` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/syn-solidity/src/attribute/function.rs b/crates/syn-solidity/src/attribute/function.rs index f407146348..aebfe83cd6 100644 --- a/crates/syn-solidity/src/attribute/function.rs +++ b/crates/syn-solidity/src/attribute/function.rs @@ -11,7 +11,7 @@ use syn::{ ext::IdentExt, parse::{Parse, ParseStream}, token::Brace, - Error, Ident, Result, Token, + Error, Ident, Result, }; /// A list of unique function attributes. Used in @@ -36,11 +36,7 @@ impl DerefMut for FunctionAttributes { impl Parse for FunctionAttributes { fn parse(input: ParseStream<'_>) -> Result { let mut attributes = HashSet::::new(); - while !(input.is_empty() - || input.peek(kw::returns) - || input.peek(Token![;]) - || input.peek(Brace)) - { + while !(input.is_empty() || input.peek(kw::returns) || !input.peek(Ident::peek_any)) { let attr = input.parse()?; if let Some(prev) = attributes.get(&attr) { let mut e = Error::new(attr.span(), "duplicate attribute"); diff --git a/crates/syn-solidity/src/type/mapping.rs b/crates/syn-solidity/src/type/mapping.rs index 208b50f740..c7fc95873a 100644 --- a/crates/syn-solidity/src/type/mapping.rs +++ b/crates/syn-solidity/src/type/mapping.rs @@ -51,7 +51,15 @@ impl fmt::Debug for TypeMapping { impl fmt::Display for TypeMapping { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "mapping({} => {})", self.key, self.value) + write!(f, "mapping({} ", self.key)?; + if let Some(key_name) = &self.key_name { + write!(f, "{key_name} ")?; + } + write!(f, "=> {} ", self.value)?; + if let Some(value_name) = &self.value_name { + write!(f, "{value_name}")?; + } + f.write_str(")") } }