2020-05-26 18:57:59 +00:00
|
|
|
//! Implementation of procedural macro for generating type-safe bindings to an
|
|
|
|
//! ethereum smart contract.
|
2021-10-16 13:42:17 +00:00
|
|
|
#![deny(missing_docs, unsafe_code)]
|
2020-05-26 18:57:59 +00:00
|
|
|
|
2021-03-15 11:59:52 +00:00
|
|
|
use proc_macro::TokenStream;
|
2021-10-16 08:19:42 +00:00
|
|
|
use syn::{parse_macro_input, DeriveInput};
|
2020-05-26 18:57:59 +00:00
|
|
|
|
2021-10-11 14:18:09 +00:00
|
|
|
use abigen::Contracts;
|
2020-05-26 18:57:59 +00:00
|
|
|
|
2021-10-16 08:19:42 +00:00
|
|
|
pub(crate) mod abi_ty;
|
2021-03-15 11:59:52 +00:00
|
|
|
mod abigen;
|
2021-10-18 10:28:38 +00:00
|
|
|
mod call;
|
2021-10-16 13:42:17 +00:00
|
|
|
mod display;
|
2021-10-16 08:19:42 +00:00
|
|
|
mod event;
|
2021-03-15 11:59:52 +00:00
|
|
|
mod spanned;
|
2021-10-16 08:19:42 +00:00
|
|
|
pub(crate) mod utils;
|
2020-05-26 18:57:59 +00:00
|
|
|
|
2021-10-16 08:19:42 +00:00
|
|
|
/// Proc macro to generate type-safe bindings to a contract(s). This macro
|
2021-11-29 13:37:11 +00:00
|
|
|
/// accepts one or more Ethereum contract ABI or a path. Note that relative paths are
|
2021-10-16 08:19:42 +00:00
|
|
|
/// rooted in the crate's root `CARGO_MANIFEST_DIR`.
|
2021-11-29 13:37:11 +00:00
|
|
|
/// Environment variable interpolation is supported via `$` prefix, like
|
|
|
|
/// `"$CARGO_MANIFEST_DIR/contracts/c.json"`
|
2020-05-26 18:57:59 +00:00
|
|
|
///
|
2020-06-10 20:26:37 +00:00
|
|
|
/// # Examples
|
2020-05-26 18:57:59 +00:00
|
|
|
///
|
2020-06-11 09:33:09 +00:00
|
|
|
/// ```ignore
|
|
|
|
/// # use ethers_contract_derive::abigen;
|
2020-06-10 20:26:37 +00:00
|
|
|
/// // ABI Path
|
|
|
|
/// abigen!(MyContract, "MyContract.json");
|
2020-05-26 18:57:59 +00:00
|
|
|
///
|
|
|
|
/// // HTTP(S) source
|
2020-06-11 09:33:09 +00:00
|
|
|
/// abigen!(MyContract, "https://my.domain.local/path/to/contract.json");
|
2020-06-10 20:26:37 +00:00
|
|
|
///
|
2020-05-26 18:57:59 +00:00
|
|
|
/// // Etherscan.io
|
2020-06-03 21:05:05 +00:00
|
|
|
/// abigen!(MyContract, "etherscan:0x0001020304050607080910111213141516171819");
|
|
|
|
/// abigen!(MyContract, "https://etherscan.io/address/0x0001020304050607080910111213141516171819");
|
2020-06-10 20:26:37 +00:00
|
|
|
///
|
2020-05-26 18:57:59 +00:00
|
|
|
/// // npmjs
|
2020-06-11 09:33:09 +00:00
|
|
|
/// abigen!(MyContract, "npm:@org/package@1.0.0/path/to/contract.json");
|
2020-05-26 18:57:59 +00:00
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// Note that Etherscan rate-limits requests to their API, to avoid this an
|
|
|
|
/// `ETHERSCAN_API_KEY` environment variable can be set. If it is, it will use
|
|
|
|
/// that API key when retrieving the contract ABI.
|
|
|
|
///
|
|
|
|
/// Currently the proc macro accepts additional parameters to configure some
|
|
|
|
/// aspects of the code generation. Specifically it accepts:
|
2021-10-29 12:29:35 +00:00
|
|
|
/// - `methods`: A list of mappings from method signatures to method names allowing methods names to
|
|
|
|
/// be explicitely set for contract methods. This also provides a workaround for generating code
|
|
|
|
/// for contracts with multiple methods with the same name.
|
|
|
|
/// - `event_derives`: A list of additional derives that should be added to contract event structs
|
|
|
|
/// and enums.
|
2020-05-26 18:57:59 +00:00
|
|
|
///
|
2021-10-11 14:18:09 +00:00
|
|
|
/// # Example
|
|
|
|
///
|
2020-06-11 09:33:09 +00:00
|
|
|
/// ```ignore
|
2020-06-03 21:05:05 +00:00
|
|
|
/// abigen!(
|
2020-06-10 20:26:37 +00:00
|
|
|
/// MyContract,
|
|
|
|
/// "path/to/MyContract.json",
|
2020-05-26 18:57:59 +00:00
|
|
|
/// methods {
|
|
|
|
/// myMethod(uint256,bool) as my_renamed_method;
|
|
|
|
/// },
|
|
|
|
/// event_derives (serde::Deserialize, serde::Serialize),
|
|
|
|
/// );
|
|
|
|
/// ```
|
2021-10-11 14:18:09 +00:00
|
|
|
///
|
2021-10-31 11:24:02 +00:00
|
|
|
/// Aliases for overloaded functions with no aliases provided in the `method` section are derived
|
|
|
|
/// automatically.
|
|
|
|
///
|
2021-10-11 14:18:09 +00:00
|
|
|
/// `abigen!` supports multiple abigen definitions separated by a semicolon `;`
|
2021-10-16 08:19:42 +00:00
|
|
|
/// This is useful if the contracts use ABIEncoderV2 structs. In which case
|
|
|
|
/// `abigen!` bundles all type duplicates so that all rust contracts also use
|
|
|
|
/// the same rust types.
|
2021-10-11 14:18:09 +00:00
|
|
|
///
|
|
|
|
/// # Example Multiple contracts
|
|
|
|
/// ```ignore
|
|
|
|
/// abigen!(
|
|
|
|
/// MyContract,
|
|
|
|
/// "path/to/MyContract.json",
|
|
|
|
/// methods {
|
|
|
|
/// myMethod(uint256,bool) as my_renamed_method;
|
|
|
|
/// },
|
|
|
|
/// event_derives (serde::Deserialize, serde::Serialize);
|
|
|
|
///
|
|
|
|
/// MyOtherContract,
|
|
|
|
/// "path/to/MyOtherContract.json",
|
|
|
|
/// event_derives (serde::Deserialize, serde::Serialize);
|
|
|
|
/// );
|
|
|
|
/// ```
|
2020-05-26 18:57:59 +00:00
|
|
|
#[proc_macro]
|
|
|
|
pub fn abigen(input: TokenStream) -> TokenStream {
|
2021-10-11 14:18:09 +00:00
|
|
|
let contracts = parse_macro_input!(input as Contracts);
|
2020-05-26 18:57:59 +00:00
|
|
|
|
2021-10-29 12:29:35 +00:00
|
|
|
contracts.expand().unwrap_or_else(|err| err.to_compile_error()).into()
|
2020-05-26 18:57:59 +00:00
|
|
|
}
|
2021-03-15 11:59:52 +00:00
|
|
|
|
2021-10-16 08:19:42 +00:00
|
|
|
/// Derives the `Tokenizable` trait for the labeled type.
|
|
|
|
///
|
|
|
|
/// This derive macro automatically adds a type bound `field: Tokenizable` for
|
|
|
|
/// each field type.
|
|
|
|
#[proc_macro_derive(EthAbiType)]
|
|
|
|
pub fn derive_abi_type(input: TokenStream) -> TokenStream {
|
|
|
|
let input = parse_macro_input!(input as DeriveInput);
|
|
|
|
TokenStream::from(abi_ty::derive_tokenizeable_impl(&input))
|
|
|
|
}
|
|
|
|
|
2021-10-16 13:42:17 +00:00
|
|
|
/// Derives `fmt::Display` trait and generates a convenient format for all the
|
|
|
|
/// underlying primitive types/tokens.
|
|
|
|
///
|
|
|
|
/// The fields of the structure are formatted comma separated, like `self.0,
|
|
|
|
/// self.1, self.2,...`
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```ignore
|
2021-10-18 10:28:38 +00:00
|
|
|
/// use ethers_contract::EthDisplay;
|
|
|
|
/// use ethers_core::types::*;
|
|
|
|
///
|
2021-10-16 13:42:17 +00:00
|
|
|
/// #[derive(Debug, Clone, EthAbiType, EthDisplay)]
|
|
|
|
/// struct MyStruct {
|
|
|
|
/// addr: Address,
|
|
|
|
/// old_value: String,
|
|
|
|
/// new_value: String,
|
|
|
|
/// h: H256,
|
|
|
|
/// arr_u8: [u8; 32],
|
|
|
|
/// arr_u16: [u16; 32],
|
|
|
|
/// v: Vec<u8>,
|
|
|
|
/// }
|
|
|
|
/// let val = MyStruct {..};
|
|
|
|
/// format!("{}", val);
|
|
|
|
/// ```
|
|
|
|
#[proc_macro_derive(EthDisplay, attributes(ethdisplay))]
|
|
|
|
pub fn derive_eth_display(input: TokenStream) -> TokenStream {
|
|
|
|
let input = parse_macro_input!(input as DeriveInput);
|
|
|
|
match display::derive_eth_display_impl(input) {
|
|
|
|
Ok(tokens) => TokenStream::from(tokens),
|
|
|
|
Err(err) => err.to_compile_error().into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-15 11:59:52 +00:00
|
|
|
/// Derives the `EthEvent` and `Tokenizeable` trait for the labeled type.
|
|
|
|
///
|
2021-10-16 08:19:42 +00:00
|
|
|
/// Additional arguments can be specified using the `#[ethevent(...)]`
|
|
|
|
/// attribute:
|
2021-03-15 11:59:52 +00:00
|
|
|
///
|
2021-03-19 15:44:59 +00:00
|
|
|
/// For the struct:
|
|
|
|
///
|
2021-10-29 12:29:35 +00:00
|
|
|
/// - `name`, `name = "..."`: Overrides the generated `EthEvent` name, default is the
|
2021-03-16 08:12:32 +00:00
|
|
|
/// struct's name.
|
2021-10-29 12:29:35 +00:00
|
|
|
/// - `signature`, `signature = "..."`: The signature as hex string to override the
|
2021-03-16 08:12:32 +00:00
|
|
|
/// event's signature.
|
2021-10-29 12:29:35 +00:00
|
|
|
/// - `abi`, `abi = "..."`: The ABI signature for the event this event's data corresponds to.
|
2021-10-16 08:19:42 +00:00
|
|
|
/// The `abi` should be solidity event definition or a tuple of the event's
|
|
|
|
/// types in case the event has non elementary (other `EthAbiType`) types as
|
|
|
|
/// members
|
2021-03-19 15:44:59 +00:00
|
|
|
/// - `anonymous`: A flag to mark this as an anonymous event
|
|
|
|
///
|
|
|
|
/// For fields:
|
|
|
|
///
|
|
|
|
/// - `indexed`: flag to mark a field as an indexed event input
|
2021-10-29 12:29:35 +00:00
|
|
|
/// - `name`: override the name of an indexed event input, default is the rust field name
|
2021-03-16 08:12:32 +00:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
/// ```ignore
|
2021-10-18 10:28:38 +00:00
|
|
|
/// use ethers_contract::EthCall;
|
|
|
|
/// use ethers_core::types::Address;
|
2021-03-19 15:44:59 +00:00
|
|
|
///
|
2021-03-16 08:12:32 +00:00
|
|
|
/// #[derive(Debug, EthAbiType)]
|
|
|
|
/// struct Inner {
|
|
|
|
/// inner: Address,
|
|
|
|
/// msg: String,
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// #[derive(Debug, EthEvent)]
|
|
|
|
/// #[ethevent(abi = "ValueChangedEvent((address,string),string)")]
|
|
|
|
/// struct ValueChangedEvent {
|
2021-03-19 15:44:59 +00:00
|
|
|
/// #[ethevent(indexed, name = "_target")]
|
|
|
|
/// target: Address,
|
2021-03-16 08:12:32 +00:00
|
|
|
/// msg: String,
|
2021-03-19 15:44:59 +00:00
|
|
|
/// inner: Inner,
|
2021-03-16 08:12:32 +00:00
|
|
|
/// }
|
|
|
|
/// ```
|
2021-03-15 11:59:52 +00:00
|
|
|
#[proc_macro_derive(EthEvent, attributes(ethevent))]
|
|
|
|
pub fn derive_abi_event(input: TokenStream) -> TokenStream {
|
|
|
|
let input = parse_macro_input!(input as DeriveInput);
|
2021-10-16 08:19:42 +00:00
|
|
|
TokenStream::from(event::derive_eth_event_impl(input))
|
2021-03-15 11:59:52 +00:00
|
|
|
}
|
2021-10-18 10:28:38 +00:00
|
|
|
|
|
|
|
/// Derives the `EthCall` and `Tokenizeable` trait for the labeled type.
|
|
|
|
///
|
|
|
|
/// Additional arguments can be specified using the `#[ethcall(...)]`
|
|
|
|
/// attribute:
|
|
|
|
///
|
|
|
|
/// For the struct:
|
|
|
|
///
|
2021-10-29 12:29:35 +00:00
|
|
|
/// - `name`, `name = "..."`: Overrides the generated `EthCall` function name, default is the
|
2021-10-18 10:28:38 +00:00
|
|
|
/// struct's name.
|
2021-10-29 12:29:35 +00:00
|
|
|
/// - `abi`, `abi = "..."`: The ABI signature for the function this call's data corresponds to.
|
2021-10-18 10:28:38 +00:00
|
|
|
///
|
|
|
|
/// NOTE: in order to successfully parse the `abi` (`<name>(<args>,...)`) the `<name`>
|
|
|
|
/// must match either the struct name or the name attribute: `#[ethcall(name ="<name>"]`
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```ignore
|
|
|
|
/// use ethers_contract::EthCall;
|
|
|
|
///
|
|
|
|
/// #[derive(Debug, Clone, EthCall)]
|
|
|
|
/// #[ethcall(name ="my_call")]
|
|
|
|
/// struct MyCall {
|
|
|
|
/// addr: Address,
|
|
|
|
/// old_value: String,
|
|
|
|
/// new_value: String,
|
|
|
|
/// }
|
|
|
|
/// assert_eq!(
|
|
|
|
/// MyCall::abi_signature().as_ref(),
|
|
|
|
/// "my_call(address,string,string)"
|
|
|
|
/// );
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// Call with struct inputs
|
|
|
|
///
|
|
|
|
/// ```ignore
|
|
|
|
/// use ethers_core::abi::Address;
|
|
|
|
///
|
|
|
|
/// #[derive(Debug, Clone, PartialEq, EthAbiType)]
|
|
|
|
/// struct SomeType {
|
|
|
|
/// inner: Address,
|
|
|
|
/// msg: String,
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// #[derive(Debug, PartialEq, EthCall)]
|
|
|
|
/// #[ethcall(name = "foo", abi = "foo(address,(address,string),string)")]
|
|
|
|
/// struct FooCall {
|
|
|
|
/// old_author: Address,
|
|
|
|
/// inner: SomeType,
|
|
|
|
/// new_value: String,
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// assert_eq!(
|
|
|
|
/// FooCall::abi_signature().as_ref(),
|
|
|
|
/// "foo(address,(address,string),string)"
|
|
|
|
/// );
|
|
|
|
/// ```
|
|
|
|
#[proc_macro_derive(EthCall, attributes(ethcall))]
|
|
|
|
pub fn derive_abi_call(input: TokenStream) -> TokenStream {
|
|
|
|
let input = parse_macro_input!(input as DeriveInput);
|
|
|
|
TokenStream::from(call::derive_eth_call_impl(input))
|
|
|
|
}
|