diff --git a/Cargo.toml b/Cargo.toml index 13dfc3700..b8935b150 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["crates/*"] +members = ["crates/*", "tests/*"] resolver = "2" [workspace.package] @@ -18,6 +18,7 @@ rustdoc-args = ["--cfg", "docsrs"] [workspace.dependencies] # workspace crates +alloy-core = { version = "0.6.2", path = "crates/core", default-features = false } alloy-dyn-abi = { version = "0.6.2", path = "crates/dyn-abi", default-features = false } alloy-json-abi = { version = "0.6.2", path = "crates/json-abi", default-features = false } alloy-primitives = { version = "0.6.2", path = "crates/primitives", default-features = false } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 7a26e6a4a..b3abaa523 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -19,7 +19,7 @@ #[doc(inline)] pub use alloy_primitives as primitives; #[doc(no_inline)] -pub use alloy_primitives::hex; +pub use primitives::hex; #[cfg(feature = "dyn-abi")] #[doc(inline)] @@ -32,10 +32,25 @@ pub use alloy_json_abi as json_abi; #[cfg(feature = "sol-types")] #[doc(inline)] pub use alloy_sol_types as sol_types; -#[cfg(feature = "sol-types")] +#[cfg(all(doc, feature = "sol-types"))] #[doc(no_inline)] -pub use alloy_sol_types::sol; +pub use sol_types::sol; #[cfg(feature = "rlp")] #[doc(inline)] pub use alloy_rlp as rlp; + +/// [`sol!`](alloy_sol_types::sol!) macro wrapper to route imports to the correct crate. +/// +/// See [`sol!`](alloy_sol_types::sol!) for the actual macro documentation. +#[cfg(all(not(doc), feature = "sol-types"))] +#[doc(hidden)] +#[macro_export] +macro_rules! sol { + ($($t:tt)*) => { + $crate::sol_types::sol! { + #![sol(alloy_sol_types = $crate::sol_types)] + $($t)* + } + }; +} diff --git a/crates/core/tests/sol.rs b/crates/core/tests/sol.rs new file mode 100644 index 000000000..df7965a83 --- /dev/null +++ b/crates/core/tests/sol.rs @@ -0,0 +1,25 @@ +#![cfg(feature = "sol-types")] + +use alloy_core::sol; + +sol! { + struct MyStruct { + uint32 a; + uint32 b; + } + + function myFunction(uint32 a, uint32 b) returns(uint32); + event MyEvent(uint32 a, uint32 b); + error MyError(uint32 a, uint32 b); + + contract MyContract { + struct MyOtherStruct { + uint32 a; + uint32 b; + } + + function myOtherFunction(uint32 a, uint32 b) returns(uint32); + event MyOtherEvent(uint32 a, uint32 b); + error MyOtherError(uint32 a, uint32 b); + } +} diff --git a/crates/sol-macro/src/attr.rs b/crates/sol-macro/src/attr.rs index 80371bdcf..222e7cb50 100644 --- a/crates/sol-macro/src/attr.rs +++ b/crates/sol-macro/src/attr.rs @@ -72,6 +72,9 @@ pub struct SolAttrs { pub extra_methods: Option, pub docs: Option, + pub alloy_sol_types: Option, + pub alloy_contract: Option, + // TODO: Implement pub rename: Option, // TODO: Implement @@ -121,6 +124,9 @@ impl SolAttrs { } }; + // `path = ` + let path = || meta.value()?.parse::(); + // `path = ""` let lit = || meta.value()?.parse::(); @@ -145,6 +151,9 @@ impl SolAttrs { extra_methods => bool()?, docs => bool()?, + alloy_sol_types => path()?, + alloy_contract => path()?, + rename => lit()?, rename_all => CasingStyle::from_lit(&lit()?)?, @@ -313,6 +322,16 @@ mod tests { #[sol(rpc)] => Ok(sol_attrs! { rpc: true }), #[sol(rpc = true)] => Ok(sol_attrs! { rpc: true }), #[sol(rpc = false)] => Ok(sol_attrs! { rpc: false }), + + #[sol(alloy_sol_types)] => Err("expected `=`"), + #[sol(alloy_sol_types = alloy_core::sol_types)] => Ok(sol_attrs! { alloy_sol_types: parse_quote!(alloy_core::sol_types) }), + #[sol(alloy_sol_types = ::alloy_core::sol_types)] => Ok(sol_attrs! { alloy_sol_types: parse_quote!(::alloy_core::sol_types) }), + #[sol(alloy_sol_types = alloy::sol_types)] => Ok(sol_attrs! { alloy_sol_types: parse_quote!(alloy::sol_types) }), + #[sol(alloy_sol_types = ::alloy::sol_types)] => Ok(sol_attrs! { alloy_sol_types: parse_quote!(::alloy::sol_types) }), + + #[sol(alloy_contract)] => Err("expected `=`"), + #[sol(alloy_contract = alloy::contract)] => Ok(sol_attrs! { alloy_contract: parse_quote!(alloy::contract) }), + #[sol(alloy_contract = ::alloy::contract)] => Ok(sol_attrs! { alloy_contract: parse_quote!(::alloy::contract) }), } rename { diff --git a/crates/sol-macro/src/expand/contract.rs b/crates/sol-macro/src/expand/contract.rs index de6db91da..2f1e16794 100644 --- a/crates/sol-macro/src/expand/contract.rs +++ b/crates/sol-macro/src/expand/contract.rs @@ -41,7 +41,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, contract: &ItemContract) -> Result, contract: &ItemContract) -> Result, contract: &ItemContract) -> Result json::JsonAbi { @@ -226,7 +228,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, contract: &ItemContract) -> Result, contract: &ItemContract) -> Result, contract: &ItemContract) -> Result(provider: P, #params) - -> impl ::core::future::Future>> + pub fn deploy(provider: P, #params) + -> impl ::core::future::Future>> { #name::

