1use super::{ExpCtxt, anon_name};
4use crate::utils::ExprArray;
5use alloy_sol_macro_input::{ContainsSolAttrs, docs_str, mk_doc};
6use ast::{Item, ItemContract, ItemError, ItemEvent, ItemFunction, SolIdent, Spanned};
7use heck::ToSnakeCase;
8use proc_macro2::{Ident, Span, TokenStream};
9use quote::{format_ident, quote};
10use syn::{Attribute, Result, parse_quote};
11
12pub(super) fn expand(cx: &mut ExpCtxt<'_>, contract: &ItemContract) -> Result<TokenStream> {
32 let ItemContract { name, body, .. } = contract;
33
34 let (sol_attrs, attrs) = contract.split_attrs()?;
35
36 let extra_methods = sol_attrs.extra_methods.or(cx.attrs.extra_methods).unwrap_or(false);
37 let rpc = sol_attrs.rpc.or(cx.attrs.rpc).unwrap_or(false);
38 let abi = sol_attrs.abi.or(cx.attrs.abi).unwrap_or(false);
39 let docs = sol_attrs.docs.or(cx.attrs.docs).unwrap_or(true);
40
41 let bytecode = sol_attrs.bytecode.as_ref().map(|lit| {
42 let name = Ident::new("BYTECODE", lit.span());
43 let hex = lit.value();
44 let bytes = hex::decode(&hex).unwrap();
45 let lit_bytes = proc_macro2::Literal::byte_string(&bytes).with_span(lit.span());
46 quote! {
47 #[doc = #hex]
51 #[rustfmt::skip]
53 #[allow(clippy::all)]
54 pub static #name: alloy_sol_types::private::Bytes =
55 alloy_sol_types::private::Bytes::from_static(#lit_bytes);
56 }
57 });
58 let deployed_bytecode = sol_attrs.deployed_bytecode.as_ref().map(|lit| {
59 let name = Ident::new("DEPLOYED_BYTECODE", lit.span());
60 let hex = lit.value();
61 let bytes = hex::decode(&hex).unwrap();
62 let lit_bytes = proc_macro2::Literal::byte_string(&bytes).with_span(lit.span());
63 quote! {
64 #[doc = #hex]
68 #[rustfmt::skip]
70 #[allow(clippy::all)]
71 pub static #name: alloy_sol_types::private::Bytes =
72 alloy_sol_types::private::Bytes::from_static(#lit_bytes);
73 }
74 });
75
76 let mut constructor = None;
77 let mut fallback = None;
78 let mut receive = None;
79 let mut functions = Vec::with_capacity(contract.body.len());
80 let mut errors = Vec::with_capacity(contract.body.len());
81 let mut events = Vec::with_capacity(contract.body.len());
82
83 let (mut mod_attrs, item_attrs) =
84 attrs.into_iter().partition::<Vec<_>, _>(|a| a.path().is_ident("doc"));
85 mod_attrs.extend(item_attrs.iter().filter(|a| !a.path().is_ident("derive")).cloned());
86
87 let mut item_tokens = TokenStream::new();
89 let prev_cx_attrs = cx.attrs.clone();
90 cx.attrs.merge(&sol_attrs);
91 for item in body {
92 match item {
93 Item::Function(function) => match function.kind {
94 ast::FunctionKind::Function(_) if function.name.is_some() => {
95 functions.push(function.clone());
96 }
97 ast::FunctionKind::Function(_) => {}
98 ast::FunctionKind::Modifier(_) => {}
99 ast::FunctionKind::Constructor(_) => {
100 if constructor.is_none() {
101 constructor = Some(function);
102 } else {
103 let msg = "duplicate constructor";
104 return Err(syn::Error::new(function.span(), msg));
105 }
106 }
107 ast::FunctionKind::Fallback(_) => {
108 if fallback.is_none() {
109 fallback = Some(function);
110 } else {
111 let msg = "duplicate fallback function";
112 return Err(syn::Error::new(function.span(), msg));
113 }
114 }
115 ast::FunctionKind::Receive(_) => {
116 if receive.is_none() {
117 receive = Some(function);
118 } else {
119 let msg = "duplicate receive function";
120 return Err(syn::Error::new(function.span(), msg));
121 }
122 }
123 },
124 Item::Error(error) => errors.push(error),
125 Item::Event(event) => events.push(event),
126 Item::Variable(var_def) => {
127 if let Some(function) = super::var_def::var_as_function(cx, var_def)? {
128 functions.push(function);
129 }
130 }
131 _ => {}
132 }
133
134 if item.attrs().is_none() || item_attrs.is_empty() {
135 item_tokens.extend(cx.expand_item(item)?);
137 } else {
138 let mut item = item.clone();
140 item.attrs_mut().expect("is_none checked above").splice(0..0, item_attrs.clone());
141 item_tokens.extend(cx.expand_item(&item)?);
142 }
143 }
144
145 let mut enum_attrs = item_attrs;
147 for attr in &mut enum_attrs {
148 if !attr.path().is_ident("derive") {
149 continue;
150 }
151
152 let derives = alloy_sol_macro_input::parse_derives(attr);
153 let mut derives = derives.into_iter().collect::<Vec<_>>();
154 if derives.is_empty() {
155 continue;
156 }
157
158 let len = derives.len();
159 derives.retain(|derive| !derive.is_ident("Default"));
160 if derives.len() == len {
161 continue;
162 }
163
164 attr.meta = parse_quote! { derive(#(#derives),*) };
165 }
166
167 let enum_expander = CallLikeExpander { cx, contract_name: name.clone(), extra_methods };
168 let errors_enum = (!errors.is_empty()).then(|| {
169 let mut attrs = enum_attrs.clone();
170 let doc_str = format!("Container for all the [`{name}`](self) custom errors.");
171 attrs.push(parse_quote!(#[doc = #doc_str]));
172 attrs.push(parse_quote!(#[derive(Clone)]));
173 let enum_tokens = enum_expander.expand(ToExpand::Errors(&errors), attrs);
174 let builders = generate_error_builders(name, &errors, cx);
175 quote! {
176 #enum_tokens
177 #builders
178 }
179 });
180
181 let events_enum = (!events.is_empty()).then(|| {
182 let mut attrs = enum_attrs.clone();
183 let doc_str = format!("Container for all the [`{name}`](self) events.");
184 attrs.push(parse_quote!(#[doc = #doc_str]));
185 attrs.push(parse_quote!(#[derive(Clone)]));
186 let enum_tokens = enum_expander.expand(ToExpand::Events(&events), attrs);
187 let builders = generate_event_builders(name, &events, cx);
188 quote! {
189 #enum_tokens
190 #builders
191 }
192 });
193
194 let functions_enum = (!functions.is_empty()).then(|| {
195 let mut attrs = enum_attrs;
196 let doc_str = format!("Container for all the [`{name}`](self) function calls.");
197 attrs.push(parse_quote!(#[doc = #doc_str]));
198 attrs.push(parse_quote!(#[derive(Clone)]));
199 let enum_expander = CallLikeExpander { cx, contract_name: name.clone(), extra_methods };
200 enum_expander.expand(ToExpand::Functions(&functions), attrs)
201 });
202
203 cx.attrs = prev_cx_attrs;
208
209 let mod_descr_doc = (docs && docs_str(&mod_attrs).trim().is_empty())
210 .then(|| mk_doc("Module containing a contract's types and functions."));
211 let mod_iface_doc = (docs && !docs_str(&mod_attrs).contains("```solidity\n"))
212 .then(|| mk_doc(format!("\n\n```solidity\n{contract}\n```")));
213
214 let abi = abi.then(|| {
215 if_json! {
216 use crate::verbatim::verbatim;
217 use super::to_abi;
218
219 let crates = &cx.crates;
220 let constructor = verbatim(&constructor.map(|x| to_abi::constructor(x, cx)), crates);
221 let fallback = verbatim(&fallback.map(|x| to_abi::fallback(x, cx)), crates);
222 let receive = verbatim(&receive.map(|x| to_abi::receive(x, cx)), crates);
223 let functions_map = to_abi::functions_map(&functions, cx);
224 let events_map = to_abi::events_map(&events, cx);
225 let errors_map = to_abi::errors_map(&errors, cx);
226 quote! {
227 pub mod abi {
229 use super::*;
230 use alloy_sol_types::private::{alloy_json_abi as json, BTreeMap, String, Vec};
231
232 pub fn contract() -> json::JsonAbi {
234 json::JsonAbi {
235 constructor: constructor(),
236 fallback: fallback(),
237 receive: receive(),
238 functions: functions(),
239 events: events(),
240 errors: errors(),
241 }
242 }
243
244 pub fn constructor() -> Option<json::Constructor> {
246 #constructor
247 }
248
249 pub fn fallback() -> Option<json::Fallback> {
251 #fallback
252 }
253
254 pub fn receive() -> Option<json::Receive> {
256 #receive
257 }
258
259 pub fn functions() -> BTreeMap<String, Vec<json::Function>> {
261 #functions_map
262 }
263
264 pub fn events() -> BTreeMap<String, Vec<json::Event>> {
266 #events_map
267 }
268
269 pub fn errors() -> BTreeMap<String, Vec<json::Error>> {
271 #errors_map
272 }
273 }
274 }
275 }
276 });
277
278 let rpc = rpc.then(|| {
279 let contract_name = name;
280 let name = format_ident!("{contract_name}Instance");
281 let name_s = name.to_string();
282 let methods = functions.iter().map(|f| call_builder_method(f, cx));
283 let new_fn_doc = format!(
284 "Creates a new wrapper around an on-chain [`{contract_name}`](self) contract instance.\n\
285 \n\
286 See the [wrapper's documentation](`{name}`) for more details."
287 );
288 let struct_doc = format!(
289 "A [`{contract_name}`](self) instance.\n\
290 \n\
291 Contains type-safe methods for interacting with an on-chain instance of the\n\
292 [`{contract_name}`](self) contract located at a given `address`, using a given\n\
293 provider `P`.\n\
294 \n\
295 If the contract bytecode is available (see the [`sol!`](alloy_sol_types::sol!)\n\
296 documentation on how to provide it), the `deploy` and `deploy_builder` methods can\n\
297 be used to deploy a new instance of the contract.\n\
298 \n\
299 See the [module-level documentation](self) for all the available methods."
300 );
301 let (deploy_fn, deploy_method) = bytecode.is_some().then(|| {
302 let deploy_doc_str =
303 "Deploys this contract using the given `provider` and constructor arguments, if any.\n\
304 \n\
305 Returns a new instance of the contract, if the deployment was successful.\n\
306 \n\
307 For more fine-grained control over the deployment process, use [`deploy_builder`] instead.";
308 let deploy_doc = mk_doc(deploy_doc_str);
309
310 let deploy_builder_doc_str =
311 "Creates a `RawCallBuilder` for deploying this contract using the given `provider`\n\
312 and constructor arguments, if any.\n\
313 \n\
314 This is a simple wrapper around creating a `RawCallBuilder` with the data set to\n\
315 the bytecode concatenated with the constructor's ABI-encoded arguments.";
316 let deploy_builder_doc = mk_doc(deploy_builder_doc_str);
317
318 let (params, args) = constructor.and_then(|c| {
319 if c.parameters.is_empty() {
320 return None;
321 }
322
323 let names1 = c.parameters.names().enumerate().map(anon_name);
324 let names2 = names1.clone();
325 let tys = c.parameters.types().map(|ty| {
326 cx.expand_rust_type(ty)
327 });
328 Some((quote!(#(#names1: #tys),*), quote!(#(#names2,)*)))
329 }).unzip();
330 let deploy_builder_data = if matches!(constructor, Some(c) if !c.parameters.is_empty()) {
331 quote! {
332 [
333 &BYTECODE[..],
334 &alloy_sol_types::SolConstructor::abi_encode(&constructorCall { #args })[..]
335 ].concat().into()
336 }
337 } else {
338 quote! {
339 ::core::clone::Clone::clone(&BYTECODE)
340 }
341 };
342
343 (
344 quote! {
345 #deploy_doc
346 #[inline]
347 pub fn deploy<P: alloy_contract::private::Provider<N>, N: alloy_contract::private::Network>(__provider: P, #params)
348 -> impl ::core::future::Future<Output = alloy_contract::Result<#name<P, N>>>
349 {
350 #name::<P, N>::deploy(__provider, #args)
351 }
352
353 #deploy_builder_doc
354 #[inline]
355 pub fn deploy_builder<P: alloy_contract::private::Provider<N>, N: alloy_contract::private::Network>(__provider: P, #params)
356 -> alloy_contract::RawCallBuilder<P, N>
357 {
358 #name::<P, N>::deploy_builder(__provider, #args)
359 }
360 },
361 quote! {
362 #deploy_doc
363 #[inline]
364 pub async fn deploy(__provider: P, #params)
365 -> alloy_contract::Result<#name<P, N>>
366 {
367 let call_builder = Self::deploy_builder(__provider, #args);
368 let contract_address = call_builder.deploy().await?;
369 Ok(Self::new(contract_address, call_builder.provider))
370 }
371
372 #deploy_builder_doc
373 #[inline]
374 pub fn deploy_builder(__provider: P, #params)
375 -> alloy_contract::RawCallBuilder<P, N>
376 {
377 alloy_contract::RawCallBuilder::new_raw_deploy(__provider, #deploy_builder_data)
378 }
379 },
380 )
381 }).unzip();
382
383 let filter_methods = events.iter().map(|&e| {
384 let event_name = cx.overloaded_name(e.into());
385 let name = format_ident!("{event_name}_filter");
386 let doc = format!(
387 "Creates a new event filter for the [`{event_name}`] event.",
388 );
389 quote! {
390 #[doc = #doc]
391 pub fn #name(&self) -> alloy_contract::Event<&P, #event_name, N> {
392 self.event_filter::<#event_name>()
393 }
394 }
395 });
396
397 let alloy_contract = &cx.crates.contract;
398
399 let generic_p_n = quote!(<P: alloy_contract::private::Provider<N>, N: alloy_contract::private::Network>);
400
401 quote! {
403 use #alloy_contract as alloy_contract;
404
405 #[doc = #new_fn_doc]
406 #[inline]
407 pub const fn new #generic_p_n(
408 address: alloy_sol_types::private::Address,
409 __provider: P,
410 ) -> #name<P, N> {
411 #name::<P, N>::new(address, __provider)
412 }
413
414 #deploy_fn
415
416 #[doc = #struct_doc]
417 #[derive(Clone)]
418 pub struct #name<P, N = alloy_contract::private::Ethereum> {
419 address: alloy_sol_types::private::Address,
420 provider: P,
421 _network: ::core::marker::PhantomData<N>,
422 }
423
424 #[automatically_derived]
425 impl<P, N> ::core::fmt::Debug for #name<P, N> {
426 #[inline]
427 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
428 f.debug_tuple(#name_s).field(&self.address).finish()
429 }
430 }
431
432 impl #generic_p_n #name<P, N> {
434 #[doc = #new_fn_doc]
435 #[inline]
436 pub const fn new(address: alloy_sol_types::private::Address, __provider: P) -> Self {
437 Self { address, provider: __provider, _network: ::core::marker::PhantomData }
438 }
439
440 #deploy_method
441
442 #[inline]
444 pub const fn address(&self) -> &alloy_sol_types::private::Address {
445 &self.address
446 }
447
448 #[inline]
450 pub fn set_address(&mut self, address: alloy_sol_types::private::Address) {
451 self.address = address;
452 }
453
454 pub fn at(mut self, address: alloy_sol_types::private::Address) -> Self {
456 self.set_address(address);
457 self
458 }
459
460 #[inline]
462 pub const fn provider(&self) -> &P {
463 &self.provider
464 }
465 }
466
467 impl<P: ::core::clone::Clone, N> #name<&P, N> {
468 #[inline]
470 pub fn with_cloned_provider(self) -> #name<P, N> {
471 #name { address: self.address, provider: ::core::clone::Clone::clone(&self.provider), _network: ::core::marker::PhantomData }
472 }
473 }
474
475 impl #generic_p_n #name<P, N> {
477 pub fn call_builder<C: alloy_sol_types::SolCall>(&self, call: &C)
482 -> alloy_contract::SolCallBuilder<&P, C, N>
483 {
484 alloy_contract::SolCallBuilder::new_sol(&self.provider, &self.address, call)
485 }
486
487 #(#methods)*
488 }
489
490 impl #generic_p_n #name<P, N> {
492 pub fn event_filter<E: alloy_sol_types::SolEvent>(&self)
497 -> alloy_contract::Event<&P, E, N>
498 {
499 alloy_contract::Event::new_sol(&self.provider, &self.address)
500 }
501
502 #(#filter_methods)*
503 }
504 }
505 });
506
507 let alloy_sol_types = &cx.crates.sol_types;
508
509 let tokens = quote! {
510 #mod_descr_doc
511 #(#mod_attrs)*
512 #mod_iface_doc
513 #[allow(non_camel_case_types, non_snake_case, clippy::pub_underscore_fields, clippy::style, clippy::empty_structs_with_brackets)]
514 pub mod #name {
515 use super::*;
516 use #alloy_sol_types as alloy_sol_types;
517
518 #bytecode
519 #deployed_bytecode
520
521 #item_tokens
522
523 #functions_enum
524 #errors_enum
525 #events_enum
526
527 #abi
528
529 #rpc
530 }
531 };
532 Ok(tokens)
533}
534
535struct CallLikeExpander<'a> {
569 cx: &'a ExpCtxt<'a>,
570 contract_name: SolIdent,
571 extra_methods: bool,
572}
573
574#[derive(Clone, Debug)]
575struct ExpandData {
576 name: Ident,
577 variants: Vec<Ident>,
578 types: Option<Vec<Ident>>,
579 min_data_len: usize,
580 trait_: Ident,
581 selectors: Vec<ExprArray<u8>>,
582 can_derive_builtin: bool,
588}
589
590impl ExpandData {
591 fn types(&self) -> &Vec<Ident> {
592 let types = self.types.as_ref().unwrap_or(&self.variants);
593 assert_eq!(types.len(), self.variants.len());
594 types
595 }
596
597 fn sort_by_selector(&mut self) {
598 let len = self.selectors.len();
599 if len <= 1 {
600 return;
601 }
602
603 let prev = self.selectors.clone();
604 self.selectors.sort_unstable();
605 if len <= 20 && prev == self.selectors {
607 return;
608 }
609
610 let old_variants = self.variants.clone();
611 let old_types = self.types.clone();
612 let new_idxs =
613 prev.iter().map(|selector| self.selectors.iter().position(|s| s == selector).unwrap());
614 for (old, new) in new_idxs.enumerate() {
615 if old == new {
616 continue;
617 }
618
619 self.variants[new] = old_variants[old].clone();
620 if let Some(types) = self.types.as_mut() {
621 types[new] = old_types.as_ref().unwrap()[old].clone();
622 }
623 }
624 }
625}
626
627enum ToExpand<'a> {
628 Functions(&'a [ItemFunction]),
629 Errors(&'a [&'a ItemError]),
630 Events(&'a [&'a ItemEvent]),
631}
632
633impl ToExpand<'_> {
634 fn to_data(&self, expander: &CallLikeExpander<'_>) -> ExpandData {
635 let &CallLikeExpander { cx, ref contract_name, .. } = expander;
636 match self {
637 Self::Functions(functions) => {
638 let variants: Vec<_> =
639 functions.iter().map(|f| cx.overloaded_name(f.into()).0).collect();
640
641 let types: Vec<_> = variants.iter().map(|name| cx.raw_call_name(name)).collect();
642
643 ExpandData {
644 name: format_ident!("{contract_name}Calls"),
645 variants,
646 types: Some(types),
647 min_data_len: functions
648 .iter()
649 .map(|function| cx.params_base_data_size(&function.parameters))
650 .min()
651 .unwrap(),
652 trait_: format_ident!("SolCall"),
653 selectors: functions.iter().map(|f| cx.function_selector(f)).collect(),
654 can_derive_builtin: functions
655 .iter()
656 .all(|f| f.parameters.types().all(|ty| cx.can_derive_builtin_traits(ty))),
657 }
658 }
659
660 Self::Errors(errors) => ExpandData {
661 name: format_ident!("{contract_name}Errors"),
662 variants: errors.iter().map(|&error| cx.overloaded_name(error.into()).0).collect(),
663 types: None,
664 min_data_len: errors
665 .iter()
666 .map(|error| cx.params_base_data_size(&error.parameters))
667 .min()
668 .unwrap(),
669 trait_: format_ident!("SolError"),
670 selectors: errors.iter().map(|e| cx.error_selector(e)).collect(),
671 can_derive_builtin: errors.iter().all(|&error| {
672 error.parameters.types().all(|ty| cx.can_derive_builtin_traits(ty))
673 }),
674 },
675
676 Self::Events(events) => {
677 let variants: Vec<_> =
678 events.iter().map(|&event| cx.overloaded_name(event.into()).0).collect();
679
680 ExpandData {
681 name: format_ident!("{contract_name}Events"),
682 variants,
683 types: None,
684 min_data_len: events
685 .iter()
686 .map(|event| cx.params_base_data_size(&event.params()))
687 .min()
688 .unwrap(),
689 trait_: format_ident!("SolEvent"),
690 selectors: events.iter().map(|e| cx.event_selector(e)).collect(),
691 can_derive_builtin: events.iter().all(|&event| {
692 event.parameters.iter().all(|p| cx.can_derive_builtin_traits(&p.ty))
693 }),
694 }
695 }
696 }
697 }
698}
699
700impl CallLikeExpander<'_> {
701 fn expand(&self, to_expand: ToExpand<'_>, attrs: Vec<Attribute>) -> TokenStream {
702 let data = &to_expand.to_data(self);
703
704 let mut sorted_data = data.clone();
705 sorted_data.sort_by_selector();
706 #[cfg(debug_assertions)]
707 for (i, sv) in sorted_data.variants.iter().enumerate() {
708 let s = &sorted_data.selectors[i];
709
710 let normal_pos = data.variants.iter().position(|v| v == sv).unwrap();
711 let ns = &data.selectors[normal_pos];
712 assert_eq!(s, ns);
713 }
714
715 if let ToExpand::Events(events) = to_expand {
716 return self.expand_events(events, data, &sorted_data, attrs);
717 }
718
719 let def = self.generate_enum(data, &sorted_data, attrs);
720 let ExpandData { name, variants, min_data_len, trait_, .. } = data;
721 let types = data.types();
722 let name_s = name.to_string();
723 let count = data.variants.len();
724
725 let sorted_variants = &sorted_data.variants;
726 let sorted_types = sorted_data.types();
727
728 quote! {
729 #def
730
731 #[automatically_derived]
732 impl alloy_sol_types::SolInterface for #name {
733 const NAME: &'static str = #name_s;
734 const MIN_DATA_LENGTH: usize = #min_data_len;
735 const COUNT: usize = #count;
736
737 #[inline]
738 fn selector(&self) -> [u8; 4] {
739 match self {#(
740 Self::#variants(_) => <#types as alloy_sol_types::#trait_>::SELECTOR,
741 )*}
742 }
743
744 #[inline]
745 fn selector_at(i: usize) -> ::core::option::Option<[u8; 4]> {
746 Self::SELECTORS.get(i).copied()
747 }
748
749 #[inline]
750 fn valid_selector(selector: [u8; 4]) -> bool {
751 Self::SELECTORS.binary_search(&selector).is_ok()
752 }
753
754 #[inline]
755 #[allow(non_snake_case)]
756 fn abi_decode_raw(
757 selector: [u8; 4],
758 data: &[u8],
759 )-> alloy_sol_types::Result<Self> {
760 Self::abi_decode_raw_with_config(
761 selector,
762 data,
763 alloy_sol_types::abi::AbiDecoderConfig::default(),
764 )
765 }
766
767 #[inline]
768 #[allow(non_snake_case)]
769 fn abi_decode_raw_with_config(
770 selector: [u8; 4],
771 data: &[u8],
772 config: alloy_sol_types::abi::AbiDecoderConfig,
773 ) -> alloy_sol_types::Result<Self> {
774 static DECODE_SHIMS: &[fn(
775 &[u8],
776 alloy_sol_types::abi::AbiDecoderConfig,
777 ) -> alloy_sol_types::Result<#name>] = &[
778 #({
779 fn #sorted_variants(
780 data: &[u8],
781 config: alloy_sol_types::abi::AbiDecoderConfig,
782 ) -> alloy_sol_types::Result<#name> {
783 <#sorted_types as alloy_sol_types::#trait_>::abi_decode_raw_with_config(
784 data,
785 config,
786 )
787 .map(#name::#sorted_variants)
788 }
789 #sorted_variants
790 }),*
791 ];
792
793 let Ok(idx) = Self::SELECTORS.binary_search(&selector) else {
794 return Err(alloy_sol_types::Error::unknown_selector(
795 <Self as alloy_sol_types::SolInterface>::NAME,
796 selector,
797 ));
798 };
799 DECODE_SHIMS[idx](data, config)
801 }
802
803 #[inline]
804 #[allow(non_snake_case)]
805 fn abi_decode_raw_validate(
808 selector: [u8; 4],
809 data: &[u8],
810 ) -> alloy_sol_types::Result<Self> {
811 Self::abi_decode_raw_with_config(
812 selector,
813 data,
814 alloy_sol_types::abi::AbiDecoderConfig::new().validate(true),
815 )
816 }
817
818 #[inline]
819 fn abi_encoded_size(&self) -> usize {
820 match self {#(
821 Self::#variants(inner) =>
822 <#types as alloy_sol_types::#trait_>::abi_encoded_size(inner),
823 )*}
824 }
825
826 #[inline]
827 fn abi_encode_raw(&self, out: &mut alloy_sol_types::private::Vec<u8>) {
828 match self {#(
829 Self::#variants(inner) =>
830 <#types as alloy_sol_types::#trait_>::abi_encode_raw(inner, out),
831 )*}
832 }
833 }
834 }
835 }
836
837 fn expand_events(
838 &self,
839 events: &[&ItemEvent],
840 data: &ExpandData,
841 sorted_data: &ExpandData,
842 attrs: Vec<Attribute>,
843 ) -> TokenStream {
844 let def = self.generate_enum(data, sorted_data, attrs);
845 let ExpandData { name, trait_, .. } = data;
846 let name_s = name.to_string();
847 let count = data.variants.len();
848
849 let has_anon = events.iter().any(|e| e.is_anonymous());
850 let has_non_anon = events.iter().any(|e| !e.is_anonymous());
851 assert!(has_anon || has_non_anon, "events shouldn't be empty");
852
853 let e_name = |&e: &&ItemEvent| self.cx.overloaded_name(e.into());
854 let err = quote! {
855 alloy_sol_types::private::Err(alloy_sol_types::Error::InvalidLog {
856 name: <Self as alloy_sol_types::SolEventInterface>::NAME,
857 log: alloy_sol_types::private::Box::new(alloy_sol_types::private::LogData::new_unchecked(
858 topics.to_vec(),
859 data.to_vec().into(),
860 )),
861 })
862 };
863 let non_anon_impl = has_non_anon.then(|| {
864 let variants = events.iter().filter(|e| !e.is_anonymous()).map(e_name);
865 let ret = has_anon.then(|| quote!(return));
866 let ret_err = (!has_anon).then_some(&err);
867 quote! {
868 match topics.first().copied() {
869 #(
870 Some(<#variants as alloy_sol_types::#trait_>::SIGNATURE_HASH) =>
871 #ret <#variants as alloy_sol_types::#trait_>::decode_raw_log(topics, data)
872 .map(Self::#variants),
873 )*
874 _ => { #ret_err }
875 }
876 }
877 });
878 let anon_impl = has_anon.then(|| {
879 let variants = events.iter().filter(|e| e.is_anonymous()).map(e_name);
880 quote! {
881 #(
882 if let Ok(res) = <#variants as alloy_sol_types::#trait_>::decode_raw_log(topics, data) {
883 return Ok(Self::#variants(res));
884 }
885 )*
886 #err
887 }
888 });
889
890 let into_impl = {
891 let variants = events.iter().map(e_name);
892 let v2 = variants.clone();
893 quote! {
894 #[automatically_derived]
895 impl alloy_sol_types::private::IntoLogData for #name {
896 fn to_log_data(&self) -> alloy_sol_types::private::LogData {
897 match self {#(
898 Self::#variants(inner) =>
899 alloy_sol_types::private::IntoLogData::to_log_data(inner),
900 )*}
901 }
902
903 fn into_log_data(self) -> alloy_sol_types::private::LogData {
904 match self {#(
905 Self::#v2(inner) =>
906 alloy_sol_types::private::IntoLogData::into_log_data(inner),
907 )*}
908 }
909 }
910 }
911 };
912
913 quote! {
914 #def
915
916 #[automatically_derived]
917 impl alloy_sol_types::SolEventInterface for #name {
918 const NAME: &'static str = #name_s;
919 const COUNT: usize = #count;
920
921 fn decode_raw_log(topics: &[alloy_sol_types::Word], data: &[u8]) -> alloy_sol_types::Result<Self> {
922 #non_anon_impl
923 #anon_impl
924 }
925 }
926
927 #into_impl
928 }
929 }
930
931 fn generate_enum(
932 &self,
933 data: &ExpandData,
934 sorted_data: &ExpandData,
935 mut attrs: Vec<Attribute>,
936 ) -> TokenStream {
937 let ExpandData { name, variants, .. } = data;
938 let types = data.types();
939
940 let selectors = &sorted_data.selectors;
941 let sorted_variants = &sorted_data.variants;
942 let sorted_types = sorted_data.types();
943
944 let selector_len = selectors.first().unwrap().array.len();
945 assert!(selectors.iter().all(|s| s.array.len() == selector_len));
946 let selector_type = quote!([u8; #selector_len]);
947
948 self.cx.enum_derives(&mut attrs, data.can_derive_builtin);
949 let trait_ = &data.trait_;
950
951 let mut tokens = quote! {
952 #(#attrs)*
953 pub enum #name {
954 #(
955 #[allow(missing_docs)]
956 #variants(#types),
957 )*
958 }
959
960 impl #name {
961 pub const SELECTORS: &'static [#selector_type] = &[#(#selectors),*];
969
970 pub const VARIANT_NAMES: &'static [&'static str] = &[#(::core::stringify!(#sorted_variants)),*];
972
973 pub const SIGNATURES: &'static [&'static str] = &[#(<#sorted_types as alloy_sol_types::#trait_>::SIGNATURE),*];
975
976 #[inline]
978 pub fn signature_by_selector(selector: #selector_type) -> ::core::option::Option<&'static str> {
979 match Self::SELECTORS.binary_search(&selector) {
980 ::core::result::Result::Ok(idx) => ::core::option::Option::Some(Self::SIGNATURES[idx]),
981 ::core::result::Result::Err(_) => ::core::option::Option::None,
982 }
983 }
984
985 #[inline]
987 pub fn name_by_selector(selector: #selector_type) -> ::core::option::Option<&'static str> {
988 let sig = Self::signature_by_selector(selector)?;
989 sig.split_once('(').map(|(name, _)| name)
990 }
991 }
992 };
993
994 if self.extra_methods {
995 let conversions =
996 variants.iter().zip(types).map(|(v, t)| generate_variant_conversions(name, v, t));
997 let methods = variants.iter().zip(types).map(generate_variant_methods);
998 tokens.extend(conversions);
999 tokens.extend(quote! {
1000 impl #name {
1001 #(#methods)*
1002 }
1003 });
1004 }
1005
1006 tokens
1007 }
1008}
1009
1010fn generate_variant_conversions(name: &Ident, variant: &Ident, ty: &Ident) -> TokenStream {
1011 quote! {
1012 #[automatically_derived]
1013 impl ::core::convert::From<#ty> for #name {
1014 #[inline]
1015 fn from(value: #ty) -> Self {
1016 Self::#variant(value)
1017 }
1018 }
1019
1020 #[automatically_derived]
1021 impl ::core::convert::TryFrom<#name> for #ty {
1022 type Error = #name;
1023
1024 #[inline]
1025 fn try_from(value: #name) -> ::core::result::Result<Self, #name> {
1026 match value {
1027 #name::#variant(value) => ::core::result::Result::Ok(value),
1028 _ => ::core::result::Result::Err(value),
1029 }
1030 }
1031 }
1032 }
1033}
1034
1035fn generate_variant_methods((variant, ty): (&Ident, &Ident)) -> TokenStream {
1036 let name_snake = snakify(&variant.to_string());
1037
1038 let is_variant = format_ident!("is_{name_snake}");
1039 let is_variant_doc =
1040 format!("Returns `true` if `self` matches [`{variant}`](Self::{variant}).");
1041
1042 let as_variant = format_ident!("as_{name_snake}");
1043 let as_variant_doc = format!(
1044 "Returns an immutable reference to the inner [`{ty}`] if `self` matches [`{variant}`](Self::{variant})."
1045 );
1046
1047 let as_variant_mut = format_ident!("as_{name_snake}_mut");
1048 let as_variant_mut_doc = format!(
1049 "Returns a mutable reference to the inner [`{ty}`] if `self` matches [`{variant}`](Self::{variant})."
1050 );
1051
1052 quote! {
1053 #[doc = #is_variant_doc]
1054 #[inline]
1055 pub const fn #is_variant(&self) -> bool {
1056 ::core::matches!(self, Self::#variant(_))
1057 }
1058
1059 #[doc = #as_variant_doc]
1060 #[inline]
1061 pub const fn #as_variant(&self) -> ::core::option::Option<&#ty> {
1062 match self {
1063 Self::#variant(inner) => ::core::option::Option::Some(inner),
1064 _ => ::core::option::Option::None,
1065 }
1066 }
1067
1068 #[doc = #as_variant_mut_doc]
1069 #[inline]
1070 pub fn #as_variant_mut(&mut self) -> ::core::option::Option<&mut #ty> {
1071 match self {
1072 Self::#variant(inner) => ::core::option::Option::Some(inner),
1073 _ => ::core::option::Option::None,
1074 }
1075 }
1076 }
1077}
1078
1079fn call_builder_method(f: &ItemFunction, cx: &ExpCtxt<'_>) -> TokenStream {
1083 let name = call_builder_method_function_name(f, cx);
1084 let call_name = cx.call_name(f);
1085 let param_names1 = f.parameters.names().enumerate().map(anon_name);
1086 let param_tys = f.parameters.types().map(|ty| cx.expand_rust_type(ty));
1087 let doc = format!("Creates a new call builder for the [`{name}`] function.");
1088
1089 let call_struct = if f.parameters.is_empty() {
1090 quote! { #call_name }
1091 } else if f.parameters.len() == 1 && f.parameters[0].name.is_none() {
1092 quote! { #call_name(_0) }
1093 } else {
1094 let call_fields = param_names1.clone();
1095 quote! {
1096 #call_name { #(#call_fields),* }
1097 }
1098 };
1099 quote! {
1100 #[doc = #doc]
1101 pub fn #name(&self, #(#param_names1: #param_tys),*) -> alloy_contract::SolCallBuilder<&P, #call_name, N> {
1102 self.call_builder(&#call_struct)
1103 }
1104 }
1105}
1106
1107fn call_builder_method_function_name(f: &ItemFunction, cx: &ExpCtxt<'_>) -> SolIdent {
1111 let call_name_ident = cx.function_name(f);
1112 let name = call_name_ident.as_string();
1113 match name.as_str() {
1114 "new" | "deploy" | "deploy_builder" | "address" | "set_address" | "at" | "provider"
1115 | "call_builder" | "event_filter" => {
1116 SolIdent::new_spanned(&format!("{name}_call"), call_name_ident.span())
1117 }
1118 _ => call_name_ident,
1119 }
1120}
1121
1122fn generate_error_builders(
1124 contract_name: &SolIdent,
1125 errors: &[&ItemError],
1126 cx: &ExpCtxt<'_>,
1127) -> TokenStream {
1128 let enum_name = format_ident!("{contract_name}Errors");
1129 let methods = errors.iter().map(|error| {
1130 let variant_name = cx.overloaded_name((*error).into());
1131 let fn_name = snakify_ident(&variant_name);
1132 let sig = cx.error_signature(error);
1133 let doc = format!("Creates a [`{variant_name}`] error.\n\n```solidity\nerror {sig}\n```");
1134
1135 match error.parameters.len() {
1136 0 => {
1138 quote! {
1139 #[doc = #doc]
1140 #[inline]
1141 pub fn #fn_name() -> Self {
1142 Self::#variant_name(#variant_name)
1143 }
1144 }
1145 }
1146 1 if error.parameters[0].name.is_none() => {
1148 let ty = cx.expand_rust_type(&error.parameters[0].ty);
1149 let param_name = format_ident!("_0");
1150 quote! {
1151 #[doc = #doc]
1152 #[inline]
1153 pub fn #fn_name(#param_name: #ty) -> Self {
1154 Self::#variant_name(#variant_name(#param_name))
1155 }
1156 }
1157 }
1158 _ => {
1160 let params: Vec<_> = error
1161 .parameters
1162 .iter()
1163 .enumerate()
1164 .map(|(i, p)| {
1165 let sol_name = super::anon_name((i, p.name.as_ref()));
1166 let param_name = snakify_ident(&sol_name);
1167 (sol_name, param_name, cx.expand_rust_type(&p.ty))
1168 })
1169 .collect();
1170 builder_method(&fn_name, &doc, &variant_name, ¶ms)
1171 }
1172 }
1173 });
1174
1175 quote! {
1176 #[automatically_derived]
1177 impl #enum_name {
1178 #(#methods)*
1179 }
1180 }
1181}
1182
1183fn generate_event_builders(
1185 contract_name: &SolIdent,
1186 events: &[&ItemEvent],
1187 cx: &ExpCtxt<'_>,
1188) -> TokenStream {
1189 let enum_name = format_ident!("{contract_name}Events");
1190 let methods = events.iter().map(|event| {
1191 let variant_name = cx.overloaded_name((*event).into());
1192 let fn_name = snakify_ident(&variant_name);
1193 let sig = cx.event_signature(event);
1194 let doc = format!("Creates a [`{variant_name}`] event.\n\n```solidity\nevent {sig}\n```");
1195
1196 if event.parameters.is_empty() {
1197 quote! {
1198 #[doc = #doc]
1199 #[inline]
1200 pub fn #fn_name() -> Self {
1201 Self::#variant_name(#variant_name)
1202 }
1203 }
1204 } else {
1205 let params: Vec<_> = event
1206 .parameters
1207 .iter()
1208 .enumerate()
1209 .map(|(i, p)| {
1210 let sol_name = super::anon_name((i, p.name.as_ref()));
1211 let param_name = snakify_ident(&sol_name);
1212 (sol_name, param_name, cx.expand_event_param_type(p))
1213 })
1214 .collect();
1215 builder_method(&fn_name, &doc, &variant_name, ¶ms)
1216 }
1217 });
1218
1219 quote! {
1220 #[automatically_derived]
1221 impl #enum_name {
1222 #(#methods)*
1223 }
1224 }
1225}
1226
1227fn builder_method(
1229 fn_name: &Ident,
1230 doc: &str,
1231 variant_name: &SolIdent,
1232 params: &[(Ident, Ident, TokenStream)],
1233) -> TokenStream {
1234 let fn_params = params.iter().map(|(_, param_name, ty)| quote!(#param_name: #ty));
1235 let field_inits = params.iter().map(|(sol_name, param_name, _)| quote!(#sol_name: #param_name));
1236 quote! {
1237 #[doc = #doc]
1238 #[inline]
1239 pub fn #fn_name(#(#fn_params),*) -> Self {
1240 Self::#variant_name(#variant_name {
1241 #(#field_inits),*
1242 })
1243 }
1244 }
1245}
1246
1247fn snakify_ident(name: &impl ToString) -> Ident {
1250 let s = snakify(&name.to_string());
1251 syn::parse_str::<Ident>(&s).unwrap_or_else(|_| Ident::new_raw(&s, Span::call_site()))
1252}
1253
1254fn snakify(s: &str) -> String {
1256 let leading_n = s.chars().take_while(|c| *c == '_').count();
1257 let (leading, s) = s.split_at(leading_n);
1258 let mut output: Vec<char> = leading.chars().chain(s.to_snake_case().chars()).collect();
1259
1260 let mut num_starts = vec![];
1261 for (pos, c) in output.iter().enumerate() {
1262 if pos != 0
1263 && c.is_ascii_digit()
1264 && !output[pos - 1].is_ascii_digit()
1265 && !output[pos - 1].is_ascii_punctuation()
1266 {
1267 num_starts.push(pos);
1268 }
1269 }
1270 for i in num_starts.into_iter().rev() {
1273 output.insert(i, '_');
1274 }
1275 output.into_iter().collect()
1276}