ethers_contract_abigen/
contract.rs

1//! Contains types to generate Rust bindings for Solidity contracts.
2
3mod errors;
4mod events;
5mod methods;
6pub(crate) mod structs;
7mod types;
8
9use super::{util, Abigen};
10use crate::contract::{methods::MethodAlias, structs::InternalStructs};
11#[cfg(feature = "providers")]
12use ethers_core::macros::ethers_providers_crate;
13use ethers_core::{
14    abi::{Abi, AbiParser, ErrorExt, EventExt, JsonAbi},
15    macros::{ethers_contract_crate, ethers_core_crate},
16    types::Bytes,
17};
18use eyre::{eyre, Result};
19use proc_macro2::{Ident, Literal, TokenStream};
20use quote::{format_ident, quote};
21use serde::Deserialize;
22use std::collections::BTreeMap;
23use syn::Path;
24
25/// The result of `Context::expand`
26#[derive(Debug)]
27pub struct ExpandedContract {
28    /// The name of the contract module
29    pub module: Ident,
30    /// The contract module's imports
31    pub imports: TokenStream,
32    /// Contract, Middle related implementations
33    pub contract: TokenStream,
34    /// All event impls of the contract
35    pub events: TokenStream,
36    /// All error impls of the contract
37    pub errors: TokenStream,
38    /// All contract call struct related types
39    pub call_structs: TokenStream,
40    /// The contract's internal structs
41    pub abi_structs: TokenStream,
42}
43
44impl ExpandedContract {
45    /// Merges everything into a single module
46    pub fn into_tokens(self) -> TokenStream {
47        self.into_tokens_with_path(None)
48    }
49
50    /// Merges everything into a single module, with an `include_bytes!` to the given path
51    pub fn into_tokens_with_path(self, path: Option<&std::path::Path>) -> TokenStream {
52        let ExpandedContract {
53            module,
54            imports,
55            contract,
56            events,
57            call_structs,
58            abi_structs,
59            errors,
60        } = self;
61
62        let include_tokens = path.and_then(|path| path.to_str()).map(|s| {
63            quote! {
64                const _: () = { ::core::include_bytes!(#s); };
65            }
66        });
67
68        quote! {
69            pub use #module::*;
70
71            /// This module was auto-generated with ethers-rs Abigen.
72            /// More information at: <https://github.com/gakonst/ethers-rs>
73            #[allow(
74                clippy::enum_variant_names,
75                clippy::too_many_arguments,
76                clippy::upper_case_acronyms,
77                clippy::type_complexity,
78                dead_code,
79                non_camel_case_types,
80            )]
81            pub mod #module {
82                #imports
83                #include_tokens
84                #contract
85                #errors
86                #events
87                #call_structs
88                #abi_structs
89            }
90        }
91    }
92}
93
94/// Internal shared context for generating smart contract bindings.
95pub struct Context {
96    /// The parsed ABI.
97    abi: Abi,
98
99    /// The parser used for human readable format
100    abi_parser: AbiParser,
101
102    /// Contains all the solidity structs extracted from the JSON ABI.
103    internal_structs: InternalStructs,
104
105    /// Was the ABI in human readable format?
106    human_readable: bool,
107
108    /// The contract name as an identifier.
109    contract_ident: Ident,
110
111    /// The contract name as string
112    contract_name: String,
113
114    /// Manually specified method aliases.
115    method_aliases: BTreeMap<String, MethodAlias>,
116
117    /// Manually specified method aliases.
118    error_aliases: BTreeMap<String, Ident>,
119
120    /// Derives added to event structs and enums.
121    extra_derives: Vec<Path>,
122
123    /// Manually specified event aliases.
124    event_aliases: BTreeMap<String, Ident>,
125
126    /// Bytecode extracted from the abi string input, if present.
127    contract_bytecode: Option<Bytes>,
128
129    /// Deployed bytecode extracted from the abi string input, if present.
130    contract_deployed_bytecode: Option<Bytes>,
131}
132
133impl Context {
134    /// Generates the tokens.
135    pub fn expand(&self) -> Result<ExpandedContract> {
136        let name_mod = util::ident(&util::safe_module_name(&self.contract_name));
137
138        // 1. Declare Contract struct
139        let struct_decl = self.struct_declaration();
140
141        // 2. Declare events structs & impl FromTokens for each event
142        let events_decl = self.events_declaration()?;
143
144        // 3. impl block for the contract methods and their corresponding types
145        let (contract_methods, call_structs) = self.methods_and_call_structs()?;
146
147        // 4. Declare the structs parsed from the human readable abi
148        let abi_structs_decl = self.abi_structs()?;
149
150        // 5. declare all error types
151        let errors_decl = self.errors()?;
152
153        let contract = quote! {
154            #struct_decl
155        };
156
157        #[cfg(feature = "providers")]
158        let contract = {
159            let name = &self.contract_ident;
160            let abi_name = self.inline_abi_ident();
161
162            // 6. impl block for the event functions
163            let contract_events = self.event_methods()?;
164
165            // 7. The deploy method, only if the contract has a bytecode object
166            let deployment_methods = self.deployment_methods();
167
168            let ethers_core = ethers_core_crate();
169            let ethers_contract = ethers_contract_crate();
170            let ethers_providers = ethers_providers_crate();
171
172            quote! {
173                #contract
174
175                impl<M: #ethers_providers::Middleware> #name<M> {
176                    /// Creates a new contract instance with the specified `ethers` client at
177                    /// `address`. The contract derefs to a `ethers::Contract` object.
178                    pub fn new<T: Into<#ethers_core::types::Address>>(address: T, client: ::std::sync::Arc<M>) -> Self {
179                        Self(#ethers_contract::Contract::new(address.into(), #abi_name.clone(), client))
180                    }
181
182                    #deployment_methods
183
184                    #contract_methods
185
186                    #contract_events
187                }
188
189                impl<M: #ethers_providers::Middleware> From<#ethers_contract::Contract<M>> for #name<M> {
190                    fn from(contract: #ethers_contract::Contract<M>) -> Self {
191                        Self::new(contract.address(), contract.client())
192                    }
193                }
194            }
195        };
196
197        // Avoid having a warning
198        #[cfg(not(feature = "providers"))]
199        let _ = contract_methods;
200
201        Ok(ExpandedContract {
202            module: name_mod,
203            imports: quote!(),
204            contract,
205            events: events_decl,
206            errors: errors_decl,
207            call_structs,
208            abi_structs: abi_structs_decl,
209        })
210    }
211
212    /// Create a context from the code generation arguments.
213    pub fn from_abigen(args: Abigen) -> Result<Self> {
214        // get the actual ABI string
215        let abi_str = args.abi_source.get().map_err(|e| eyre!("failed to get ABI JSON: {e}"))?;
216
217        // holds the bytecode parsed from the abi_str, if present
218        let mut contract_bytecode = None;
219
220        // holds the deployed bytecode parsed from the abi_str, if present
221        let mut contract_deployed_bytecode = None;
222
223        let (abi, human_readable, abi_parser) = parse_abi(&abi_str).map_err(|e| {
224            eyre::eyre!("error parsing abi for contract '{}': {e}", args.contract_name)
225        })?;
226
227        // try to extract all the solidity structs from the normal JSON ABI
228        // we need to parse the json abi again because we need the internalType fields which are
229        // omitted by ethabi. If the ABI was defined as human readable we use the `internal_structs`
230        // from the Abi Parser
231        let internal_structs = if human_readable {
232            let mut internal_structs = InternalStructs::default();
233            // the types in the abi_parser are already valid rust types so simply clone them to make
234            // it consistent with the `RawAbi` variant
235            internal_structs
236                .rust_type_names
237                .extend(abi_parser.function_params.values().map(|ty| (ty.clone(), ty.clone())));
238            internal_structs.function_params = abi_parser.function_params.clone();
239            internal_structs.event_params = abi_parser.event_params.clone();
240            internal_structs.outputs = abi_parser.outputs.clone();
241
242            internal_structs
243        } else {
244            match serde_json::from_str::<JsonAbi>(&abi_str)? {
245                JsonAbi::Object(obj) => {
246                    contract_bytecode = obj.bytecode;
247                    contract_deployed_bytecode = obj.deployed_bytecode;
248                    InternalStructs::new(obj.abi)
249                }
250                JsonAbi::Array(abi) => InternalStructs::new(abi),
251            }
252        };
253
254        // NOTE: We only check for duplicate signatures here, since if there are
255        //   duplicate aliases, the compiler will produce a warning because a
256        //   method will be re-defined.
257        let mut method_aliases = BTreeMap::new();
258        for (signature, alias) in args.method_aliases.into_iter() {
259            let alias = MethodAlias {
260                function_name: util::safe_ident(&alias),
261                struct_name: util::safe_pascal_case_ident(&alias),
262            };
263
264            if method_aliases.insert(signature.clone(), alias).is_some() {
265                eyre::bail!("duplicate method signature {signature:?} in method aliases")
266            }
267        }
268
269        let mut event_aliases = BTreeMap::new();
270        for (signature, alias) in args.event_aliases.into_iter() {
271            let alias = syn::parse_str(&alias)?;
272            event_aliases.insert(signature, alias);
273        }
274
275        // also check for overloaded events not covered by aliases, in which case we simply
276        // numerate them
277        for events in abi.events.values() {
278            insert_alias_names(
279                &mut event_aliases,
280                events.iter().map(|e| (e.abi_signature(), e.name.as_str())),
281                events::event_struct_alias,
282            );
283        }
284
285        let mut error_aliases = BTreeMap::new();
286        for (signature, alias) in args.error_aliases.into_iter() {
287            let alias = syn::parse_str(&alias)?;
288            error_aliases.insert(signature, alias);
289        }
290
291        // also check for overloaded errors not covered by aliases, in which case we simply
292        // numerate them
293        for errors in abi.errors.values() {
294            insert_alias_names(
295                &mut error_aliases,
296                errors.iter().map(|e| (e.abi_signature(), e.name.as_str())),
297                errors::error_struct_alias,
298            );
299        }
300
301        Ok(Self {
302            abi,
303            human_readable,
304            abi_parser,
305            internal_structs,
306            contract_name: args.contract_name.to_string(),
307            contract_ident: args.contract_name,
308            contract_bytecode,
309            contract_deployed_bytecode,
310            method_aliases,
311            error_aliases: Default::default(),
312            event_aliases,
313            extra_derives: args.derives,
314        })
315    }
316
317    /// The name of the contract.
318    pub(crate) fn contract_name(&self) -> &str {
319        &self.contract_name
320    }
321
322    /// Name of the `Lazy` that stores the ABI.
323    pub(crate) fn inline_abi_ident(&self) -> Ident {
324        format_ident!("{}_ABI", self.contract_name.to_uppercase())
325    }
326
327    /// Name of the `Lazy` that stores the Bytecode.
328    pub(crate) fn inline_bytecode_ident(&self) -> Ident {
329        format_ident!("{}_BYTECODE", self.contract_name.to_uppercase())
330    }
331
332    /// Name of the `Lazy` that stores the Deployed Bytecode.
333    pub(crate) fn inline_deployed_bytecode_ident(&self) -> Ident {
334        format_ident!("{}_DEPLOYED_BYTECODE", self.contract_name.to_uppercase())
335    }
336
337    /// Returns a reference to the internal ABI struct mapping table.
338    pub fn internal_structs(&self) -> &InternalStructs {
339        &self.internal_structs
340    }
341
342    /// Returns a mutable reference to the internal ABI struct mapping table.
343    pub fn internal_structs_mut(&mut self) -> &mut InternalStructs {
344        &mut self.internal_structs
345    }
346
347    /// Expands `self.extra_derives` into a comma separated list to be inserted in a
348    /// `#[derive(...)]` attribute.
349    pub(crate) fn expand_extra_derives(&self) -> TokenStream {
350        let extra_derives = &self.extra_derives;
351        quote!(#( #extra_derives, )*)
352    }
353
354    /// Generates the token stream for the contract's ABI, bytecode and struct declarations.
355    pub(crate) fn struct_declaration(&self) -> TokenStream {
356        let ethers_core = ethers_core_crate();
357        let ethers_contract = ethers_contract_crate();
358
359        let abi = {
360            let doc_str = if self.human_readable {
361                "The parsed human-readable ABI of the contract."
362            } else {
363                "The parsed JSON ABI of the contract."
364            };
365            let abi_name = self.inline_abi_ident();
366            let abi = crate::verbatim::generate(&self.abi, &ethers_core);
367            quote! {
368                #[allow(deprecated)]
369                fn __abi() -> #ethers_core::abi::Abi {
370                    #abi
371                }
372
373                #[doc = #doc_str]
374                pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> =
375                    #ethers_contract::Lazy::new(__abi);
376            }
377        };
378
379        let bytecode = self.contract_bytecode.as_ref().map(|bytecode| {
380            let bytecode = Literal::byte_string(bytecode);
381            let bytecode_name = self.inline_bytecode_ident();
382            quote! {
383                #[rustfmt::skip]
384                const __BYTECODE: &[u8] = #bytecode;
385
386                /// The bytecode of the contract.
387                pub static #bytecode_name: #ethers_core::types::Bytes =
388                    #ethers_core::types::Bytes::from_static(__BYTECODE);
389            }
390        });
391
392        let deployed_bytecode = self.contract_deployed_bytecode.as_ref().map(|bytecode| {
393            let bytecode = Literal::byte_string(bytecode);
394            let bytecode_name = self.inline_deployed_bytecode_ident();
395            quote! {
396                #[rustfmt::skip]
397                const __DEPLOYED_BYTECODE: &[u8] = #bytecode;
398
399                /// The deployed bytecode of the contract.
400                pub static #bytecode_name: #ethers_core::types::Bytes =
401                    #ethers_core::types::Bytes::from_static(__DEPLOYED_BYTECODE);
402            }
403        });
404
405        let code = quote! {
406            // The `Lazy` ABI
407            #abi
408
409            // The static Bytecode, if present
410            #bytecode
411
412            // The static deployed Bytecode, if present
413            #deployed_bytecode
414        };
415
416        #[cfg(feature = "providers")]
417        let code = {
418            let name = &self.contract_ident;
419
420            quote! {
421                #code
422
423                // Struct declaration
424                pub struct #name<M>(#ethers_contract::Contract<M>);
425
426                // Manual implementation since `M` is stored in `Arc<M>` and does not need to be `Clone`
427                impl<M> ::core::clone::Clone for #name<M> {
428                    fn clone(&self) -> Self {
429                        Self(::core::clone::Clone::clone(&self.0))
430                    }
431                }
432
433                // Deref to the inner contract to have access to all its methods
434                impl<M> ::core::ops::Deref for #name<M> {
435                    type Target = #ethers_contract::Contract<M>;
436
437                    fn deref(&self) -> &Self::Target {
438                        &self.0
439                    }
440                }
441
442                impl<M> ::core::ops::DerefMut for #name<M> {
443                    fn deref_mut(&mut self) -> &mut Self::Target {
444                        &mut self.0
445                    }
446                }
447
448                // `<name>(<address>)`
449                impl<M> ::core::fmt::Debug for #name<M> {
450                    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
451                        f.debug_tuple(::core::stringify!(#name))
452                            .field(&self.address())
453                            .finish()
454                    }
455                }
456            }
457        };
458
459        code
460    }
461}
462
463/// Solidity supports overloading as long as the signature of an event, error, function is unique,
464/// which results in a mapping `(name -> Vec<Element>)`
465///
466///
467/// This will populate the alias map for the value in the mapping (`Vec<Element>`) via `abi
468/// signature -> name` using the given aliases and merge it with all names not yet aliased.
469///
470/// If the iterator yields more than one element, this will simply numerate them
471fn insert_alias_names<'a, I, F>(aliases: &mut BTreeMap<String, Ident>, elements: I, get_ident: F)
472where
473    I: IntoIterator<Item = (String, &'a str)>,
474    F: Fn(&str) -> Ident,
475{
476    let not_aliased =
477        elements.into_iter().filter(|(sig, _name)| !aliases.contains_key(sig)).collect::<Vec<_>>();
478    if not_aliased.len() > 1 {
479        let mut overloaded_aliases = Vec::new();
480        for (idx, (sig, name)) in not_aliased.into_iter().enumerate() {
481            let unique_name = format!("{name}{}", idx + 1);
482            overloaded_aliases.push((sig, get_ident(&unique_name)));
483        }
484        aliases.extend(overloaded_aliases);
485    }
486}
487
488/// Parse the abi via `Source::parse` and return if the abi defined as human readable
489fn parse_abi(abi_str: &str) -> Result<(Abi, bool, AbiParser)> {
490    let mut abi_parser = AbiParser::default();
491    match abi_parser.parse_str(abi_str) {
492        Ok(abi) => Ok((abi, true, abi_parser)),
493        Err(e) => match serde_json::from_str::<JsonContract>(abi_str) {
494            Ok(contract) => Ok((contract.into_abi(), false, abi_parser)),
495            Err(e2) => Err(eyre::eyre!(
496                "couldn't parse ABI string as either human readable (1) or JSON (2):\n1. {e}\n2. {e2}"
497            )),
498        },
499    }
500}
501
502#[derive(Deserialize)]
503struct ContractObject {
504    abi: Abi,
505}
506
507#[derive(Deserialize)]
508#[serde(untagged)]
509enum JsonContract {
510    /// json object input as `{"abi": [..], "bin": "..."}`
511    Object(ContractObject),
512    /// json array input as `[]`
513    Array(Abi),
514}
515
516impl JsonContract {
517    fn into_abi(self) -> Abi {
518        match self {
519            JsonContract::Object(o) => o.abi,
520            JsonContract::Array(abi) => abi,
521        }
522    }
523}