::deploy(provider, #args) } #deploy_builder_doc #[inline] - pub fn deploy_builder(provider: P, #params) - -> ::alloy_contract::RawCallBuilder

+ pub fn deploy_builder(provider: P, #params) + -> alloy_contract::RawCallBuilder

{ #name::

::deploy_builder(provider, #args) } @@ -294,7 +298,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, contract: &ItemContract) -> Result ::alloy_contract::Result<#name

> + -> alloy_contract::Result<#name

> { let call_builder = Self::deploy_builder(provider, #args); let contract_address = call_builder.deploy().await?; @@ -304,18 +308,23 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, contract: &ItemContract) -> Result ::alloy_contract::RawCallBuilder

+ -> alloy_contract::RawCallBuilder

{ - ::alloy_contract::RawCallBuilder::new_raw(provider, #deploy_builder_data) + alloy_contract::RawCallBuilder::new_raw(provider, #deploy_builder_data) } }, ) })); + + let alloy_contract = &cx.crates.contract; + quote! { + use #alloy_contract as alloy_contract; + #[doc = #new_fn_doc] #[inline] - pub const fn new( - address: ::alloy_sol_types::private::Address, + pub const fn new( + address: alloy_sol_types::private::Address, provider: P, ) -> #name

{ #name::

::new(address, provider) @@ -326,7 +335,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, contract: &ItemContract) -> Result { - address: ::alloy_sol_types::private::Address, + address: alloy_sol_types::private::Address, provider: P, } @@ -340,10 +349,10 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, contract: &ItemContract) -> Result #name

{ + impl #name

{ #[doc = #new_fn_doc] #[inline] - pub const fn new(address: ::alloy_sol_types::private::Address, provider: P) -> Self { + pub const fn new(address: alloy_sol_types::private::Address, provider: P) -> Self { Self { address, provider } } @@ -351,18 +360,18 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, contract: &ItemContract) -> Result &::alloy_sol_types::private::Address { + pub const fn address(&self) -> &alloy_sol_types::private::Address { &self.address } /// Sets the address. #[inline] - pub fn set_address(&mut self, address: ::alloy_sol_types::private::Address) { + pub fn set_address(&mut self, address: alloy_sol_types::private::Address) { self.address = address; } /// Sets the address and returns `self`. - pub fn at(mut self, address: ::alloy_sol_types::private::Address) -> Self { + pub fn at(mut self, address: alloy_sol_types::private::Address) -> Self { self.set_address(address); self } @@ -384,15 +393,15 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, contract: &ItemContract) -> Result #name

{ + impl #name

{ /// Creates a new call builder using this contract instance's provider and address. /// /// Note that the call can be any function call, not just those defined in this /// contract. Prefer using the other methods for building type-safe contract calls. - pub fn call_builder(&self, call: &C) - -> ::alloy_contract::SolCallBuilder<&P, C> + pub fn call_builder(&self, call: &C) + -> alloy_contract::SolCallBuilder<&P, C> { - ::alloy_contract::SolCallBuilder::new_sol(&self.provider, &self.address, call) + alloy_contract::SolCallBuilder::new_sol(&self.provider, &self.address, call) } #(#methods)* @@ -400,6 +409,8 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, contract: &ItemContract) -> Result, contract: &ItemContract) -> Result CallLikeExpander<'a> { #def #[automatically_derived] - impl ::alloy_sol_types::SolInterface for #name { + impl alloy_sol_types::SolInterface for #name { const NAME: &'static str = #name_s; const MIN_DATA_LENGTH: usize = #min_data_len; const COUNT: usize = #count; @@ -615,7 +627,7 @@ impl<'a> CallLikeExpander<'a> { #[inline] fn selector(&self) -> [u8; 4] { match self {#( - Self::#variants(_) => <#types as ::alloy_sol_types::#trait_>::SELECTOR, + Self::#variants(_) => <#types as alloy_sol_types::#trait_>::SELECTOR, )*} } @@ -635,11 +647,11 @@ impl<'a> CallLikeExpander<'a> { selector: [u8; 4], data: &[u8], validate: bool - )-> ::alloy_sol_types::Result { - static DECODE_SHIMS: &[fn(&[u8], bool) -> ::alloy_sol_types::Result<#name>] = &[ + )-> alloy_sol_types::Result { + static DECODE_SHIMS: &[fn(&[u8], bool) -> alloy_sol_types::Result<#name>] = &[ #({ - fn #sorted_variants(data: &[u8], validate: bool) -> ::alloy_sol_types::Result<#name> { - <#sorted_types as ::alloy_sol_types::#trait_>::abi_decode_raw(data, validate) + fn #sorted_variants(data: &[u8], validate: bool) -> alloy_sol_types::Result<#name> { + <#sorted_types as alloy_sol_types::#trait_>::abi_decode_raw(data, validate) .map(#name::#sorted_variants) } #sorted_variants @@ -647,8 +659,8 @@ impl<'a> CallLikeExpander<'a> { ]; let Ok(idx) = Self::SELECTORS.binary_search(&selector) else { - return Err(::alloy_sol_types::Error::unknown_selector( - ::NAME, + return Err(alloy_sol_types::Error::unknown_selector( + ::NAME, selector, )); }; @@ -660,15 +672,15 @@ impl<'a> CallLikeExpander<'a> { fn abi_encoded_size(&self) -> usize { match self {#( Self::#variants(inner) => - <#types as ::alloy_sol_types::#trait_>::abi_encoded_size(inner), + <#types as alloy_sol_types::#trait_>::abi_encoded_size(inner), )*} } #[inline] - fn abi_encode_raw(&self, out: &mut ::alloy_sol_types::private::Vec) { + fn abi_encode_raw(&self, out: &mut alloy_sol_types::private::Vec) { match self {#( Self::#variants(inner) => - <#types as ::alloy_sol_types::#trait_>::abi_encode_raw(inner, out), + <#types as alloy_sol_types::#trait_>::abi_encode_raw(inner, out), )*} } } @@ -693,9 +705,9 @@ impl<'a> CallLikeExpander<'a> { let e_name = |&e: &&ItemEvent| self.cx.overloaded_name(e.into()); let err = quote! { - ::alloy_sol_types::private::Err(::alloy_sol_types::Error::InvalidLog { - name: ::NAME, - log: ::alloy_sol_types::private::Box::new(::alloy_sol_types::private::LogData::new_unchecked( + alloy_sol_types::private::Err(alloy_sol_types::Error::InvalidLog { + name: ::NAME, + log: alloy_sol_types::private::Box::new(alloy_sol_types::private::LogData::new_unchecked( topics.to_vec(), data.to_vec().into(), )), @@ -708,8 +720,8 @@ impl<'a> CallLikeExpander<'a> { quote! { match topics.first().copied() { #( - Some(<#variants as ::alloy_sol_types::#trait_>::SIGNATURE_HASH) => - #ret <#variants as ::alloy_sol_types::#trait_>::decode_raw_log(topics, data, validate) + Some(<#variants as alloy_sol_types::#trait_>::SIGNATURE_HASH) => + #ret <#variants as alloy_sol_types::#trait_>::decode_raw_log(topics, data, validate) .map(Self::#variants), )* _ => { #ret_err } @@ -720,7 +732,7 @@ impl<'a> CallLikeExpander<'a> { let variants = events.iter().filter(|e| e.is_anonymous()).map(e_name); quote! { #( - if let Ok(res) = <#variants as ::alloy_sol_types::#trait_>::decode_raw_log(topics, data, validate) { + if let Ok(res) = <#variants as alloy_sol_types::#trait_>::decode_raw_log(topics, data, validate) { return Ok(Self::#variants(res)); } )* @@ -731,11 +743,11 @@ impl<'a> CallLikeExpander<'a> { quote! { #def - impl ::alloy_sol_types::SolEventInterface for #name { + impl alloy_sol_types::SolEventInterface for #name { const NAME: &'static str = #name_s; const COUNT: usize = #count; - fn decode_raw_log(topics: &[::alloy_sol_types::Word], data: &[u8], validate: bool) -> ::alloy_sol_types::Result { + fn decode_raw_log(topics: &[alloy_sol_types::Word], data: &[u8], validate: bool) -> alloy_sol_types::Result { #non_anon_impl #anon_impl } @@ -870,11 +882,11 @@ fn call_builder_method(f: &ItemFunction, cx: &ExpCtxt<'_>) -> TokenStream { let call_name = cx.call_name(f); let param_names1 = f.parameters.names().enumerate().map(anon_name); let param_names2 = param_names1.clone(); - let param_tys = f.parameters.types().map(super::ty::expand_rust_type); + let param_tys = f.parameters.types().map(|ty| super::ty::expand_rust_type(ty, &cx.crates)); let doc = format!("Creates a new call builder for the [`{name}`] function."); quote! { #[doc = #doc] - pub fn #name(&self, #(#param_names1: #param_tys),*) -> ::alloy_contract::SolCallBuilder<&P, #call_name> { + pub fn #name(&self, #(#param_names1: #param_tys),*) -> alloy_contract::SolCallBuilder<&P, #call_name> { self.call_builder(&#call_name { #(#param_names2),* }) } } diff --git a/crates/sol-macro/src/expand/enum.rs b/crates/sol-macro/src/expand/enum.rs index b55b3d41c..0fe3be58d 100644 --- a/crates/sol-macro/src/expand/enum.rs +++ b/crates/sol-macro/src/expand/enum.rs @@ -65,8 +65,10 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result quote! { expect("unreachable") } }; - let uint8 = quote!(::alloy_sol_types::sol_data::Uint<8>); - let uint8_st = quote!(<#uint8 as ::alloy_sol_types::SolType>); + let alloy_sol_types = &cx.crates.sol_types; + + let uint8 = quote!(alloy_sol_types::sol_data::Uint<8>); + let uint8_st = quote!(<#uint8 as alloy_sol_types::SolType>); let doc = docs.then(|| attr::mk_doc(format!("```solidity\n{enumm}\n```"))); let tokens = quote! { @@ -82,6 +84,8 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result #[allow(non_camel_case_types, non_snake_case, clippy::style)] const _: () = { + use #alloy_sol_types as alloy_sol_types; + #[automatically_derived] impl ::core::convert::From<#name> for u8 { #[inline] @@ -92,15 +96,15 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result #[automatically_derived] impl ::core::convert::TryFrom for #name { - type Error = ::alloy_sol_types::Error; + type Error = alloy_sol_types::Error; #[allow(unsafe_code)] #[inline] - fn try_from(v: u8) -> ::alloy_sol_types::Result { + fn try_from(v: u8) -> alloy_sol_types::Result { if v <= #max { ::core::result::Result::Ok(unsafe { ::core::mem::transmute(v) }) } else { - ::core::result::Result::Err(::alloy_sol_types::Error::InvalidEnumValue { + ::core::result::Result::Err(alloy_sol_types::Error::InvalidEnumValue { name: #name_s, value: v, max: #max, @@ -110,30 +114,30 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result } #[automatically_derived] - impl ::alloy_sol_types::SolValue for #name { + impl alloy_sol_types::SolValue for #name { type SolType = Self; } #[automatically_derived] - impl ::alloy_sol_types::private::SolTypeValue<#name> for #name { + impl alloy_sol_types::private::SolTypeValue<#name> for #name { #[inline] fn stv_to_tokens(&self) -> #uint8_st::Token<'_> { - ::alloy_sol_types::Word::with_last_byte(*self as u8).into() + alloy_sol_types::Word::with_last_byte(*self as u8).into() } #[inline] - fn stv_eip712_data_word(&self) -> ::alloy_sol_types::Word { + fn stv_eip712_data_word(&self) -> alloy_sol_types::Word { #uint8_st::eip712_data_word(self.as_u8()) } #[inline] - fn stv_abi_encode_packed_to(&self, out: &mut ::alloy_sol_types::private::Vec) { + fn stv_abi_encode_packed_to(&self, out: &mut alloy_sol_types::private::Vec) { out.push(*self as u8); } } #[automatically_derived] - impl ::alloy_sol_types::SolType for #name { + impl alloy_sol_types::SolType for #name { type RustType = #name; type Token<'a> = #uint8_st::Token<'a>; @@ -146,7 +150,7 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result } #[inline] - fn type_check(token: &Self::Token<'_>) -> ::alloy_sol_types::Result<()> { + fn type_check(token: &Self::Token<'_>) -> alloy_sol_types::Result<()> { #uint8_st::type_check(token)?; >::try_from( #uint8_st::detokenize(*token) @@ -162,25 +166,25 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result } #[automatically_derived] - impl ::alloy_sol_types::EventTopic for #name { + impl alloy_sol_types::EventTopic for #name { #[inline] fn topic_preimage_length(rust: &Self::RustType) -> usize { - <#uint8 as ::alloy_sol_types::EventTopic>::topic_preimage_length(rust.as_u8()) + <#uint8 as alloy_sol_types::EventTopic>::topic_preimage_length(rust.as_u8()) } #[inline] - fn encode_topic_preimage(rust: &Self::RustType, out: &mut ::alloy_sol_types::private::Vec) { - <#uint8 as ::alloy_sol_types::EventTopic>::encode_topic_preimage(rust.as_u8(), out); + fn encode_topic_preimage(rust: &Self::RustType, out: &mut alloy_sol_types::private::Vec) { + <#uint8 as alloy_sol_types::EventTopic>::encode_topic_preimage(rust.as_u8(), out); } #[inline] - fn encode_topic(rust: &Self::RustType) -> ::alloy_sol_types::abi::token::WordToken { - <#uint8 as ::alloy_sol_types::EventTopic>::encode_topic(rust.as_u8()) + fn encode_topic(rust: &Self::RustType) -> alloy_sol_types::abi::token::WordToken { + <#uint8 as alloy_sol_types::EventTopic>::encode_topic(rust.as_u8()) } } #[automatically_derived] - impl ::alloy_sol_types::SolEnum for #name { + impl alloy_sol_types::SolEnum for #name { const COUNT: usize = #count; } diff --git a/crates/sol-macro/src/expand/error.rs b/crates/sol-macro/src/expand/error.rs index 12f3d17bb..06fa22bf0 100644 --- a/crates/sol-macro/src/expand/error.rs +++ b/crates/sol-macro/src/expand/error.rs @@ -27,13 +27,15 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, error: &ItemError) -> Result let docs = sol_attrs.docs.or(cx.attrs.docs).unwrap_or(true); let abi = sol_attrs.abi.or(cx.attrs.abi).unwrap_or(false); - let tokenize_impl = expand_tokenize(params); + let tokenize_impl = expand_tokenize(params, cx); let signature = cx.error_signature(error); let selector = crate::utils::selector(&signature); - let converts = expand_from_into_tuples(&name.0, params); - let fields = expand_fields(params); + let alloy_sol_types = &cx.crates.sol_types; + + let converts = expand_from_into_tuples(&name.0, params, cx); + let fields = expand_fields(params, cx); let doc = docs.then(|| { let selector = hex::encode_prefixed(selector.array.as_slice()); attr::mk_doc(format!( @@ -46,8 +48,8 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, error: &ItemError) -> Result let error = super::to_abi::generate(error, cx); quote! { #[automatically_derived] - impl ::alloy_sol_types::JsonAbiExt for #name { - type Abi = ::alloy_sol_types::private::alloy_json_abi::Error; + impl alloy_sol_types::JsonAbiExt for #name { + type Abi = alloy_sol_types::private::alloy_json_abi::Error; #[inline] fn abi() -> Self::Abi { @@ -68,18 +70,20 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, error: &ItemError) -> Result #[allow(non_camel_case_types, non_snake_case, clippy::style)] const _: () = { + use #alloy_sol_types as alloy_sol_types; + #converts #[automatically_derived] - impl ::alloy_sol_types::SolError for #name { + impl alloy_sol_types::SolError for #name { type Parameters<'a> = UnderlyingSolTuple<'a>; - type Token<'a> = as ::alloy_sol_types::SolType>::Token<'a>; + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; const SIGNATURE: &'static str = #signature; const SELECTOR: [u8; 4] = #selector; #[inline] - fn new<'a>(tuple: as ::alloy_sol_types::SolType>::RustType) -> Self { + fn new<'a>(tuple: as alloy_sol_types::SolType>::RustType) -> Self { tuple.into() } diff --git a/crates/sol-macro/src/expand/event.rs b/crates/sol-macro/src/expand/event.rs index c5bd365f4..10432316c 100644 --- a/crates/sol-macro/src/expand/event.rs +++ b/crates/sol-macro/src/expand/event.rs @@ -36,11 +36,11 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, event: &ItemEvent) -> Result let anonymous = event.is_anonymous(); // prepend the first topic if not anonymous - let first_topic = (!anonymous).then(|| quote!(::alloy_sol_types::sol_data::FixedBytes<32>)); - let topic_list = event.indexed_params().map(expand_event_topic_type); + let first_topic = (!anonymous).then(|| quote!(alloy_sol_types::sol_data::FixedBytes<32>)); + let topic_list = event.indexed_params().map(|p| expand_event_topic_type(p, cx)); let topic_list = first_topic.into_iter().chain(topic_list); - let (data_tuple, _) = expand_tuple_types(event.non_indexed_params().map(|p| &p.ty)); + let (data_tuple, _) = expand_tuple_types(event.non_indexed_params().map(|p| &p.ty), cx); // skip first topic if not anonymous, which is the hash of the signature let mut topic_i = !anonymous as usize; @@ -69,20 +69,20 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, event: &ItemEvent) -> Result quote! {(Self::SIGNATURE_HASH.into(), #(self.#topic_tuple_names.clone(),)*)} }; - let encode_first_topic = (!anonymous) - .then(|| quote!(::alloy_sol_types::abi::token::WordToken(Self::SIGNATURE_HASH))); + let encode_first_topic = + (!anonymous).then(|| quote!(alloy_sol_types::abi::token::WordToken(Self::SIGNATURE_HASH))); let encode_topics_impl = event.indexed_params().enumerate().map(|(i, p)| { let name = anon_name((i, p.name.as_ref())); - let ty = expand_type(&p.ty); + let ty = expand_type(&p.ty, &cx.crates); if p.indexed_as_hash() { quote! { - <::alloy_sol_types::sol_data::FixedBytes<32> as ::alloy_sol_types::EventTopic>::encode_topic(&self.#name) + as alloy_sol_types::EventTopic>::encode_topic(&self.#name) } } else { quote! { - <#ty as ::alloy_sol_types::EventTopic>::encode_topic(&self.#name) + <#ty as alloy_sol_types::EventTopic>::encode_topic(&self.#name) } } }); @@ -91,9 +91,9 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, event: &ItemEvent) -> Result .parameters .iter() .enumerate() - .map(|(i, p)| expand_event_topic_field(i, p, p.name.as_ref())); + .map(|(i, p)| expand_event_topic_field(i, p, p.name.as_ref(), cx)); - let tokenize_body_impl = expand_event_tokenize(&event.parameters); + let tokenize_body_impl = expand_event_tokenize(&event.parameters, cx); let encode_topics_impl = encode_first_topic .into_iter() @@ -114,8 +114,8 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, event: &ItemEvent) -> Result let event = super::to_abi::generate(event, cx); quote! { #[automatically_derived] - impl ::alloy_sol_types::JsonAbiExt for #name { - type Abi = ::alloy_sol_types::private::alloy_json_abi::Event; + impl alloy_sol_types::JsonAbiExt for #name { + type Abi = alloy_sol_types::private::alloy_json_abi::Event; #[inline] fn abi() -> Self::Abi { @@ -126,6 +126,8 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, event: &ItemEvent) -> Result } }); + let alloy_sol_types = &cx.crates.sol_types; + let tokens = quote! { #(#attrs)* #doc @@ -136,24 +138,26 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, event: &ItemEvent) -> Result #[allow(non_camel_case_types, non_snake_case, clippy::style)] const _: () = { + use #alloy_sol_types as alloy_sol_types; + #[automatically_derived] - impl ::alloy_sol_types::SolEvent for #name { + impl alloy_sol_types::SolEvent for #name { type DataTuple<'a> = #data_tuple; - type DataToken<'a> = as ::alloy_sol_types::SolType>::Token<'a>; + type DataToken<'a> = as alloy_sol_types::SolType>::Token<'a>; type TopicList = (#(#topic_list,)*); const SIGNATURE: &'static str = #signature; - const SIGNATURE_HASH: ::alloy_sol_types::private::B256 = - ::alloy_sol_types::private::B256::new(#selector); + const SIGNATURE_HASH: alloy_sol_types::private::B256 = + alloy_sol_types::private::B256::new(#selector); const ANONYMOUS: bool = #anonymous; #[allow(unused_variables)] #[inline] fn new( - topics: ::RustType, - data: as ::alloy_sol_types::SolType>::RustType, + topics: ::RustType, + data: as alloy_sol_types::SolType>::RustType, ) -> Self { Self { #(#new_impl,)* @@ -166,17 +170,17 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, event: &ItemEvent) -> Result } #[inline] - fn topics(&self) -> ::RustType { + fn topics(&self) -> ::RustType { #topics_impl } #[inline] fn encode_topics_raw( &self, - out: &mut [::alloy_sol_types::abi::token::WordToken], - ) -> ::alloy_sol_types::Result<()> { - if out.len() < ::COUNT { - return Err(::alloy_sol_types::Error::Overrun); + out: &mut [alloy_sol_types::abi::token::WordToken], + ) -> alloy_sol_types::Result<()> { + if out.len() < ::COUNT { + return Err(alloy_sol_types::Error::Overrun); } #(#encode_topics_impl)* Ok(()) @@ -189,12 +193,13 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, event: &ItemEvent) -> Result Ok(tokens) } -fn expand_event_topic_type(param: &EventParameter) -> TokenStream { +fn expand_event_topic_type(param: &EventParameter, cx: &ExpCtxt<'_>) -> TokenStream { + let alloy_sol_types = &cx.crates.sol_types; assert!(param.is_indexed()); if param.is_abi_dynamic() { - quote_spanned! {param.ty.span()=> ::alloy_sol_types::sol_data::FixedBytes<32> } + quote_spanned! {param.ty.span()=> #alloy_sol_types::sol_data::FixedBytes<32> } } else { - expand_type(¶m.ty) + expand_type(¶m.ty, &cx.crates) } } @@ -202,15 +207,14 @@ fn expand_event_topic_field( i: usize, param: &EventParameter, name: Option<&SolIdent>, + cx: &ExpCtxt<'_>, ) -> TokenStream { let name = anon_name((i, name)); let ty = if param.indexed_as_hash() { - ty::expand_rust_type(&ast::Type::FixedBytes( - name.span(), - core::num::NonZeroU16::new(32).unwrap(), - )) + let bytes32 = ast::Type::FixedBytes(name.span(), core::num::NonZeroU16::new(32).unwrap()); + ty::expand_rust_type(&bytes32, &cx.crates) } else { - ty::expand_rust_type(¶m.ty) + ty::expand_rust_type(¶m.ty, &cx.crates) }; quote!(#name: #ty) } diff --git a/crates/sol-macro/src/expand/function.rs b/crates/sol-macro/src/expand/function.rs index 2fdb1c03a..987b7b8ff 100644 --- a/crates/sol-macro/src/expand/function.rs +++ b/crates/sol-macro/src/expand/function.rs @@ -54,18 +54,18 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, function: &ItemFunction) -> Result, function: &ItemFunction) -> Result Self::Abi { @@ -97,6 +97,8 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, function: &ItemFunction) -> Result, function: &ItemFunction) -> Result = #call_tuple; - type Token<'a> = as ::alloy_sol_types::SolType>::Token<'a>; + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; type Return = #return_name; type ReturnTuple<'a> = #return_tuple; - type ReturnToken<'a> = as ::alloy_sol_types::SolType>::Token<'a>; + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; const SIGNATURE: &'static str = #signature; const SELECTOR: [u8; 4] = #selector; #[inline] - fn new<'a>(tuple: as ::alloy_sol_types::SolType>::RustType) -> Self { + fn new<'a>(tuple: as alloy_sol_types::SolType>::RustType) -> Self { tuple.into() } @@ -143,8 +147,8 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, function: &ItemFunction) -> Result ::alloy_sol_types::Result { - as ::alloy_sol_types::SolType>::abi_decode_sequence(data, validate).map(Into::into) + fn abi_decode_returns(data: &[u8], validate: bool) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence(data, validate).map(Into::into) } } @@ -159,11 +163,14 @@ fn expand_constructor(cx: &ExpCtxt<'_>, constructor: &ItemFunction) -> Result, constructor: &ItemFunction) -> Result = #call_tuple; - type Token<'a> = as ::alloy_sol_types::SolType>::Token<'a>; + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; #[inline] - fn new<'a>(tuple: as ::alloy_sol_types::SolType>::RustType) -> Self { + fn new<'a>(tuple: as alloy_sol_types::SolType>::RustType) -> Self { tuple.into() } diff --git a/crates/sol-macro/src/expand/mod.rs b/crates/sol-macro/src/expand/mod.rs index 72b275645..9194c8768 100644 --- a/crates/sol-macro/src/expand/mod.rs +++ b/crates/sol-macro/src/expand/mod.rs @@ -23,7 +23,7 @@ use syn::{ext::IdentExt, parse_quote, Attribute, Error, Result}; mod macros; mod ty; -pub use ty::expand_type; +pub(crate) use ty::expand_type; mod contract; mod r#enum; @@ -56,6 +56,7 @@ pub struct ExpCtxt<'ast> { overloads: IndexMap, attrs: SolAttrs, + crates: ExternCrates, ast: &'ast File, } @@ -68,6 +69,7 @@ impl<'ast> ExpCtxt<'ast> { overloaded_items: IndexMap::new(), overloads: IndexMap::new(), attrs: SolAttrs::default(), + crates: ExternCrates::default(), ast, } } @@ -124,6 +126,7 @@ impl<'ast> ExpCtxt<'ast> { fn parse_file_attributes(&mut self) -> Result<()> { let (attrs, others) = attr::SolAttrs::parse(&self.ast.attrs)?; self.attrs = attrs; + self.crates.fill(&self.attrs); let errs = others.iter().map(|attr| Error::new_spanned(attr, "unexpected attribute")); utils::combine_errors(errs) @@ -514,13 +517,45 @@ impl<'ast> ExpCtxt<'ast> { } } +/// Configurable extern crate dependencies. +/// +/// These should be added to import lists at the top of anonymous `const _: () = { ... }` blocks, +/// and in case of top-level structs they should be inlined into all `path`s. +pub(crate) struct ExternCrates { + pub(crate) sol_types: syn::Path, + pub(crate) contract: syn::Path, +} + +impl Default for ExternCrates { + fn default() -> Self { + Self { + sol_types: parse_quote!(::alloy_sol_types), + contract: parse_quote!(::alloy_contract), + } + } +} + +impl ExternCrates { + pub(crate) fn fill(&mut self, attrs: &SolAttrs) { + if let Some(sol_types) = &attrs.alloy_sol_types { + self.sol_types = sol_types.clone(); + } + if let Some(alloy_contract) = &attrs.alloy_contract { + self.contract = alloy_contract.clone(); + } + } +} + // helper functions /// Expands a list of parameters into a list of struct fields. -fn expand_fields

(params: &Parameters

) -> impl Iterator + '_ { +fn expand_fields<'a, P>( + params: &'a Parameters

, + cx: &'a ExpCtxt<'_>, +) -> impl Iterator + 'a { params.iter().enumerate().map(|(i, var)| { let name = anon_name((i, var.name.as_ref())); - let ty = expand_rust_type(&var.ty); + let ty = expand_rust_type(&var.ty, &cx.crates); let attrs = &var.attrs; quote! { #(#attrs)* @@ -544,13 +579,17 @@ fn anon_name + Clone>((i, name): (usize, Option<&T>)) -> Ident { } /// Expands `From` impls for a list of types and the corresponding tuple. -fn expand_from_into_tuples

(name: &Ident, fields: &Parameters

) -> TokenStream { +fn expand_from_into_tuples

( + name: &Ident, + fields: &Parameters

, + cx: &ExpCtxt<'_>, +) -> TokenStream { let names = fields.names().enumerate().map(anon_name); let names2 = names.clone(); let idxs = (0..fields.len()).map(syn::Index::from); - let (sol_tuple, rust_tuple) = expand_tuple_types(fields.types()); + let (sol_tuple, rust_tuple) = expand_tuple_types(fields.types(), cx); quote! { #[doc(hidden)] @@ -560,9 +599,9 @@ fn expand_from_into_tuples

(name: &Ident, fields: &Parameters

) -> TokenStre #[cfg(test)] #[allow(dead_code, unreachable_patterns)] - fn _type_assertion(_t: ::alloy_sol_types::private::AssertTypeEq) { + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { match _t { - ::alloy_sol_types::private::AssertTypeEq::<::RustType>(_) => {} + alloy_sol_types::private::AssertTypeEq::<::RustType>(_) => {} } } @@ -589,15 +628,16 @@ fn expand_from_into_tuples

(name: &Ident, fields: &Parameters

) -> TokenStre /// Returns `(sol_tuple, rust_tuple)` fn expand_tuple_types<'a, I: IntoIterator>( types: I, + cx: &ExpCtxt<'_>, ) -> (TokenStream, TokenStream) { let mut sol = TokenStream::new(); let mut rust = TokenStream::new(); let comma = Punct::new(',', Spacing::Alone); for ty in types { - ty::rec_expand_type(ty, &mut sol); + ty::rec_expand_type(ty, &cx.crates, &mut sol); sol.append(comma.clone()); - ty::rec_expand_rust_type(ty, &mut rust); + ty::rec_expand_rust_type(ty, &cx.crates, &mut rust); rust.append(comma.clone()); } let wrap_in_parens = @@ -606,29 +646,34 @@ fn expand_tuple_types<'a, I: IntoIterator>( } /// Expand the body of a `tokenize` function. -fn expand_tokenize

(params: &Parameters

) -> TokenStream { - tokenize_(params.iter().enumerate().map(|(i, p)| (i, &p.ty, p.name.as_ref()))) +fn expand_tokenize

(params: &Parameters

, cx: &ExpCtxt<'_>) -> TokenStream { + tokenize_(params.iter().enumerate().map(|(i, p)| (i, &p.ty, p.name.as_ref())), cx) } /// Expand the body of a `tokenize` function. -fn expand_event_tokenize<'a>(params: impl IntoIterator) -> TokenStream { +fn expand_event_tokenize<'a>( + params: impl IntoIterator, + cx: &ExpCtxt<'_>, +) -> TokenStream { tokenize_( params .into_iter() .enumerate() .filter(|(_, p)| !p.is_indexed()) .map(|(i, p)| (i, &p.ty, p.name.as_ref())), + cx, ) } fn tokenize_<'a>( iter: impl Iterator)>, + cx: &'a ExpCtxt<'_>, ) -> TokenStream { let statements = iter.into_iter().map(|(i, ty, name)| { - let ty = expand_type(ty); + let ty = expand_type(ty, &cx.crates); let name = name.cloned().unwrap_or_else(|| generate_name(i).into()); quote! { - <#ty as ::alloy_sol_types::SolType>::tokenize(&self.#name) + <#ty as alloy_sol_types::SolType>::tokenize(&self.#name) } }); quote! { @@ -642,7 +687,7 @@ fn emit_json_error() { if !EMITTED.swap(true, Ordering::Relaxed) { emit_error!( Span::call_site(), - "the `#[sol(dyn_abi)]` attribute requires the `\"json\"` feature" + "the `#[sol(abi)]` attribute requires the `\"json\"` feature" ); } } diff --git a/crates/sol-macro/src/expand/struct.rs b/crates/sol-macro/src/expand/struct.rs index 156d259e4..270c5586d 100644 --- a/crates/sol-macro/src/expand/struct.rs +++ b/crates/sol-macro/src/expand/struct.rs @@ -31,30 +31,32 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { let docs = sol_attrs.docs.or(cx.attrs.docs).unwrap_or(true); let (field_types, field_names): (Vec<_>, Vec<_>) = - fields.iter().map(|f| (expand_type(&f.ty), f.name.as_ref().unwrap())).unzip(); + fields.iter().map(|f| (expand_type(&f.ty, &cx.crates), f.name.as_ref().unwrap())).unzip(); let eip712_encode_type_fns = expand_encode_type_fns(cx, fields, name); - let tokenize_impl = expand_tokenize(fields); + let tokenize_impl = expand_tokenize(fields, cx); let encode_data_impl = match fields.len() { 0 => unreachable!("struct with zero fields"), 1 => { let name = *field_names.first().unwrap(); let ty = field_types.first().unwrap(); - quote!(<#ty as ::alloy_sol_types::SolType>::eip712_data_word(&self.#name).0.to_vec()) + quote!(<#ty as alloy_sol_types::SolType>::eip712_data_word(&self.#name).0.to_vec()) } _ => quote! { [#( - <#field_types as ::alloy_sol_types::SolType>::eip712_data_word(&self.#field_names).0, + <#field_types as alloy_sol_types::SolType>::eip712_data_word(&self.#field_names).0, )*].concat() }, }; + let alloy_sol_types = &cx.crates.sol_types; + let attrs = attrs.iter(); - let convert = expand_from_into_tuples(&name.0, fields); + let convert = expand_from_into_tuples(&name.0, fields, cx); let name_s = name.as_string(); - let fields = expand_fields(fields); + let fields = expand_fields(fields, cx); let doc = docs.then(|| attr::mk_doc(format!("```solidity\n{s}\n```"))); let tokens = quote! { @@ -68,17 +70,19 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { #[allow(non_camel_case_types, non_snake_case, clippy::style)] const _: () = { + use #alloy_sol_types as alloy_sol_types; + #convert #[automatically_derived] - impl ::alloy_sol_types::SolValue for #name { + impl alloy_sol_types::SolValue for #name { type SolType = Self; } #[automatically_derived] - impl ::alloy_sol_types::private::SolTypeValue for #name { + impl alloy_sol_types::private::SolTypeValue for #name { #[inline] - fn stv_to_tokens(&self) -> ::Token<'_> { + fn stv_to_tokens(&self) -> ::Token<'_> { #tokenize_impl } @@ -86,79 +90,79 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, s: &ItemStruct) -> Result { fn stv_abi_encoded_size(&self) -> usize { // TODO: Avoid cloning let tuple = as ::core::convert::From>::from(self.clone()); - as ::alloy_sol_types::SolType>::abi_encoded_size(&tuple) + as alloy_sol_types::SolType>::abi_encoded_size(&tuple) } #[inline] - fn stv_eip712_data_word(&self) -> ::alloy_sol_types::Word { - ::eip712_hash_struct(self) + fn stv_eip712_data_word(&self) -> alloy_sol_types::Word { + ::eip712_hash_struct(self) } #[inline] - fn stv_abi_encode_packed_to(&self, out: &mut ::alloy_sol_types::private::Vec) { + fn stv_abi_encode_packed_to(&self, out: &mut alloy_sol_types::private::Vec) { // TODO: Avoid cloning let tuple = as ::core::convert::From>::from(self.clone()); - as ::alloy_sol_types::SolType>::abi_encode_packed_to(&tuple, out) + as alloy_sol_types::SolType>::abi_encode_packed_to(&tuple, out) } } #[automatically_derived] - impl ::alloy_sol_types::SolType for #name { + impl alloy_sol_types::SolType for #name { type RustType = Self; - type Token<'a> = as ::alloy_sol_types::SolType>::Token<'a>; + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; - const SOL_NAME: &'static str = ::NAME; + const SOL_NAME: &'static str = ::NAME; const ENCODED_SIZE: Option = - as ::alloy_sol_types::SolType>::ENCODED_SIZE; + as alloy_sol_types::SolType>::ENCODED_SIZE; #[inline] fn valid_token(token: &Self::Token<'_>) -> bool { - as ::alloy_sol_types::SolType>::valid_token(token) + as alloy_sol_types::SolType>::valid_token(token) } #[inline] fn detokenize(token: Self::Token<'_>) -> Self::RustType { - let tuple = as ::alloy_sol_types::SolType>::detokenize(token); + let tuple = as alloy_sol_types::SolType>::detokenize(token); >>::from(tuple) } } #[automatically_derived] - impl ::alloy_sol_types::SolStruct for #name { + impl alloy_sol_types::SolStruct for #name { const NAME: &'static str = #name_s; #eip712_encode_type_fns #[inline] - fn eip712_encode_data(&self) -> ::alloy_sol_types::private::Vec { + fn eip712_encode_data(&self) -> alloy_sol_types::private::Vec { #encode_data_impl } } #[automatically_derived] - impl ::alloy_sol_types::EventTopic for #name { + impl alloy_sol_types::EventTopic for #name { #[inline] fn topic_preimage_length(rust: &Self::RustType) -> usize { 0usize #( - + <#field_types as ::alloy_sol_types::EventTopic>::topic_preimage_length(&rust.#field_names) + + <#field_types as alloy_sol_types::EventTopic>::topic_preimage_length(&rust.#field_names) )* } #[inline] - fn encode_topic_preimage(rust: &Self::RustType, out: &mut ::alloy_sol_types::private::Vec) { - out.reserve(::topic_preimage_length(rust)); + fn encode_topic_preimage(rust: &Self::RustType, out: &mut alloy_sol_types::private::Vec) { + out.reserve(::topic_preimage_length(rust)); #( - <#field_types as ::alloy_sol_types::EventTopic>::encode_topic_preimage(&rust.#field_names, out); + <#field_types as alloy_sol_types::EventTopic>::encode_topic_preimage(&rust.#field_names, out); )* } #[inline] - fn encode_topic(rust: &Self::RustType) -> ::alloy_sol_types::abi::token::WordToken { - let mut out = ::alloy_sol_types::private::Vec::new(); - ::encode_topic_preimage(rust, &mut out); - ::alloy_sol_types::abi::token::WordToken( - ::alloy_sol_types::private::keccak256(out) + fn encode_topic(rust: &Self::RustType) -> alloy_sol_types::abi::token::WordToken { + let mut out = alloy_sol_types::private::Vec::new(); + ::encode_topic_preimage(rust, &mut out); + alloy_sol_types::abi::token::WordToken( + alloy_sol_types::private::keccak256(out) ) } } @@ -202,40 +206,40 @@ fn expand_encode_type_fns( } }); // cannot panic as this field is guaranteed to contain a custom type - let ty = expand_type(&ty.unwrap()); + let ty = expand_type(&ty.unwrap(), &cx.crates); quote! { - components.push(<#ty as ::alloy_sol_types::SolStruct>::eip712_root_type()); - components.extend(<#ty as ::alloy_sol_types::SolStruct>::eip712_components()); + components.push(<#ty as alloy_sol_types::SolStruct>::eip712_root_type()); + components.extend(<#ty as alloy_sol_types::SolStruct>::eip712_components()); } }); let capacity = proc_macro2::Literal::usize_unsuffixed(n_custom); quote! { - let mut components = ::alloy_sol_types::private::Vec::with_capacity(#capacity); + let mut components = alloy_sol_types::private::Vec::with_capacity(#capacity); #(#bits)* components } } else { - quote! { ::alloy_sol_types::private::Vec::new() } + quote! { alloy_sol_types::private::Vec::new() } }; let encode_type_impl_opt = (n_custom == 0).then(|| { quote! { #[inline] - fn eip712_encode_type() -> ::alloy_sol_types::private::Cow<'static, str> { - ::eip712_root_type() + fn eip712_encode_type() -> alloy_sol_types::private::Cow<'static, str> { + ::eip712_root_type() } } }); quote! { #[inline] - fn eip712_root_type() -> ::alloy_sol_types::private::Cow<'static, str> { - ::alloy_sol_types::private::Cow::Borrowed(#root) + fn eip712_root_type() -> alloy_sol_types::private::Cow<'static, str> { + alloy_sol_types::private::Cow::Borrowed(#root) } #[inline] - fn eip712_components() -> ::alloy_sol_types::private::Vec<::alloy_sol_types::private::Cow<'static, str>> { + fn eip712_components() -> alloy_sol_types::private::Vec> { #components_impl } diff --git a/crates/sol-macro/src/expand/to_abi.rs b/crates/sol-macro/src/expand/to_abi.rs index 861c5a066..d69901a4c 100644 --- a/crates/sol-macro/src/expand/to_abi.rs +++ b/crates/sol-macro/src/expand/to_abi.rs @@ -7,15 +7,15 @@ use ast::{ItemError, ItemEvent, ItemFunction}; use proc_macro2::TokenStream; use std::fmt::Write; -pub fn generate(t: &T, cx: &ExpCtxt<'_>) -> TokenStream +pub(crate) fn generate(t: &T, cx: &ExpCtxt<'_>) -> TokenStream where T: ToAbi, T::DynAbi: Verbatim, { - crate::verbatim::verbatim(&t.to_dyn_abi(cx)) + crate::verbatim::verbatim(&t.to_dyn_abi(cx), &cx.crates) } -pub trait ToAbi { +pub(crate) trait ToAbi { type DynAbi; fn to_dyn_abi(&self, cx: &ExpCtxt<'_>) -> Self::DynAbi; @@ -187,7 +187,7 @@ macro_rules! make_map { let item = item.to_dyn_abi($cx); map.entry(item.name.clone()).or_default().push(item); } - crate::verbatim::verbatim(&map) + crate::verbatim::verbatim(&map, &$cx.crates) }}; } diff --git a/crates/sol-macro/src/expand/ty.rs b/crates/sol-macro/src/expand/ty.rs index 5c65b59cc..c07a9c8e6 100644 --- a/crates/sol-macro/src/expand/ty.rs +++ b/crates/sol-macro/src/expand/ty.rs @@ -1,6 +1,6 @@ //! [`Type`] expansion. -use super::ExpCtxt; +use super::{ExpCtxt, ExternCrates}; use ast::{Item, Parameters, Spanned, Type, TypeArray}; use proc_macro2::{Ident, Literal, TokenStream}; use quote::{quote_spanned, ToTokens}; @@ -8,9 +8,9 @@ use std::{fmt, num::NonZeroU16}; /// Expands a single [`Type`] recursively to its `alloy_sol_types::sol_data` /// equivalent. -pub fn expand_type(ty: &Type) -> TokenStream { +pub(crate) fn expand_type(ty: &Type, crates: &ExternCrates) -> TokenStream { let mut tokens = TokenStream::new(); - rec_expand_type(ty, &mut tokens); + rec_expand_type(ty, crates, &mut tokens); tokens } @@ -19,24 +19,25 @@ pub fn expand_type(ty: &Type) -> TokenStream { /// This is the same as `<#expand_type(ty) as SolType>::RustType`, but generates /// nicer code for documentation and IDE/LSP support when the type is not /// ambiguous. -pub fn expand_rust_type(ty: &Type) -> TokenStream { +pub(crate) fn expand_rust_type(ty: &Type, crates: &ExternCrates) -> TokenStream { let mut tokens = TokenStream::new(); - rec_expand_rust_type(ty, &mut tokens); + rec_expand_rust_type(ty, crates, &mut tokens); tokens } /// The [`expand_type`] recursive implementation. -pub fn rec_expand_type(ty: &Type, tokens: &mut TokenStream) { +pub(crate) fn rec_expand_type(ty: &Type, crates: &ExternCrates, tokens: &mut TokenStream) { + let alloy_sol_types = &crates.sol_types; let tts = match *ty { - Type::Address(span, _) => quote_spanned! {span=> ::alloy_sol_types::sol_data::Address }, - Type::Bool(span) => quote_spanned! {span=> ::alloy_sol_types::sol_data::Bool }, - Type::String(span) => quote_spanned! {span=> ::alloy_sol_types::sol_data::String }, - Type::Bytes(span) => quote_spanned! {span=> ::alloy_sol_types::sol_data::Bytes }, + Type::Address(span, _) => quote_spanned! {span=> #alloy_sol_types::sol_data::Address }, + Type::Bool(span) => quote_spanned! {span=> #alloy_sol_types::sol_data::Bool }, + Type::String(span) => quote_spanned! {span=> #alloy_sol_types::sol_data::String }, + Type::Bytes(span) => quote_spanned! {span=> #alloy_sol_types::sol_data::Bytes }, Type::FixedBytes(span, size) => { assert!(size.get() <= 32); let size = Literal::u16_unsuffixed(size.get()); - quote_spanned! {span=> ::alloy_sol_types::sol_data::FixedBytes<#size> } + quote_spanned! {span=> #alloy_sol_types::sol_data::FixedBytes<#size> } } Type::Int(span, size) | Type::Uint(span, size) => { let name = match ty { @@ -50,29 +51,29 @@ pub fn rec_expand_type(ty: &Type, tokens: &mut TokenStream) { assert!(size <= 256 && size % 8 == 0); let size = Literal::u16_unsuffixed(size); - quote_spanned! {span=> ::alloy_sol_types::sol_data::#name<#size> } + quote_spanned! {span=> #alloy_sol_types::sol_data::#name<#size> } } Type::Tuple(ref tuple) => { return tuple.paren_token.surround(tokens, |tokens| { for pair in tuple.types.pairs() { let (ty, comma) = pair.into_tuple(); - rec_expand_type(ty, tokens); + rec_expand_type(ty, crates, tokens); comma.to_tokens(tokens); } }) } Type::Array(ref array) => { - let ty = expand_type(&array.ty); + let ty = expand_type(&array.ty, crates); let span = array.span(); if let Some(size) = array.size() { - quote_spanned! {span=> ::alloy_sol_types::sol_data::FixedArray<#ty, #size> } + quote_spanned! {span=> #alloy_sol_types::sol_data::FixedArray<#ty, #size> } } else { - quote_spanned! {span=> ::alloy_sol_types::sol_data::Array<#ty> } + quote_spanned! {span=> #alloy_sol_types::sol_data::Array<#ty> } } } Type::Function(ref function) => quote_spanned! {function.span()=> - ::alloy_sol_types::sol_data::Function + #alloy_sol_types::sol_data::Function }, Type::Mapping(ref mapping) => quote_spanned! {mapping.span()=> ::core::compile_error!("Mapping types are not supported here") @@ -85,23 +86,24 @@ pub fn rec_expand_type(ty: &Type, tokens: &mut TokenStream) { // IMPORTANT: Keep in sync with `sol-types/src/types/data_type.rs` /// The [`expand_rust_type`] recursive implementation. -pub fn rec_expand_rust_type(ty: &Type, tokens: &mut TokenStream) { +pub(crate) fn rec_expand_rust_type(ty: &Type, crates: &ExternCrates, tokens: &mut TokenStream) { // Display sizes that match with the Rust type, otherwise we lose information // (e.g. `uint24` displays the same as `uint32` because both use `u32`) fn allowed_int_size(size: Option) -> bool { matches!(size.map_or(256, NonZeroU16::get), 8 | 16 | 32 | 64 | 128 | 256) } + let alloy_sol_types = &crates.sol_types; let tts = match *ty { - Type::Address(span, _) => quote_spanned! {span=> ::alloy_sol_types::private::Address }, + Type::Address(span, _) => quote_spanned! {span=> #alloy_sol_types::private::Address }, Type::Bool(span) => return Ident::new("bool", span).to_tokens(tokens), - Type::String(span) => quote_spanned! {span=> ::alloy_sol_types::private::String }, - Type::Bytes(span) => quote_spanned! {span=> ::alloy_sol_types::private::Vec }, + Type::String(span) => quote_spanned! {span=> #alloy_sol_types::private::String }, + Type::Bytes(span) => quote_spanned! {span=> #alloy_sol_types::private::Vec }, Type::FixedBytes(span, size) => { assert!(size.get() <= 32); let size = Literal::u16_unsuffixed(size.get()); - quote_spanned! {span=> ::alloy_sol_types::private::FixedBytes<#size> } + quote_spanned! {span=> #alloy_sol_types::private::FixedBytes<#size> } } Type::Int(span, size) | Type::Uint(span, size) if allowed_int_size(size) => { let size = size.map_or(256, NonZeroU16::get); @@ -115,8 +117,8 @@ pub fn rec_expand_rust_type(ty: &Type, tokens: &mut TokenStream) { } assert_eq!(size, 256); match ty { - Type::Int(..) => quote_spanned! {span=> ::alloy_sol_types::private::I256 }, - Type::Uint(..) => quote_spanned! {span=> ::alloy_sol_types::private::U256 }, + Type::Int(..) => quote_spanned! {span=> #alloy_sol_types::private::I256 }, + Type::Uint(..) => quote_spanned! {span=> #alloy_sol_types::private::U256 }, _ => unreachable!(), } } @@ -125,22 +127,22 @@ pub fn rec_expand_rust_type(ty: &Type, tokens: &mut TokenStream) { return tuple.paren_token.surround(tokens, |tokens| { for pair in tuple.types.pairs() { let (ty, comma) = pair.into_tuple(); - rec_expand_rust_type(ty, tokens); + rec_expand_rust_type(ty, crates, tokens); comma.to_tokens(tokens); } }) } Type::Array(ref array) => { - let ty = expand_rust_type(&array.ty); + let ty = expand_rust_type(&array.ty, crates); let span = array.span(); if let Some(size) = array.size() { quote_spanned! {span=> [#ty; #size] } } else { - quote_spanned! {span=> ::alloy_sol_types::private::Vec<#ty> } + quote_spanned! {span=> #alloy_sol_types::private::Vec<#ty> } } } Type::Function(ref function) => quote_spanned! {function.span()=> - ::alloy_sol_types::private::Function + #alloy_sol_types::private::Function }, Type::Mapping(ref mapping) => quote_spanned! {mapping.span()=> ::core::compile_error!("Mapping types are not supported here") @@ -149,8 +151,8 @@ pub fn rec_expand_rust_type(ty: &Type, tokens: &mut TokenStream) { // Exhaustive fallback to `SolType::RustType` ref ty @ (Type::Int(..) | Type::Uint(..) | Type::Custom(_)) => { let span = ty.span(); - let ty = expand_type(ty); - quote_spanned! {span=> <#ty as ::alloy_sol_types::SolType>::RustType } + let ty = expand_type(ty, crates); + quote_spanned! {span=> <#ty as #alloy_sol_types::SolType>::RustType } } }; tokens.extend(tts); diff --git a/crates/sol-macro/src/expand/udt.rs b/crates/sol-macro/src/expand/udt.rs index 4e56bc399..52256f733 100644 --- a/crates/sol-macro/src/expand/udt.rs +++ b/crates/sol-macro/src/expand/udt.rs @@ -13,118 +13,124 @@ pub(super) fn expand(cx: &ExpCtxt<'_>, udt: &ItemUdt) -> Result { let (sol_attrs, mut attrs) = crate::attr::SolAttrs::parse(attrs)?; cx.type_derives(&mut attrs, std::iter::once(ty), true); - let underlying_sol = expand_type(ty); - let underlying_rust = expand_rust_type(ty); + let underlying_sol = expand_type(ty, &cx.crates); + let underlying_rust = expand_rust_type(ty, &cx.crates); let type_check_body = if let Some(lit_str) = sol_attrs.type_check { let func_path: syn::Path = lit_str.parse()?; quote! { - <#underlying_sol as ::alloy_sol_types::SolType>::type_check(token)?; + <#underlying_sol as alloy_sol_types::SolType>::type_check(token)?; #func_path(token) } } else { quote! { - <#underlying_sol as ::alloy_sol_types::SolType>::type_check(token) + <#underlying_sol as alloy_sol_types::SolType>::type_check(token) } }; + let alloy_sol_types = &cx.crates.sol_types; + let tokens = quote! { #(#attrs)* #[allow(non_camel_case_types, non_snake_case)] #[derive(Clone)] pub struct #name(#underlying_rust); - #[automatically_derived] - impl ::alloy_sol_types::private::SolTypeValue<#name> for #underlying_rust { - #[inline] - fn stv_to_tokens(&self) -> <#underlying_sol as ::alloy_sol_types::SolType>::Token<'_> { - ::alloy_sol_types::private::SolTypeValue::<#underlying_sol>::stv_to_tokens(self) - } - - #[inline] - fn stv_eip712_data_word(&self) -> ::alloy_sol_types::Word { - <#underlying_sol as ::alloy_sol_types::SolType>::tokenize(self).0 - } - - #[inline] - fn stv_abi_encode_packed_to(&self, out: &mut ::alloy_sol_types::private::Vec) { - <#underlying_sol as ::alloy_sol_types::SolType>::abi_encode_packed_to(self, out) - } - } - - #[automatically_derived] - impl #name { - /// The Solidity type name. - pub const NAME: &'static str = stringify!(@name); - - /// Convert from the underlying value type. - #[inline] - pub const fn from(value: #underlying_rust) -> Self { - Self(value) - } - - /// Return the underlying value. - #[inline] - pub const fn into(self) -> #underlying_rust { - self.0 + const _: () = { + use #alloy_sol_types as alloy_sol_types; + + #[automatically_derived] + impl alloy_sol_types::private::SolTypeValue<#name> for #underlying_rust { + #[inline] + fn stv_to_tokens(&self) -> <#underlying_sol as alloy_sol_types::SolType>::Token<'_> { + alloy_sol_types::private::SolTypeValue::<#underlying_sol>::stv_to_tokens(self) + } + + #[inline] + fn stv_eip712_data_word(&self) -> alloy_sol_types::Word { + <#underlying_sol as alloy_sol_types::SolType>::tokenize(self).0 + } + + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut alloy_sol_types::private::Vec) { + <#underlying_sol as alloy_sol_types::SolType>::abi_encode_packed_to(self, out) + } } - /// Return the single encoding of this value, delegating to the - /// underlying type. - #[inline] - pub fn abi_encode(&self) -> ::alloy_sol_types::private::Vec { - ::abi_encode(&self.0) + #[automatically_derived] + impl #name { + /// The Solidity type name. + pub const NAME: &'static str = stringify!(@name); + + /// Convert from the underlying value type. + #[inline] + pub const fn from(value: #underlying_rust) -> Self { + Self(value) + } + + /// Return the underlying value. + #[inline] + pub const fn into(self) -> #underlying_rust { + self.0 + } + + /// Return the single encoding of this value, delegating to the + /// underlying type. + #[inline] + pub fn abi_encode(&self) -> alloy_sol_types::private::Vec { + ::abi_encode(&self.0) + } + + /// Return the packed encoding of this value, delegating to the + /// underlying type. + #[inline] + pub fn abi_encode_packed(&self) -> alloy_sol_types::private::Vec { + ::abi_encode_packed(&self.0) + } } - /// Return the packed encoding of this value, delegating to the - /// underlying type. - #[inline] - pub fn abi_encode_packed(&self) -> ::alloy_sol_types::private::Vec { - ::abi_encode_packed(&self.0) - } - } - - #[automatically_derived] - impl ::alloy_sol_types::SolType for #name { - type RustType = #underlying_rust; - type Token<'a> = <#underlying_sol as ::alloy_sol_types::SolType>::Token<'a>; + #[automatically_derived] + impl alloy_sol_types::SolType for #name { + type RustType = #underlying_rust; + type Token<'a> = <#underlying_sol as alloy_sol_types::SolType>::Token<'a>; - const SOL_NAME: &'static str = Self::NAME; - const ENCODED_SIZE: Option = <#underlying_sol as ::alloy_sol_types::SolType>::ENCODED_SIZE; + const SOL_NAME: &'static str = Self::NAME; + const ENCODED_SIZE: Option = <#underlying_sol as alloy_sol_types::SolType>::ENCODED_SIZE; - #[inline] - fn valid_token(token: &Self::Token<'_>) -> bool { - Self::type_check(token).is_ok() - } + #[inline] + fn valid_token(token: &Self::Token<'_>) -> bool { + Self::type_check(token).is_ok() + } - #[inline] - fn type_check(token: &Self::Token<'_>) -> ::alloy_sol_types::Result<()> { - #type_check_body - } + #[inline] + fn type_check(token: &Self::Token<'_>) -> alloy_sol_types::Result<()> { + #type_check_body + } - #[inline] - fn detokenize(token: Self::Token<'_>) -> Self::RustType { - <#underlying_sol as ::alloy_sol_types::SolType>::detokenize(token) + #[inline] + fn detokenize(token: Self::Token<'_>) -> Self::RustType { + <#underlying_sol as alloy_sol_types::SolType>::detokenize(token) + } } - } - #[automatically_derived] - impl ::alloy_sol_types::EventTopic for #name { - #[inline] - fn topic_preimage_length(rust: &Self::RustType) -> usize { - <#underlying_sol as ::alloy_sol_types::EventTopic>::topic_preimage_length(rust) + #[automatically_derived] + impl alloy_sol_types::EventTopic for #name { + #[inline] + fn topic_preimage_length(rust: &Self::RustType) -> usize { + <#underlying_sol as alloy_sol_types::EventTopic>::topic_preimage_length(rust) + } + + #[inline] + fn encode_topic_preimage(rust: &Self::RustType, out: &mut alloy_sol_types::private::Vec) { + <#underlying_sol as alloy_sol_types::EventTopic>::encode_topic_preimage(rust, out) + } + + #[inline] + fn encode_topic(rust: &Self::RustType) -> alloy_sol_types::abi::token::WordToken { + <#underlying_sol as alloy_sol_types::EventTopic>::encode_topic(rust) + } } - - #[inline] - fn encode_topic_preimage(rust: &Self::RustType, out: &mut ::alloy_sol_types::private::Vec) { - <#underlying_sol as ::alloy_sol_types::EventTopic>::encode_topic_preimage(rust, out) - } - - #[inline] - fn encode_topic(rust: &Self::RustType) -> ::alloy_sol_types::abi::token::WordToken { - <#underlying_sol as ::alloy_sol_types::EventTopic>::encode_topic(rust) - } - } + }; }; Ok(tokens) } diff --git a/crates/sol-macro/src/input.rs b/crates/sol-macro/src/input.rs index 424f23f47..d6e525f2d 100644 --- a/crates/sol-macro/src/input.rs +++ b/crates/sol-macro/src/input.rs @@ -141,14 +141,17 @@ impl SolInput { crate::expand::expand(file) } SolInputKind::Type(ty) => { - if attrs.is_empty() { - Ok(crate::expand::expand_type(&ty)) - } else { - Err(Error::new_spanned( - attrs.first().unwrap(), - "attributes are not allowed here", - )) + let (sol_attrs, rest) = crate::attr::SolAttrs::parse(&attrs)?; + if !rest.is_empty() { + return Err(Error::new_spanned( + rest.first().unwrap(), + "only `#[sol]` attributes are allowed here", + )); } + + let mut crates = crate::expand::ExternCrates::default(); + crates.fill(&sol_attrs); + Ok(crate::expand::expand_type(&ty, &crates)) } #[cfg(feature = "json")] SolInputKind::Json(name, json) => crate::json::expand(name, json, attrs), diff --git a/crates/sol-macro/src/lib.rs b/crates/sol-macro/src/lib.rs index aef92714f..9eb428c80 100644 --- a/crates/sol-macro/src/lib.rs +++ b/crates/sol-macro/src/lib.rs @@ -122,6 +122,11 @@ mod json; /// - `pub fn errors() -> BTreeMap>` /// - items: generates implementations of the `SolAbiExt` trait, alongside the existing /// [`alloy-sol-types`] traits +/// - `alloy_sol_types = ` (inner attribute only): specifies the path to +/// the required dependency [`alloy-sol-types`]. +/// - `alloy_contract = ` (inner attribute only): specifies the path to the +/// optional dependency [`alloy-contract`]. This is only used in combination with the `rpc` +/// attribute. /// - `all_derives [ = ]`: adds all possible `#[derive(...)]` attributes to all /// generated types. May significantly increase compile times due to all the extra generated code. /// This is the default behavior of [`abigen`] @@ -130,12 +135,12 @@ mod json; /// compile times due to all the extra generated code. This is the default behavior of [`abigen`] /// - `docs [ = ]`: adds doc comments to all generated types. This is the default /// behavior of [`abigen`] -/// - `bytecode = `: specifies the creation/init bytecode of a contract. This -/// will emit a `static` item with the specified bytes. -/// - `deployed_bytecode = `: specifies the deployed bytecode of a contract. -/// This will emit a `static` item with the specified bytes. -/// - `type_check = `: specifies a function to be used to check an User Defined -/// Type. +/// - `bytecode = ` (contract-like only): specifies the creation/init bytecode +/// of a contract. This will emit a `static` item with the specified bytes. +/// - `deployed_bytecode = ` (contract-like only): specifies the deployed +/// bytecode of a contract. This will emit a `static` item with the specified bytes. +/// - `type_check = ` (UDVT only): specifies a function to be used to check an User +/// Defined Type. /// /// ### Structs and enums /// diff --git a/crates/sol-macro/src/verbatim.rs b/crates/sol-macro/src/verbatim.rs index 72b590c20..38b5e7540 100644 --- a/crates/sol-macro/src/verbatim.rs +++ b/crates/sol-macro/src/verbatim.rs @@ -1,116 +1,88 @@ +use crate::expand::ExternCrates; use proc_macro2::TokenStream; use quote::quote; use std::collections::BTreeMap; /// Converts the given value into tokens that represent itself. -pub fn verbatim(t: &T) -> TokenStream { - t.to_verbatim_token_stream() +pub(crate) fn verbatim(t: &T, crates: &ExternCrates) -> TokenStream { + let mut s = TokenStream::new(); + t.to_verbatim_tokens(&mut s, crates); + s } /// Conversion to tokens that represent the value itself. -pub trait Verbatim { +pub(crate) trait Verbatim { /// Converts `self` into tokens that represent itself. - fn to_verbatim_tokens(&self, s: &mut TokenStream); - - /// Converts `self` into a [`TokenStream`] that represents itself. - fn to_verbatim_token_stream(&self) -> TokenStream { - let mut s = TokenStream::new(); - self.to_verbatim_tokens(&mut s); - s - } - - /// Uses [`Verbatim::to_verbatim_tokens`] to provide a [`quote::ToTokens`] implementation. - #[inline] - #[allow(dead_code)] - fn quote_verbatim(&self) -> ToTokensCompat<'_, Self> { - ToTokensCompat(self) - } - - /// Uses [`Verbatim::to_verbatim_tokens`] to provide a [`quote::ToTokens`] implementation. - #[inline] - #[allow(dead_code)] - fn into_quote_verbatim(self) -> IntoTokensCompat - where - Self: Sized, - { - IntoTokensCompat(self) - } + fn to_verbatim_tokens(&self, s: &mut TokenStream, crates: &ExternCrates); } /// Provides a [`quote::ToTokens`] implementations for references of values that implement /// [`Verbatim`]. -pub struct ToTokensCompat<'a, T: ?Sized + Verbatim>(pub &'a T); +pub(crate) struct ToTokensCompat<'a, T: ?Sized + Verbatim>(pub &'a T, &'a ExternCrates); impl quote::ToTokens for ToTokensCompat<'_, T> { #[inline] fn to_tokens(&self, tokens: &mut TokenStream) { - self.0.to_verbatim_tokens(tokens) - } -} - -/// Provides a [`quote::ToTokens`] implementations for owned values that implement [`Verbatim`]. -pub struct IntoTokensCompat(pub T); - -impl quote::ToTokens for IntoTokensCompat { - #[inline] - fn to_tokens(&self, tokens: &mut TokenStream) { - self.0.to_verbatim_tokens(tokens) + self.0.to_verbatim_tokens(tokens, self.1) } } impl Verbatim for String { - fn to_verbatim_tokens(&self, tokens: &mut TokenStream) { + fn to_verbatim_tokens(&self, tokens: &mut TokenStream, crates: &ExternCrates) { + let alloy_sol_types = &crates.sol_types; tokens.extend(if self.is_empty() { - quote!(::alloy_sol_types::private::String::new()) + quote!(#alloy_sol_types::private::String::new()) } else { - quote!(::alloy_sol_types::private::ToOwned::to_owned(#self)) + quote!(#alloy_sol_types::private::ToOwned::to_owned(#self)) }) } } impl Verbatim for bool { #[inline] - fn to_verbatim_tokens(&self, s: &mut TokenStream) { + fn to_verbatim_tokens(&self, s: &mut TokenStream, _crates: &ExternCrates) { quote::ToTokens::to_tokens(self, s) } } impl Verbatim for usize { #[inline] - fn to_verbatim_tokens(&self, s: &mut TokenStream) { + fn to_verbatim_tokens(&self, s: &mut TokenStream, _crates: &ExternCrates) { quote::ToTokens::to_tokens(self, s) } } impl Verbatim for Vec { - fn to_verbatim_tokens(&self, s: &mut TokenStream) { + fn to_verbatim_tokens(&self, s: &mut TokenStream, crates: &ExternCrates) { + let alloy_sol_types = &crates.sol_types; s.extend(if self.is_empty() { - quote!(::alloy_sol_types::private::Vec::new()) + quote!(#alloy_sol_types::private::Vec::new()) } else { - let iter = self.iter().map(ToTokensCompat); - quote!(::alloy_sol_types::private::vec![#(#iter),*]) + let iter = self.iter().map(|t| ToTokensCompat(t, crates)); + quote!(#alloy_sol_types::private::vec![#(#iter),*]) }); } } impl Verbatim for BTreeMap { - fn to_verbatim_tokens(&self, s: &mut TokenStream) { + fn to_verbatim_tokens(&self, s: &mut TokenStream, crates: &ExternCrates) { + let alloy_sol_types = &crates.sol_types; s.extend(if self.is_empty() { - quote!(::alloy_sol_types::private::BTreeMap::new()) + quote!(#alloy_sol_types::private::BTreeMap::new()) } else { - let k = self.keys().map(ToTokensCompat); - let v = self.values().map(ToTokensCompat); - quote!(::alloy_sol_types::private::BTreeMap::from([#( (#k, #v) ),*])) + let k = self.keys().map(|t| ToTokensCompat(t, crates)); + let v = self.values().map(|t| ToTokensCompat(t, crates)); + quote!(#alloy_sol_types::private::BTreeMap::from([#( (#k, #v) ),*])) }); } } impl Verbatim for Option { - fn to_verbatim_tokens(&self, s: &mut TokenStream) { + fn to_verbatim_tokens(&self, s: &mut TokenStream, crates: &ExternCrates) { let tts = match self { Some(t) => { let mut s = TokenStream::new(); - t.to_verbatim_tokens(&mut s); + t.to_verbatim_tokens(&mut s, crates); quote!(::core::option::Option::Some(#s)) } None => quote!(::core::option::Option::None), @@ -124,13 +96,14 @@ macro_rules! derive_verbatim { (struct $name:ident { $($field:ident),* $(,)? } $($rest:tt)*) => { impl Verbatim for alloy_json_abi::$name { - fn to_verbatim_tokens(&self, s: &mut TokenStream) { + fn to_verbatim_tokens(&self, s: &mut TokenStream, crates: &ExternCrates) { let Self { $($field),* } = self; $( - let $field = ToTokensCompat($field); + let $field = ToTokensCompat($field, crates); )* + let alloy_sol_types = &crates.sol_types; s.extend(quote! { - ::alloy_sol_types::private::alloy_json_abi::$name { + #alloy_sol_types::private::alloy_json_abi::$name { $($field: #$field,)* } }); @@ -141,14 +114,15 @@ macro_rules! derive_verbatim { (enum $name:ident { $($variant:ident $( { $($field_idx:tt : $field:ident),* $(,)? } )?),* $(,)? } $($rest:tt)*) => { impl Verbatim for alloy_json_abi::$name { - fn to_verbatim_tokens(&self, s: &mut TokenStream) { + fn to_verbatim_tokens(&self, s: &mut TokenStream, crates: &ExternCrates) { match self {$( Self::$variant $( { $($field_idx: $field),* } )? => { $($( - let $field = ToTokensCompat($field); + let $field = ToTokensCompat($field, crates); )*)? + let alloy_sol_types = &crates.sol_types; s.extend(quote! { - ::alloy_sol_types::private::alloy_json_abi::$name::$variant $( { $($field_idx: #$field),* } )? + #alloy_sol_types::private::alloy_json_abi::$name::$variant $( { $($field_idx: #$field),* } )? }); } )*} diff --git a/tests/core-sol/Cargo.toml b/tests/core-sol/Cargo.toml new file mode 100644 index 000000000..7f9b7d03e --- /dev/null +++ b/tests/core-sol/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "alloy-core-sol-test" +publish = false + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +exclude.workspace = true + +[dependencies] +alloy-core = { workspace = true, features = ["sol-types", "json"] } diff --git a/tests/core-sol/src/lib.rs b/tests/core-sol/src/lib.rs new file mode 100644 index 000000000..6bf1dec33 --- /dev/null +++ b/tests/core-sol/src/lib.rs @@ -0,0 +1,65 @@ +//! Tests `#[sol(alloy_sol_types = ...)]`. +//! +//! This has to be in a separate crate where `alloy_sol_types` is not provided as a dependency. + +#![no_std] + +use alloy_core::sol; + +type _MyUint = sol!(uint32); +type _MyTuple = sol!((_MyUint, bytes, bool, string, bytes32, (address, uint64))); + +sol! { + #![sol(abi)] + + enum MyEnum { + A, B + } + + struct MyStruct { + uint32 a; + uint64 b; + } + + event MyEvent(MyEnum a, MyStruct indexed b, bytes c, string indexed d, bytes32 indexed e); + + error MyError(uint32 a, MyStruct b); + + constructor myConstructor(address); + + function myFunction(MyStruct a, bytes b) returns(uint32); + + modifier myModifier(bool a, string b); + + mapping(bytes32 a => bool b) myMapping; + + type MyType is uint32; +} + +sol! { + + contract MyContract { + enum MyOtherEnum { + A, B + } + + struct MyOtherStruct { + uint32 a; + uint32 b; + } + + event MyOtherEvent(MyOtherEnum indexed a, MyOtherStruct b, (bool, string) indexed c); + + error MyOtherError(uint32 a, MyOtherStruct b); + + constructor myOtherConstructor(address); + + function myOtherFunction(MyOtherStruct a, bytes b) returns(uint32); + + modifier myOtherModifier(bool a, string b); + + mapping(bytes32 a => bool b) myOtherMapping; + + type MyOtherType is uint32; + } +}