ethers_contract_derive/lib.rs
1//! Procedural macros for generating type-safe bindings to an Ethereum smart contract.
2
3#![deny(missing_docs, unsafe_code, unused_crate_dependencies)]
4#![deny(rustdoc::broken_intra_doc_links)]
5#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
6
7use abigen::Contracts;
8use proc_macro::TokenStream;
9use syn::{parse_macro_input, DeriveInput};
10
11pub(crate) mod abi_ty;
12mod abigen;
13mod call;
14pub(crate) mod calllike;
15mod codec;
16mod display;
17mod eip712;
18mod error;
19mod event;
20mod spanned;
21pub(crate) mod utils;
22
23/// Generates type-safe bindings to an Ethereum smart contract from its ABI.
24///
25/// All the accepted ABI sources are listed in the examples below and in [Source].
26///
27/// Note:
28/// - relative paths are rooted in the crate's root (`CARGO_MANIFEST_DIR`).
29/// - Environment variable interpolation is supported via `$` prefix, like
30/// `"$CARGO_MANIFEST_DIR/contracts/c.json"`
31/// - Etherscan rate-limits requests to their API. To avoid this, set the `ETHERSCAN_API_KEY`
32/// environment variable.
33///
34/// Additionally, this macro accepts additional parameters to configure some aspects of the code
35/// generation:
36/// - `methods`: A list of mappings from method signatures to method names allowing methods names to
37/// be explicitely set for contract methods. This also provides a workaround for generating code
38/// for contracts with multiple methods with the same name.
39/// - `derives`: A list of additional derive macros that are added to all the generated structs and
40/// enums, after the default ones which are ([when applicable][tuple_derive_ref]):
41/// * [PartialEq]
42/// * [Eq]
43/// * [Debug]
44/// * [Default]
45/// * [Hash]
46///
47/// [Source]: ethers_contract_abigen::Source
48/// [tuple_derive_ref]: https://doc.rust-lang.org/stable/std/primitive.tuple.html#trait-implementations-1
49///
50/// # Examples
51///
52/// All the possible ABI sources:
53///
54/// ```ignore
55/// # use ethers_contract_derive::abigen;
56/// // ABI Path
57/// abigen!(MyContract, "./MyContractABI.json");
58///
59/// // HTTP(S) source
60/// abigen!(MyContract, "https://my.domain.local/path/to/contract.json");
61///
62/// // Etherscan.io
63/// abigen!(MyContract, "etherscan:0x0001020304050607080910111213141516171819");
64/// abigen!(MyContract, "https://etherscan.io/address/0x0001020304050607080910111213141516171819");
65///
66/// // npmjs
67/// abigen!(MyContract, "npm:@org/package@1.0.0/path/to/contract.json");
68///
69/// // Human readable ABI
70/// abigen!(MyContract, r"[
71/// function setValue(string)
72/// function getValue() external view returns (string)
73/// event ValueChanged(address indexed author, string oldValue, string newValue)
74/// ]");
75/// ```
76///
77/// Specify additional parameters:
78///
79/// ```ignore
80/// # use ethers_contract_derive::abigen;
81/// abigen!(
82/// MyContract,
83/// "path/to/MyContract.json",
84/// methods {
85/// myMethod(uint256,bool) as my_renamed_method;
86/// },
87/// derives(serde::Deserialize, serde::Serialize),
88/// );
89/// ```
90///
91/// Aliases for overloaded functions with no aliases provided in the `method` section are derived
92/// automatically.
93///
94/// `abigen!` supports multiple abigen definitions separated by a semicolon `;`
95/// This is useful if the contracts use ABIEncoderV2 structs. In which case
96/// `abigen!` bundles all type duplicates so that all rust contracts also use
97/// the same rust types.
98///
99/// ```ignore
100/// abigen!(
101/// MyContract,
102/// "path/to/MyContract.json",
103/// methods {
104/// myMethod(uint256,bool) as my_renamed_method;
105/// },
106/// derives(serde::Deserialize, serde::Serialize);
107///
108/// MyOtherContract,
109/// "path/to/MyOtherContract.json",
110/// derives(serde::Deserialize, serde::Serialize);
111/// );
112/// ```
113#[proc_macro]
114pub fn abigen(input: TokenStream) -> TokenStream {
115 let contracts = parse_macro_input!(input as Contracts);
116 match contracts.expand() {
117 Ok(tokens) => tokens,
118 Err(err) => err.to_compile_error(),
119 }
120 .into()
121}
122
123/// Derives the [`AbiType`] and all `Tokenizable` traits for the labeled type.
124///
125/// This derive macro adds a type bound `field: Tokenizable` for each field type.
126///
127/// [`AbiType`]: ethers_core::abi::AbiType
128///
129/// # Examples
130///
131/// ```
132/// use ethers_contract_derive::EthAbiType;
133/// use ethers_core::types::*;
134/// use ethers_core::abi::{AbiType, ParamType};
135///
136/// #[derive(Clone, EthAbiType)]
137/// struct MyStruct {
138/// a: U256,
139/// b: Address,
140/// c: Bytes,
141/// d: String,
142/// e: H256,
143/// }
144///
145/// assert_eq!(
146/// MyStruct::param_type(),
147/// ParamType::Tuple(vec![
148/// ParamType::Uint(256),
149/// ParamType::Address,
150/// ParamType::Bytes,
151/// ParamType::String,
152/// ParamType::FixedBytes(32),
153/// ]),
154/// );
155/// ```
156#[proc_macro_derive(EthAbiType)]
157pub fn derive_abi_type(input: TokenStream) -> TokenStream {
158 let input = parse_macro_input!(input as DeriveInput);
159 match abi_ty::derive_tokenizeable_impl(&input) {
160 Ok(tokens) => tokens,
161 Err(err) => err.to_compile_error(),
162 }
163 .into()
164}
165
166/// Derives the [`AbiEncode`] and [`AbiDecode`] traits for the labeled type.
167///
168/// This is separate from other derive macros because this derives a generic codec implementation
169/// for structs, while [`EthEvent`] and others derive a specialized implementation.
170///
171/// [`AbiEncode`]: ethers_core::abi::AbiEncode
172/// [`AbiDecode`]: ethers_core::abi::AbiDecode
173///
174/// Note that this macro requires the `EthAbiType` macro to be derived or for the type to implement
175/// `AbiType` and `Tokenizable`. The type returned by the `AbiType` implementation must be a
176/// `Token::Tuple`, otherwise this macro's implementation of `AbiDecode` will panic at runtime.
177///
178/// # Examples
179///
180/// ```
181/// use ethers_contract_derive::{EthAbiCodec, EthAbiType};
182/// use ethers_core::types::Address;
183/// use ethers_core::abi::{AbiDecode, AbiEncode};
184///
185/// #[derive(Clone, Debug, Default, PartialEq, EthAbiType, EthAbiCodec)]
186/// struct MyStruct {
187/// addr: Address,
188/// old_value: String,
189/// new_value: String,
190/// }
191///
192/// let value = MyStruct::default();
193/// let encoded = value.clone().encode();
194/// let decoded = MyStruct::decode(&encoded).unwrap();
195/// assert_eq!(decoded, value);
196/// ```
197#[proc_macro_derive(EthAbiCodec)]
198pub fn derive_abi_codec(input: TokenStream) -> TokenStream {
199 let input = parse_macro_input!(input as DeriveInput);
200 codec::derive_codec_impl(&input).into()
201}
202
203/// Derives the [`Display`] trait on structs by formatting each field based on its Ethereum type.
204///
205/// The final output is a comma separated list of the struct's fields, formatted as follows:
206/// `self.0, self.1, self.2,...`
207///
208/// [`Display`]: std::fmt::Display
209///
210/// # Examples
211///
212/// ```
213/// use ethers_contract_derive::{EthAbiType, EthDisplay};
214/// use ethers_core::types::*;
215///
216/// #[derive(Clone, Default, EthAbiType, EthDisplay)]
217/// struct MyStruct {
218/// addr: Address,
219/// old_value: String,
220/// new_value: String,
221/// h: H256,
222/// arr_u8: [u8; 32],
223/// arr_u16: [u16; 32],
224/// v: Vec<u8>,
225/// }
226///
227/// let s = MyStruct::default();
228/// assert!(!format!("{s}").is_empty());
229/// ```
230#[proc_macro_derive(EthDisplay, attributes(ethdisplay))]
231pub fn derive_eth_display(input: TokenStream) -> TokenStream {
232 let input = parse_macro_input!(input as DeriveInput);
233 match display::derive_eth_display_impl(input) {
234 Ok(tokens) => tokens,
235 Err(err) => err.to_compile_error(),
236 }
237 .into()
238}
239
240/// Derives the [`EthEvent`] and `Tokenizable` traits for the labeled type.
241///
242/// Additional arguments can be specified using the `#[ethevent(...)]` attribute:
243///
244/// For the struct:
245///
246/// - `name = "..."`: Overrides the generated event name. Defaults to the struct's name;
247/// - `signature = "..."`: The signature as hex string to override the event's signature;
248/// - `abi = "..."`: The ABI signature of the event. The `abi` should be a Solidity event definition
249/// or a tuple of the event's types in case the event has non elementary (other `EthAbiType`)
250/// types as members;
251/// - `anonymous`: A flag to mark this as an anonymous event.
252///
253/// For fields:
254///
255/// - `indexed`: flag to mark a field as an indexed event input;
256/// - `name = "..."`: override the name of an indexed event input, default is the rust field name.
257///
258/// [`EthEvent`]: https://docs.rs/ethers/latest/ethers/contract/trait.EthEvent.html
259///
260/// # Examples
261///
262/// ```ignore
263/// use ethers_contract_derive::{EthAbiType, EthEvent};
264/// use ethers_core::types::Address;
265///
266/// #[derive(EthAbiType)]
267/// struct Inner {
268/// inner: Address,
269/// msg: String,
270/// }
271///
272/// #[derive(EthEvent)]
273/// #[ethevent(abi = "ValueChangedEvent(address,string,(address,string))")]
274/// struct ValueChangedEvent {
275/// #[ethevent(indexed, name = "_target")]
276/// target: Address,
277/// msg: String,
278/// inner: Inner,
279/// }
280/// ```
281#[proc_macro_derive(EthEvent, attributes(ethevent))]
282pub fn derive_abi_event(input: TokenStream) -> TokenStream {
283 let input = parse_macro_input!(input as DeriveInput);
284 match event::derive_eth_event_impl(input) {
285 Ok(tokens) => tokens,
286 Err(err) => err.to_compile_error(),
287 }
288 .into()
289}
290
291/// Derives the [`EthCall`] and `Tokenizeable` trait for the labeled type.
292///
293/// Additional arguments can be specified using the `#[ethcall(...)]` attribute:
294///
295/// For the struct:
296///
297/// - `name = "..."`: Overrides the generated function name. Defaults to the struct's name;
298/// - `abi = "..."`: The ABI signature of the function.
299///
300/// NOTE: in order to successfully parse the `abi` (`<name>(<args>,...)`), `<name>` must match
301/// either the struct's name or the name attribute: `#[ethcall(name = "<name>"]`
302///
303/// [`EthCall`]: https://docs.rs/ethers/latest/ethers/contract/trait.EthCall.html
304///
305/// # Examples
306///
307/// ```ignore
308/// use ethers_contract_derive::EthCall;
309/// use ethers_core::abi::{Address, FunctionExt};
310///
311/// #[derive(EthCall)]
312/// #[ethcall(name = "my_call")]
313/// struct MyCall {
314/// addr: Address,
315/// old_value: String,
316/// new_value: String,
317/// }
318///
319/// assert_eq!(
320/// MyCall::abi_signature(),
321/// "my_call(address,string,string)"
322/// );
323/// ```
324///
325/// Call with struct inputs
326///
327/// ```ignore
328/// use ethers_core::abi::{Address, EventExt};
329/// use ethers_contract_derive::EthCall;
330///
331/// #[derive(Clone, PartialEq, EthAbiType)]
332/// struct SomeType {
333/// inner: Address,
334/// msg: String,
335/// }
336///
337/// #[derive(PartialEq, EthCall)]
338/// #[ethcall(name = "foo", abi = "foo(address,(address,string),string)")]
339/// struct FooCall {
340/// old_author: Address,
341/// inner: SomeType,
342/// new_value: String,
343/// }
344///
345/// assert_eq!(
346/// FooCall::abi_signature(),
347/// "foo(address,(address,string),string)"
348/// );
349/// ```
350#[proc_macro_derive(EthCall, attributes(ethcall))]
351pub fn derive_abi_call(input: TokenStream) -> TokenStream {
352 let input = parse_macro_input!(input as DeriveInput);
353 match call::derive_eth_call_impl(input) {
354 Ok(tokens) => tokens,
355 Err(err) => err.to_compile_error(),
356 }
357 .into()
358}
359
360/// Derives the [`EthError`] and `Tokenizeable` trait for the labeled type.
361///
362/// Additional arguments can be specified using the `#[etherror(...)]` attribute:
363///
364/// For the struct:
365///
366/// - `name = "..."`: Overrides the generated error name. Defaults to the struct's name;
367/// - `abi = "..."`: The ABI signature of the error.
368///
369/// NOTE: in order to successfully parse the `abi` (`<name>(<args>,...)`), `<name>` must match
370/// either the struct's name or the name attribute: `#[ethcall(name = "<name>"]`
371///
372/// [`EthError`]: https://docs.rs/ethers/latest/ethers/contract/trait.EthError.html
373///
374/// # Examples
375///
376/// ```ignore
377/// use ethers_core::abi::{Address, ErrorExt};
378/// use ethers_contract_derive::EthError;
379///
380/// #[derive(Clone, EthError)]
381/// #[etherror(name = "my_error")]
382/// struct MyError {
383/// addr: Address,
384/// old_value: String,
385/// new_value: String,
386/// }
387///
388/// assert_eq!(
389/// MyError::abi_signature(),
390/// "my_error(address,string,string)"
391/// );
392/// ```
393#[proc_macro_derive(EthError, attributes(etherror))]
394pub fn derive_abi_error(input: TokenStream) -> TokenStream {
395 let input = parse_macro_input!(input as DeriveInput);
396 match error::derive_eth_error_impl(input) {
397 Ok(tokens) => tokens,
398 Err(err) => err.to_compile_error(),
399 }
400 .into()
401}
402
403/// Derives the [`Eip712`] trait for the labeled type.
404///
405/// Encodes a Rust struct into a payload hash, according to [eip-712](https://eips.ethereum.org/EIPS/eip-712).
406///
407/// The following traits are required to be implemented for the struct:
408/// - [`Clone`]
409/// - [`Tokenizable`]: can be derived with [`EthAbiType`]
410///
411/// [`Tokenizable`]: ethers_core::abi::Tokenizable
412///
413/// # Attribute parameters
414///
415/// Required:
416///
417/// - `name = "..."`: The name of the EIP712 domain separator.
418/// - `version = "..."`: The version of the EIP712 domain separator.
419/// - `chain_id = ...`: The chain id of the EIP712 domain separator.
420/// - `verifying_contract = "..."`: The verifying contract's address of the EIP712 domain separator.
421///
422/// Optional:
423///
424/// - `salt = "..."` or `raw_salt = "..."`: The salt of the EIP712 domain separator;
425/// - `salt` is interpreted as UTF-8 bytes and hashed, while `raw_salt` is interpreted as a hex
426/// string.
427///
428/// # Examples
429///
430/// ```
431/// use ethers_contract_derive::{EthAbiType, Eip712};
432/// use ethers_core::types::{transaction::eip712::Eip712, H160};
433///
434/// #[derive(Clone, Default, EthAbiType, Eip712)]
435/// #[eip712(
436/// name = "Radicle",
437/// version = "1",
438/// chain_id = 1,
439/// verifying_contract = "0x0000000000000000000000000000000000000000",
440/// // salt/raw_salt are optional parameters
441/// salt = "my-unique-spice",
442/// )]
443/// pub struct Puzzle {
444/// pub organization: H160,
445/// pub contributor: H160,
446/// pub commit: String,
447/// pub project: String,
448/// }
449///
450/// let puzzle = Puzzle::default();
451/// let hash = puzzle.encode_eip712().unwrap();
452/// ```
453///
454/// # Limitations
455///
456/// At the moment, the derive macro does not recursively encode nested Eip712 structs.
457///
458/// There is an Inner helper attribute `#[eip712]` for fields that will eventually be used to
459/// determine if there is a nested eip712 struct. However, this work is not yet complete.
460#[proc_macro_derive(Eip712, attributes(eip712))]
461pub fn derive_eip712(input: TokenStream) -> TokenStream {
462 let input = parse_macro_input!(input as DeriveInput);
463 match eip712::impl_derive_eip712(&input) {
464 Ok(tokens) => tokens,
465 Err(e) => e.to_compile_error(),
466 }
467 .into()
468}