From 97582cc346f84255e8139b9a9731e23623b4352a Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 3 Jan 2023 14:11:57 +0100 Subject: [PATCH] feat(core, contract): improve `determine_ethers_crates` (#1988) * refactor: determine_ethers_crates * feat: improve crate resolution * export new types and functions * export * fix: file name check * fix: invert condition and check CARGO_MANIFEST_DIR * use abigen macro to generate multicall * chore: rm debug * rm unnecessary .replace * chore: clippy * chore: clippy * Revert "chore: clippy" This reverts commit bd220f308d12beaaca794ec8583bd9c27b416408. * Revert "chore: clippy" This reverts commit 5550f4e85671254b3bf6bed4db1dce9b3bd6397f. * add tests * better tests, docs * add another test * fix docs * refactor: add an environment struct for determining ethers crates * fix: use fmt::Debug to escape paths * docs: rename and rm old docs * feat: use global path for crates * fix: docs * chore: move rand impl to tests mod --- .../src/multicall/multicall_abi.json | 1 + .../src/multicall/multicall_contract.rs | 941 +----------------- ethers-core/src/macros/ethers_crate.rs | 699 +++++++++++-- ethers-core/src/macros/mod.rs | 2 +- 4 files changed, 594 insertions(+), 1049 deletions(-) create mode 100644 ethers-contract/src/multicall/multicall_abi.json diff --git a/ethers-contract/src/multicall/multicall_abi.json b/ethers-contract/src/multicall/multicall_abi.json new file mode 100644 index 00000000..75da07c0 --- /dev/null +++ b/ethers-contract/src/multicall/multicall_abi.json @@ -0,0 +1 @@ +[{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call[]","name":"calls","type":"tuple[]"}],"name":"aggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes[]","name":"returnData","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bool","name":"allowFailure","type":"bool"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call3[]","name":"calls","type":"tuple[]"}],"name":"aggregate3","outputs":[{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"internalType":"struct Multicall3.Result[]","name":"returnData","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bool","name":"allowFailure","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call3Value[]","name":"calls","type":"tuple[]"}],"name":"aggregate3Value","outputs":[{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"internalType":"struct Multicall3.Result[]","name":"returnData","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call[]","name":"calls","type":"tuple[]"}],"name":"blockAndAggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"internalType":"struct Multicall3.Result[]","name":"returnData","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getBasefee","outputs":[{"internalType":"uint256","name":"basefee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBlockNumber","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"internalType":"uint256","name":"chainid","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockCoinbase","outputs":[{"internalType":"address","name":"coinbase","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockDifficulty","outputs":[{"internalType":"uint256","name":"difficulty","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockGasLimit","outputs":[{"internalType":"uint256","name":"gaslimit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockTimestamp","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getEthBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"requireSuccess","type":"bool"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call[]","name":"calls","type":"tuple[]"}],"name":"tryAggregate","outputs":[{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"internalType":"struct Multicall3.Result[]","name":"returnData","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bool","name":"requireSuccess","type":"bool"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call[]","name":"calls","type":"tuple[]"}],"name":"tryBlockAndAggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"internalType":"struct Multicall3.Result[]","name":"returnData","type":"tuple[]"}],"stateMutability":"payable","type":"function"}] \ No newline at end of file diff --git a/ethers-contract/src/multicall/multicall_contract.rs b/ethers-contract/src/multicall/multicall_contract.rs index 0b58455e..82878124 100644 --- a/ethers-contract/src/multicall/multicall_contract.rs +++ b/ethers-contract/src/multicall/multicall_contract.rs @@ -1,940 +1 @@ -#[allow(clippy::too_many_arguments, non_camel_case_types)] -pub mod multicall_3 { - #![allow(clippy::enum_variant_names)] - #![allow(dead_code)] - #![allow(clippy::type_complexity)] - #![allow(unused_imports)] - - // This is a hack to guarantee all ethers-derive macros can find the types. - // See [`ethers_core::macros::determine_ethers_crates`]. - #[doc(hidden)] - mod ethers { - pub mod contract { - pub use crate::*; - } - pub mod core { - pub use ethers_core::*; - } - pub mod providers { - pub use ethers_providers::*; - } - pub mod types { - pub use ethers_core::types::*; - } - } - #[doc(hidden)] - mod ethers_contract { - pub use crate::*; - } - - use self::ethers_contract::{ - builders::{ContractCall, Event}, - Contract, Lazy, - }; - use ethers_core::{ - abi::{Abi, Detokenize, InvalidOutputType, Token, Tokenizable}, - types::{Address, Bytes, U256}, - }; - use ethers_providers::Middleware; - use std::sync::Arc; - - #[doc = "Multicall3 was auto-generated with ethers-rs Abigen. More information at: https://github.com/gakonst/ethers-rs"] - # [rustfmt :: skip] const __ABI : & str = "[{\"type\":\"function\",\"name\":\"aggregate\",\"inputs\":[{\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"returnData\",\"type\":\"bytes[]\"}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"aggregate3\",\"inputs\":[{\"internalType\":\"struct Multicall3.Call3[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"aggregate3Value\",\"inputs\":[{\"internalType\":\"struct Multicall3.Call3Value[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bool\"},{\"type\":\"uint256\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"blockAndAggregate\",\"inputs\":[{\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"getBasefee\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"basefee\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getBlockHash\",\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getBlockNumber\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getChainId\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"chainid\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getCurrentBlockCoinbase\",\"inputs\":[],\"outputs\":[{\"internalType\":\"address\",\"name\":\"coinbase\",\"type\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getCurrentBlockDifficulty\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"difficulty\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getCurrentBlockGasLimit\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"gaslimit\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getCurrentBlockTimestamp\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getEthBalance\",\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getLastBlockHash\",\"inputs\":[],\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"tryAggregate\",\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"tryBlockAndAggregate\",\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"stateMutability\":\"payable\"}]" ; - #[doc = r" The parsed JSON-ABI of the contract."] - pub static MULTICALL3_ABI: ethers_contract::Lazy = - ethers_contract::Lazy::new(|| { - ethers_core::utils::__serde_json::from_str(__ABI).expect("invalid abi") - }); - pub struct Multicall3(ethers_contract::Contract); - impl Clone for Multicall3 { - fn clone(&self) -> Self { - Multicall3(self.0.clone()) - } - } - impl std::ops::Deref for Multicall3 { - type Target = ethers_contract::Contract; - fn deref(&self) -> &Self::Target { - &self.0 - } - } - impl std::fmt::Debug for Multicall3 { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.debug_tuple(stringify!(Multicall3)).field(&self.address()).finish() - } - } - impl Multicall3 { - #[doc = r" Creates a new contract instance with the specified `ethers`"] - #[doc = r" client at the given `Address`. The contract derefs to a `crate`"] - #[doc = r" object"] - pub fn new>( - address: T, - client: ::std::sync::Arc, - ) -> Self { - ethers_contract::Contract::new(address.into(), MULTICALL3_ABI.clone(), client).into() - } - #[doc = "Calls the contract's `aggregate` (0x252dba42) function"] - pub fn aggregate( - &self, - calls: ::std::vec::Vec, - ) -> ethers_contract::builders::ContractCall< - M, - (ethers_core::types::U256, ::std::vec::Vec), - > { - self.0 - .method_hash([37, 45, 186, 66], calls) - .expect("method not found (this should never happen)") - } - #[doc = "Calls the contract's `aggregate3` (0x82ad56cb) function"] - pub fn aggregate_3( - &self, - calls: ::std::vec::Vec, - ) -> ethers_contract::builders::ContractCall> { - self.0 - .method_hash([130, 173, 86, 203], calls) - .expect("method not found (this should never happen)") - } - #[doc = "Calls the contract's `aggregate3Value` (0x174dea71) function"] - pub fn aggregate_3_value( - &self, - calls: ::std::vec::Vec, - ) -> ethers_contract::builders::ContractCall> { - self.0 - .method_hash([23, 77, 234, 113], calls) - .expect("method not found (this should never happen)") - } - #[doc = "Calls the contract's `blockAndAggregate` (0xc3077fa9) function"] - pub fn block_and_aggregate( - &self, - calls: ::std::vec::Vec, - ) -> ethers_contract::builders::ContractCall< - M, - (ethers_core::types::U256, [u8; 32], ::std::vec::Vec), - > { - self.0 - .method_hash([195, 7, 127, 169], calls) - .expect("method not found (this should never happen)") - } - #[doc = "Calls the contract's `getBasefee` (0x3e64a696) function"] - pub fn get_basefee( - &self, - ) -> ethers_contract::builders::ContractCall { - self.0 - .method_hash([62, 100, 166, 150], ()) - .expect("method not found (this should never happen)") - } - #[doc = "Calls the contract's `getBlockHash` (0xee82ac5e) function"] - pub fn get_block_hash( - &self, - block_number: ethers_core::types::U256, - ) -> ethers_contract::builders::ContractCall { - self.0 - .method_hash([238, 130, 172, 94], block_number) - .expect("method not found (this should never happen)") - } - #[doc = "Calls the contract's `getBlockNumber` (0x42cbb15c) function"] - pub fn get_block_number( - &self, - ) -> ethers_contract::builders::ContractCall { - self.0 - .method_hash([66, 203, 177, 92], ()) - .expect("method not found (this should never happen)") - } - #[doc = "Calls the contract's `getChainId` (0x3408e470) function"] - pub fn get_chain_id( - &self, - ) -> ethers_contract::builders::ContractCall { - self.0 - .method_hash([52, 8, 228, 112], ()) - .expect("method not found (this should never happen)") - } - #[doc = "Calls the contract's `getCurrentBlockCoinbase` (0xa8b0574e) function"] - pub fn get_current_block_coinbase( - &self, - ) -> ethers_contract::builders::ContractCall { - self.0 - .method_hash([168, 176, 87, 78], ()) - .expect("method not found (this should never happen)") - } - #[doc = "Calls the contract's `getCurrentBlockDifficulty` (0x72425d9d) function"] - pub fn get_current_block_difficulty( - &self, - ) -> ethers_contract::builders::ContractCall { - self.0 - .method_hash([114, 66, 93, 157], ()) - .expect("method not found (this should never happen)") - } - #[doc = "Calls the contract's `getCurrentBlockGasLimit` (0x86d516e8) function"] - pub fn get_current_block_gas_limit( - &self, - ) -> ethers_contract::builders::ContractCall { - self.0 - .method_hash([134, 213, 22, 232], ()) - .expect("method not found (this should never happen)") - } - #[doc = "Calls the contract's `getCurrentBlockTimestamp` (0x0f28c97d) function"] - pub fn get_current_block_timestamp( - &self, - ) -> ethers_contract::builders::ContractCall { - self.0 - .method_hash([15, 40, 201, 125], ()) - .expect("method not found (this should never happen)") - } - #[doc = "Calls the contract's `getEthBalance` (0x4d2301cc) function"] - pub fn get_eth_balance( - &self, - addr: ethers_core::types::Address, - ) -> ethers_contract::builders::ContractCall { - self.0 - .method_hash([77, 35, 1, 204], addr) - .expect("method not found (this should never happen)") - } - #[doc = "Calls the contract's `getLastBlockHash` (0x27e86d6e) function"] - pub fn get_last_block_hash(&self) -> ethers_contract::builders::ContractCall { - self.0 - .method_hash([39, 232, 109, 110], ()) - .expect("method not found (this should never happen)") - } - #[doc = "Calls the contract's `tryAggregate` (0xbce38bd7) function"] - pub fn try_aggregate( - &self, - require_success: bool, - calls: ::std::vec::Vec, - ) -> ethers_contract::builders::ContractCall> { - self.0 - .method_hash([188, 227, 139, 215], (require_success, calls)) - .expect("method not found (this should never happen)") - } - #[doc = "Calls the contract's `tryBlockAndAggregate` (0x399542e9) function"] - pub fn try_block_and_aggregate( - &self, - require_success: bool, - calls: ::std::vec::Vec, - ) -> ethers_contract::builders::ContractCall< - M, - (ethers_core::types::U256, [u8; 32], ::std::vec::Vec), - > { - self.0 - .method_hash([57, 149, 66, 233], (require_success, calls)) - .expect("method not found (this should never happen)") - } - } - impl From> for Multicall3 { - fn from(contract: ethers_contract::Contract) -> Self { - Self(contract) - } - } - #[doc = "Container type for all input parameters for the `aggregate` function with signature `aggregate((address,bytes)[])` and selector `[37, 45, 186, 66]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "aggregate", abi = "aggregate((address,bytes)[])")] - pub struct AggregateCall { - pub calls: ::std::vec::Vec, - } - #[doc = "Container type for all input parameters for the `aggregate3` function with signature `aggregate3((address,bool,bytes)[])` and selector `[130, 173, 86, 203]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "aggregate3", abi = "aggregate3((address,bool,bytes)[])")] - pub struct Aggregate3Call { - pub calls: ::std::vec::Vec, - } - #[doc = "Container type for all input parameters for the `aggregate3Value` function with signature `aggregate3Value((address,bool,uint256,bytes)[])` and selector `[23, 77, 234, 113]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "aggregate3Value", abi = "aggregate3Value((address,bool,uint256,bytes)[])")] - pub struct Aggregate3ValueCall { - pub calls: ::std::vec::Vec, - } - #[doc = "Container type for all input parameters for the `blockAndAggregate` function with signature `blockAndAggregate((address,bytes)[])` and selector `[195, 7, 127, 169]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "blockAndAggregate", abi = "blockAndAggregate((address,bytes)[])")] - pub struct BlockAndAggregateCall { - pub calls: ::std::vec::Vec, - } - #[doc = "Container type for all input parameters for the `getBasefee` function with signature `getBasefee()` and selector `[62, 100, 166, 150]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "getBasefee", abi = "getBasefee()")] - pub struct GetBasefeeCall; - #[doc = "Container type for all input parameters for the `getBlockHash` function with signature `getBlockHash(uint256)` and selector `[238, 130, 172, 94]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "getBlockHash", abi = "getBlockHash(uint256)")] - pub struct GetBlockHashCall { - pub block_number: ethers_core::types::U256, - } - #[doc = "Container type for all input parameters for the `getBlockNumber` function with signature `getBlockNumber()` and selector `[66, 203, 177, 92]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "getBlockNumber", abi = "getBlockNumber()")] - pub struct GetBlockNumberCall; - #[doc = "Container type for all input parameters for the `getChainId` function with signature `getChainId()` and selector `[52, 8, 228, 112]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "getChainId", abi = "getChainId()")] - pub struct GetChainIdCall; - #[doc = "Container type for all input parameters for the `getCurrentBlockCoinbase` function with signature `getCurrentBlockCoinbase()` and selector `[168, 176, 87, 78]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "getCurrentBlockCoinbase", abi = "getCurrentBlockCoinbase()")] - pub struct GetCurrentBlockCoinbaseCall; - #[doc = "Container type for all input parameters for the `getCurrentBlockDifficulty` function with signature `getCurrentBlockDifficulty()` and selector `[114, 66, 93, 157]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "getCurrentBlockDifficulty", abi = "getCurrentBlockDifficulty()")] - pub struct GetCurrentBlockDifficultyCall; - #[doc = "Container type for all input parameters for the `getCurrentBlockGasLimit` function with signature `getCurrentBlockGasLimit()` and selector `[134, 213, 22, 232]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "getCurrentBlockGasLimit", abi = "getCurrentBlockGasLimit()")] - pub struct GetCurrentBlockGasLimitCall; - #[doc = "Container type for all input parameters for the `getCurrentBlockTimestamp` function with signature `getCurrentBlockTimestamp()` and selector `[15, 40, 201, 125]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "getCurrentBlockTimestamp", abi = "getCurrentBlockTimestamp()")] - pub struct GetCurrentBlockTimestampCall; - #[doc = "Container type for all input parameters for the `getEthBalance` function with signature `getEthBalance(address)` and selector `[77, 35, 1, 204]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "getEthBalance", abi = "getEthBalance(address)")] - pub struct GetEthBalanceCall { - pub addr: ethers_core::types::Address, - } - #[doc = "Container type for all input parameters for the `getLastBlockHash` function with signature `getLastBlockHash()` and selector `[39, 232, 109, 110]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "getLastBlockHash", abi = "getLastBlockHash()")] - pub struct GetLastBlockHashCall; - #[doc = "Container type for all input parameters for the `tryAggregate` function with signature `tryAggregate(bool,(address,bytes)[])` and selector `[188, 227, 139, 215]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "tryAggregate", abi = "tryAggregate(bool,(address,bytes)[])")] - pub struct TryAggregateCall { - pub require_success: bool, - pub calls: ::std::vec::Vec, - } - #[doc = "Container type for all input parameters for the `tryBlockAndAggregate` function with signature `tryBlockAndAggregate(bool,(address,bytes)[])` and selector `[57, 149, 66, 233]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthCall, - ethers_contract :: EthDisplay, - )] - #[ethcall(name = "tryBlockAndAggregate", abi = "tryBlockAndAggregate(bool,(address,bytes)[])")] - pub struct TryBlockAndAggregateCall { - pub require_success: bool, - pub calls: ::std::vec::Vec, - } - #[derive(Debug, Clone, PartialEq, Eq, ethers_contract :: EthAbiType)] - pub enum Multicall3Calls { - Aggregate(AggregateCall), - Aggregate3(Aggregate3Call), - Aggregate3Value(Aggregate3ValueCall), - BlockAndAggregate(BlockAndAggregateCall), - GetBasefee(GetBasefeeCall), - GetBlockHash(GetBlockHashCall), - GetBlockNumber(GetBlockNumberCall), - GetChainId(GetChainIdCall), - GetCurrentBlockCoinbase(GetCurrentBlockCoinbaseCall), - GetCurrentBlockDifficulty(GetCurrentBlockDifficultyCall), - GetCurrentBlockGasLimit(GetCurrentBlockGasLimitCall), - GetCurrentBlockTimestamp(GetCurrentBlockTimestampCall), - GetEthBalance(GetEthBalanceCall), - GetLastBlockHash(GetLastBlockHashCall), - TryAggregate(TryAggregateCall), - TryBlockAndAggregate(TryBlockAndAggregateCall), - } - impl ethers_core::abi::AbiDecode for Multicall3Calls { - fn decode( - data: impl AsRef<[u8]>, - ) -> ::std::result::Result { - if let Ok(decoded) = - ::decode(data.as_ref()) - { - return Ok(Multicall3Calls::Aggregate(decoded)) - } - if let Ok(decoded) = - ::decode(data.as_ref()) - { - return Ok(Multicall3Calls::Aggregate3(decoded)) - } - if let Ok(decoded) = - ::decode(data.as_ref()) - { - return Ok(Multicall3Calls::Aggregate3Value(decoded)) - } - if let Ok(decoded) = - ::decode(data.as_ref()) - { - return Ok(Multicall3Calls::BlockAndAggregate(decoded)) - } - if let Ok(decoded) = - ::decode(data.as_ref()) - { - return Ok(Multicall3Calls::GetBasefee(decoded)) - } - if let Ok(decoded) = - ::decode(data.as_ref()) - { - return Ok(Multicall3Calls::GetBlockHash(decoded)) - } - if let Ok(decoded) = - ::decode(data.as_ref()) - { - return Ok(Multicall3Calls::GetBlockNumber(decoded)) - } - if let Ok(decoded) = - ::decode(data.as_ref()) - { - return Ok(Multicall3Calls::GetChainId(decoded)) - } - if let Ok(decoded) = - ::decode(data.as_ref()) - { - return Ok(Multicall3Calls::GetCurrentBlockCoinbase(decoded)) - } - if let Ok(decoded) = - ::decode( - data.as_ref(), - ) - { - return Ok(Multicall3Calls::GetCurrentBlockDifficulty(decoded)) - } - if let Ok(decoded) = - ::decode(data.as_ref()) - { - return Ok(Multicall3Calls::GetCurrentBlockGasLimit(decoded)) - } - if let Ok(decoded) = - ::decode(data.as_ref()) - { - return Ok(Multicall3Calls::GetCurrentBlockTimestamp(decoded)) - } - if let Ok(decoded) = - ::decode(data.as_ref()) - { - return Ok(Multicall3Calls::GetEthBalance(decoded)) - } - if let Ok(decoded) = - ::decode(data.as_ref()) - { - return Ok(Multicall3Calls::GetLastBlockHash(decoded)) - } - if let Ok(decoded) = - ::decode(data.as_ref()) - { - return Ok(Multicall3Calls::TryAggregate(decoded)) - } - if let Ok(decoded) = - ::decode(data.as_ref()) - { - return Ok(Multicall3Calls::TryBlockAndAggregate(decoded)) - } - Err(ethers_core::abi::Error::InvalidData.into()) - } - } - impl ethers_core::abi::AbiEncode for Multicall3Calls { - fn encode(self) -> Vec { - match self { - Multicall3Calls::Aggregate(element) => element.encode(), - Multicall3Calls::Aggregate3(element) => element.encode(), - Multicall3Calls::Aggregate3Value(element) => element.encode(), - Multicall3Calls::BlockAndAggregate(element) => element.encode(), - Multicall3Calls::GetBasefee(element) => element.encode(), - Multicall3Calls::GetBlockHash(element) => element.encode(), - Multicall3Calls::GetBlockNumber(element) => element.encode(), - Multicall3Calls::GetChainId(element) => element.encode(), - Multicall3Calls::GetCurrentBlockCoinbase(element) => element.encode(), - Multicall3Calls::GetCurrentBlockDifficulty(element) => element.encode(), - Multicall3Calls::GetCurrentBlockGasLimit(element) => element.encode(), - Multicall3Calls::GetCurrentBlockTimestamp(element) => element.encode(), - Multicall3Calls::GetEthBalance(element) => element.encode(), - Multicall3Calls::GetLastBlockHash(element) => element.encode(), - Multicall3Calls::TryAggregate(element) => element.encode(), - Multicall3Calls::TryBlockAndAggregate(element) => element.encode(), - } - } - } - impl ::std::fmt::Display for Multicall3Calls { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - match self { - Multicall3Calls::Aggregate(element) => element.fmt(f), - Multicall3Calls::Aggregate3(element) => element.fmt(f), - Multicall3Calls::Aggregate3Value(element) => element.fmt(f), - Multicall3Calls::BlockAndAggregate(element) => element.fmt(f), - Multicall3Calls::GetBasefee(element) => element.fmt(f), - Multicall3Calls::GetBlockHash(element) => element.fmt(f), - Multicall3Calls::GetBlockNumber(element) => element.fmt(f), - Multicall3Calls::GetChainId(element) => element.fmt(f), - Multicall3Calls::GetCurrentBlockCoinbase(element) => element.fmt(f), - Multicall3Calls::GetCurrentBlockDifficulty(element) => element.fmt(f), - Multicall3Calls::GetCurrentBlockGasLimit(element) => element.fmt(f), - Multicall3Calls::GetCurrentBlockTimestamp(element) => element.fmt(f), - Multicall3Calls::GetEthBalance(element) => element.fmt(f), - Multicall3Calls::GetLastBlockHash(element) => element.fmt(f), - Multicall3Calls::TryAggregate(element) => element.fmt(f), - Multicall3Calls::TryBlockAndAggregate(element) => element.fmt(f), - } - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: AggregateCall) -> Self { - Multicall3Calls::Aggregate(var) - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: Aggregate3Call) -> Self { - Multicall3Calls::Aggregate3(var) - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: Aggregate3ValueCall) -> Self { - Multicall3Calls::Aggregate3Value(var) - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: BlockAndAggregateCall) -> Self { - Multicall3Calls::BlockAndAggregate(var) - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: GetBasefeeCall) -> Self { - Multicall3Calls::GetBasefee(var) - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: GetBlockHashCall) -> Self { - Multicall3Calls::GetBlockHash(var) - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: GetBlockNumberCall) -> Self { - Multicall3Calls::GetBlockNumber(var) - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: GetChainIdCall) -> Self { - Multicall3Calls::GetChainId(var) - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: GetCurrentBlockCoinbaseCall) -> Self { - Multicall3Calls::GetCurrentBlockCoinbase(var) - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: GetCurrentBlockDifficultyCall) -> Self { - Multicall3Calls::GetCurrentBlockDifficulty(var) - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: GetCurrentBlockGasLimitCall) -> Self { - Multicall3Calls::GetCurrentBlockGasLimit(var) - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: GetCurrentBlockTimestampCall) -> Self { - Multicall3Calls::GetCurrentBlockTimestamp(var) - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: GetEthBalanceCall) -> Self { - Multicall3Calls::GetEthBalance(var) - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: GetLastBlockHashCall) -> Self { - Multicall3Calls::GetLastBlockHash(var) - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: TryAggregateCall) -> Self { - Multicall3Calls::TryAggregate(var) - } - } - impl ::std::convert::From for Multicall3Calls { - fn from(var: TryBlockAndAggregateCall) -> Self { - Multicall3Calls::TryBlockAndAggregate(var) - } - } - #[doc = "Container type for all return fields from the `aggregate` function with signature `aggregate((address,bytes)[])` and selector `[37, 45, 186, 66]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct AggregateReturn { - pub block_number: ethers_core::types::U256, - pub return_data: ::std::vec::Vec, - } - #[doc = "Container type for all return fields from the `aggregate3` function with signature `aggregate3((address,bool,bytes)[])` and selector `[130, 173, 86, 203]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct Aggregate3Return { - pub return_data: ::std::vec::Vec, - } - #[doc = "Container type for all return fields from the `aggregate3Value` function with signature `aggregate3Value((address,bool,uint256,bytes)[])` and selector `[23, 77, 234, 113]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct Aggregate3ValueReturn { - pub return_data: ::std::vec::Vec, - } - #[doc = "Container type for all return fields from the `blockAndAggregate` function with signature `blockAndAggregate((address,bytes)[])` and selector `[195, 7, 127, 169]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct BlockAndAggregateReturn { - pub block_number: ethers_core::types::U256, - pub block_hash: [u8; 32], - pub return_data: ::std::vec::Vec, - } - #[doc = "Container type for all return fields from the `getBasefee` function with signature `getBasefee()` and selector `[62, 100, 166, 150]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct GetBasefeeReturn { - pub basefee: ethers_core::types::U256, - } - #[doc = "Container type for all return fields from the `getBlockHash` function with signature `getBlockHash(uint256)` and selector `[238, 130, 172, 94]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct GetBlockHashReturn { - pub block_hash: [u8; 32], - } - #[doc = "Container type for all return fields from the `getBlockNumber` function with signature `getBlockNumber()` and selector `[66, 203, 177, 92]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct GetBlockNumberReturn { - pub block_number: ethers_core::types::U256, - } - #[doc = "Container type for all return fields from the `getChainId` function with signature `getChainId()` and selector `[52, 8, 228, 112]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct GetChainIdReturn { - pub chainid: ethers_core::types::U256, - } - #[doc = "Container type for all return fields from the `getCurrentBlockCoinbase` function with signature `getCurrentBlockCoinbase()` and selector `[168, 176, 87, 78]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct GetCurrentBlockCoinbaseReturn { - pub coinbase: ethers_core::types::Address, - } - #[doc = "Container type for all return fields from the `getCurrentBlockDifficulty` function with signature `getCurrentBlockDifficulty()` and selector `[114, 66, 93, 157]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct GetCurrentBlockDifficultyReturn { - pub difficulty: ethers_core::types::U256, - } - #[doc = "Container type for all return fields from the `getCurrentBlockGasLimit` function with signature `getCurrentBlockGasLimit()` and selector `[134, 213, 22, 232]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct GetCurrentBlockGasLimitReturn { - pub gaslimit: ethers_core::types::U256, - } - #[doc = "Container type for all return fields from the `getCurrentBlockTimestamp` function with signature `getCurrentBlockTimestamp()` and selector `[15, 40, 201, 125]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct GetCurrentBlockTimestampReturn { - pub timestamp: ethers_core::types::U256, - } - #[doc = "Container type for all return fields from the `getEthBalance` function with signature `getEthBalance(address)` and selector `[77, 35, 1, 204]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct GetEthBalanceReturn { - pub balance: ethers_core::types::U256, - } - #[doc = "Container type for all return fields from the `getLastBlockHash` function with signature `getLastBlockHash()` and selector `[39, 232, 109, 110]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct GetLastBlockHashReturn { - pub block_hash: [u8; 32], - } - #[doc = "Container type for all return fields from the `tryAggregate` function with signature `tryAggregate(bool,(address,bytes)[])` and selector `[188, 227, 139, 215]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct TryAggregateReturn { - pub return_data: ::std::vec::Vec, - } - #[doc = "Container type for all return fields from the `tryBlockAndAggregate` function with signature `tryBlockAndAggregate(bool,(address,bytes)[])` and selector `[57, 149, 66, 233]`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct TryBlockAndAggregateReturn { - pub block_number: ethers_core::types::U256, - pub block_hash: [u8; 32], - pub return_data: ::std::vec::Vec, - } - #[doc = "`Call(address,bytes)`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct Call { - pub target: ethers_core::types::Address, - pub call_data: ethers_core::types::Bytes, - } - #[doc = "`Call3(address,bool,bytes)`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct Call3 { - pub target: ethers_core::types::Address, - pub allow_failure: bool, - pub call_data: ethers_core::types::Bytes, - } - #[doc = "`Call3Value(address,bool,uint256,bytes)`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct Call3Value { - pub target: ethers_core::types::Address, - pub allow_failure: bool, - pub value: ethers_core::types::U256, - pub call_data: ethers_core::types::Bytes, - } - #[doc = "`Result(bool,bytes)`"] - #[derive( - Clone, - Debug, - Default, - Eq, - PartialEq, - ethers_contract :: EthAbiType, - ethers_contract :: EthAbiCodec, - )] - pub struct Result { - pub success: bool, - pub return_data: ethers_core::types::Bytes, - } -} +ethers_contract_derive::abigen!(Multicall3, "src/multicall/multicall_abi.json"); diff --git a/ethers-core/src/macros/ethers_crate.rs b/ethers-core/src/macros/ethers_crate.rs index 2c33eb5c..5e984c23 100644 --- a/ethers-core/src/macros/ethers_crate.rs +++ b/ethers-core/src/macros/ethers_crate.rs @@ -1,123 +1,606 @@ use cargo_metadata::MetadataCommand; use once_cell::sync::Lazy; +use std::{ + collections::HashMap, + env, fmt, fs, + path::{Path, PathBuf}, +}; +use strum::{EnumCount, EnumIter, EnumString, EnumVariantNames, IntoEnumIterator}; -use syn::Path; +/// `ethers_crate => name` +type CrateNames = HashMap; -/// See `determine_ethers_crates` +const DIRS: [&str; 3] = ["benches", "examples", "tests"]; + +/// Maps an [`EthersCrate`] to its path string. /// -/// This ensures that the `MetadataCommand` is only run once -static ETHERS_CRATES: Lazy<(&'static str, &'static str, &'static str)> = - Lazy::new(determine_ethers_crates); +/// See [`ProjectEnvironment`] for more information. +/// +/// Note: this static variable cannot hold [`syn::Path`] because it is not [`Sync`], so the names +/// must be parsed at every call. +static ETHERS_CRATE_NAMES: Lazy = Lazy::new(|| { + ProjectEnvironment::new_from_env() + .and_then(|x| x.determine_ethers_crates()) + .unwrap_or_else(|| EthersCrate::ethers_path_names().collect()) +}); -/// Convenience function to turn the `ethers_core` name in `ETHERS_CRATE` into a `Path` -pub fn ethers_core_crate() -> Path { - syn::parse_str(ETHERS_CRATES.0).expect("valid path; qed") -} -/// Convenience function to turn the `ethers_contract` name in `ETHERS_CRATE` into an `Path` -pub fn ethers_contract_crate() -> Path { - syn::parse_str(ETHERS_CRATES.1).expect("valid path; qed") -} -pub fn ethers_providers_crate() -> Path { - syn::parse_str(ETHERS_CRATES.2).expect("valid path; qed") +/// Returns the `core` crate's [`Path`][syn::Path]. +#[inline] +pub fn ethers_core_crate() -> syn::Path { + get_crate_path(EthersCrate::EthersCore) } -/// The crates name to use when deriving macros: (`core`, `contract`) -/// -/// We try to determine which crate ident to use based on the dependencies of -/// the project in which the macro is used. This is useful because the macros, -/// like `EthEvent` are provided by the `ethers-contract` crate which depends on -/// `ethers_core`. Most commonly `ethers` will be used as dependency which -/// reexports all the different crates, essentially `ethers::core` is -/// `ethers_core` So depending on the dependency used `ethers` ors `ethers_core -/// | ethers_contract`, we need to use the fitting crate ident when expand the -/// macros This will attempt to parse the current `Cargo.toml` and check the -/// ethers related dependencies. -/// -/// This determines -/// - `ethers_*` idents if `ethers-core`, `ethers-contract`, `ethers-providers` are present in -/// the manifest or the `ethers` is _not_ present -/// - `ethers::*` otherwise -/// -/// This process is a bit hacky, we run `cargo metadata` internally which -/// resolves the current package but creates a new `Cargo.lock` file in the -/// process. This is not a problem for regular workspaces but becomes an issue -/// during publishing with `cargo publish` if the project does not ignore -/// `Cargo.lock` in `.gitignore`, because then cargo can't proceed with -/// publishing the crate because the created `Cargo.lock` leads to a modified -/// workspace, not the `CARGO_MANIFEST_DIR` but the workspace `cargo publish` -/// created in `./target/package/..`. Therefore we check prior to executing -/// `cargo metadata` if a `Cargo.lock` file exists and delete it afterwards if -/// it was created by `cargo metadata`. -pub fn determine_ethers_crates() -> (&'static str, &'static str, &'static str) { - let manifest_dir = std::env::var("CARGO_MANIFEST_DIR"); +/// Returns the `contract` crate's [`Path`][syn::Path]. +#[inline] +pub fn ethers_contract_crate() -> syn::Path { + get_crate_path(EthersCrate::EthersContract) +} - // if there is no cargo manifest, default to `ethers::`-style imports. - let manifest_dir = if let Ok(manifest_dir) = manifest_dir { - manifest_dir - } else { - return ("ethers::core", "ethers::contract", "ethers::providers") - }; +/// Returns the `providers` crate's [`Path`][syn::Path]. +#[inline] +pub fn ethers_providers_crate() -> syn::Path { + get_crate_path(EthersCrate::EthersProviders) +} - // check if the lock file exists, if it's missing we need to clean up afterward - let lock_file = format!("{manifest_dir}/Cargo.lock"); - let needs_lock_file_cleanup = !std::path::Path::new(&lock_file).exists(); +/// Returns an [`EthersCrate`]'s [`Path`][syn::Path] in the current project. +#[inline(always)] +pub fn get_crate_path(krate: EthersCrate) -> syn::Path { + krate.get_path() +} - let res = MetadataCommand::new() - .manifest_path(format!("{manifest_dir}/Cargo.toml")) - .exec() - .ok() - .and_then(|metadata| { - metadata.root_package().and_then(|pkg| { - let sub_crates = Some(("ethers_core", "ethers_contract", "ethers_providers")); +/// Represents a generic Rust/Cargo project's environment. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ProjectEnvironment { + manifest_dir: PathBuf, + crate_name: Option, +} - // Note(mattsse): this is super hacky but required in order to compile and test - // ethers' internal crates - if [ - "ethers-contract", - "ethers-derive-eip712", - "ethers-signers", - "ethers-middleware", - "ethers-solc", - ] - .contains(&pkg.name.as_str()) - { - return sub_crates - } - - let mut has_ethers_core = false; - let mut has_ethers_contract = false; - let mut has_ethers_providers = false; - - for dep in pkg.dependencies.iter() { - match dep.name.as_str() { - "ethers-core" => { - has_ethers_core = true; - } - "ethers-contract" => { - has_ethers_contract = true; - } - "ethers-providers" => { - has_ethers_providers = true; - } - "ethers" => return None, - _ => {} - } - } - - if has_ethers_core && has_ethers_contract && has_ethers_providers { - return sub_crates - } - - None - }) - }) - .unwrap_or(("ethers::core", "ethers::contract", "ethers::providers")); - - if needs_lock_file_cleanup { - // delete the `Cargo.lock` file that was created by `cargo metadata` - // if the package is not part of a workspace - let _ = std::fs::remove_file(lock_file); +impl ProjectEnvironment { + pub fn new, U: Into>(manifest_dir: T, crate_name: U) -> Self { + Self { manifest_dir: manifest_dir.into(), crate_name: Some(crate_name.into()) } } - res + pub fn new_from_env() -> Option { + Some(Self { + manifest_dir: env::var_os("CARGO_MANIFEST_DIR")?.into(), + crate_name: env::var("CARGO_CRATE_NAME").ok(), + }) + } + + /// Determines the crate paths to use by looking at the [metadata][cargo_metadata] of the + /// project. + /// + /// The names will be: + /// - `ethers::*` if `ethers` is a dependency for all crates; + /// - for each `crate`: + /// - `ethers_` if it is a dependency, otherwise `ethers::`. + #[inline] + pub fn determine_ethers_crates(&self) -> Option { + let lock_file = self.manifest_dir.join("Cargo.lock"); + let lock_file_existed = lock_file.exists(); + + let names = self.crate_names_from_metadata(); + + // remove the lock file created from running the command + if !lock_file_existed && lock_file.exists() { + let _ = std::fs::remove_file(lock_file); + } + + names + } + + #[inline] + pub fn crate_names_from_metadata(&self) -> Option { + let metadata = MetadataCommand::new().current_dir(&self.manifest_dir).exec().ok()?; + let pkg = metadata.root_package()?; + + // return ethers_* if the root package is an internal ethers crate since `ethers` is not + // available + let crate_is_root = self.is_crate_root(); + if let Ok(current_pkg) = pkg.name.parse::() { + // replace `current_pkg`'s name with "crate" + let names = + EthersCrate::path_names() + .map(|(pkg, name)| { + if crate_is_root && pkg == current_pkg { + (pkg, "crate") + } else { + (pkg, name) + } + }) + .collect(); + return Some(names) + } /* else if pkg.name == "ethers" { + // should not happen (the root package the `ethers` workspace package itself) + } */ + + let mut names: CrateNames = EthersCrate::ethers_path_names().collect(); + for dep in pkg.dependencies.iter() { + let name = dep.name.as_str(); + if name.starts_with("ethers") { + if name == "ethers" { + return None + } else if let Ok(dep) = name.parse::() { + names.insert(dep, dep.path_name()); + } + } + } + Some(names) + } + + /// Returns whether the `crate` path identifier refers to the root package. + /// + /// This is false for integration tests, benches, and examples, as the `crate` keyword will not + /// refer to the root package. + /// + /// We can find this using some [environment variables set by Cargo during compilation][ref]: + /// - `CARGO_TARGET_TMPDIR` is only set when building integration test or benchmark code; + /// - When `CARGO_MANIFEST_DIR` contains `/benches/` or `/examples/` + /// - `CARGO_CRATE_NAME`, see `is_crate_name_in_dirs`. + /// + /// [ref]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates + #[inline] + pub fn is_crate_root(&self) -> bool { + env::var_os("CARGO_TARGET_TMPDIR").is_none() && + self.manifest_dir.components().all(|c| { + let s = c.as_os_str(); + s != "examples" && s != "benches" + }) && + !self.is_crate_name_in_dirs() + } + + /// Returns whether `crate_name` is the name of a file or directory in the first level of + /// `manifest_dir/{benches,examples,tests}/`. + /// + /// # Example + /// + /// With this project structure: + /// + /// ```text + /// . + /// ├── Cargo.lock + /// ├── Cargo.toml + /// ├── src/ + /// │   ... + /// ├── benches/ + /// │   ├── large-input.rs + /// │   └── multi-file-bench/ + /// │   ├── main.rs + /// │   └── bench_module.rs + /// ├── examples/ + /// │   ├── simple.rs + /// │   └── multi-file-example/ + /// │   ├── main.rs + /// │   └── ex_module.rs + /// └── tests/ + /// ├── some-integration-tests.rs + /// └── multi-file-test/ + /// ├── main.rs + /// └── test_module.rs + /// ``` + /// + /// The resulting `CARGO_CRATE_NAME` values will be: + /// + /// | Path | Value | + /// |:-------------------------------------- | ----------------------:| + /// | benches/large-input.rs | large-input | + /// | benches/multi-file-bench/\*\*/\*.rs | multi-file-bench | + /// | examples/simple.rs | simple | + /// | examples/multi-file-example/\*\*/\*.rs | multi-file-example | + /// | tests/some-integration-tests.rs | some-integration-tests | + /// | tests/multi-file-test/\*\*/\*.rs | multi-file-test | + #[inline] + pub fn is_crate_name_in_dirs(&self) -> bool { + let crate_name = match self.crate_name.as_ref() { + Some(name) => name, + None => return false, + }; + let dirs = DIRS.map(|dir| self.manifest_dir.join(dir)); + dirs.iter().any(|dir| { + fs::read_dir(dir) + .ok() + .and_then(|entries| { + entries + .filter_map(Result::ok) + .find(|entry| file_stem_eq(entry.path(), crate_name)) + }) + .is_some() + }) + } +} + +/// An `ethers-rs` internal crate. +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + EnumCount, + EnumIter, + EnumString, + EnumVariantNames, +)] +#[strum(serialize_all = "kebab-case")] +pub enum EthersCrate { + EthersAddressbook, + EthersContract, + EthersContractAbigen, + EthersContractDerive, + EthersCore, + EthersDeriveEip712, + EthersEtherscan, + EthersMiddleware, + EthersProviders, + EthersSigners, + EthersSolc, +} + +impl AsRef for EthersCrate { + fn as_ref(&self) -> &str { + self.crate_name() + } +} + +impl fmt::Display for EthersCrate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad(self.as_ref()) + } +} + +impl EthersCrate { + /// "``" + #[inline] + pub const fn crate_name(self) -> &'static str { + match self { + Self::EthersAddressbook => "ethers-addressbook", + Self::EthersContract => "ethers-contract", + Self::EthersContractAbigen => "ethers-contract-abigen", + Self::EthersContractDerive => "ethers-contract-derive", + Self::EthersCore => "ethers-core", + Self::EthersDeriveEip712 => "ethers-derive-eip712", + Self::EthersEtherscan => "ethers-etherscan", + Self::EthersMiddleware => "ethers-middleware", + Self::EthersProviders => "ethers-providers", + Self::EthersSigners => "ethers-signers", + Self::EthersSolc => "ethers-solc", + } + } + + /// "`::`" + #[inline] + pub const fn path_name(self) -> &'static str { + match self { + Self::EthersAddressbook => "::ethers_addressbook", + Self::EthersContract => "::ethers_contract", + Self::EthersContractAbigen => "::ethers_contract_abigen", + Self::EthersContractDerive => "::ethers_contract_derive", + Self::EthersCore => "::ethers_core", + Self::EthersDeriveEip712 => "::ethers_derive_eip712", + Self::EthersEtherscan => "::ethers_etherscan", + Self::EthersMiddleware => "::ethers_middleware", + Self::EthersProviders => "::ethers_providers", + Self::EthersSigners => "::ethers_signers", + Self::EthersSolc => "::ethers_solc", + } + } + + /// "::ethers::``" + #[inline] + pub const fn ethers_path_name(self) -> &'static str { + match self { + // re-exported in ethers::contract + Self::EthersContractAbigen => "::ethers::contract", // partly + Self::EthersContractDerive => "::ethers::contract", + Self::EthersDeriveEip712 => "::ethers::contract", + + Self::EthersAddressbook => "::ethers::addressbook", + Self::EthersContract => "::ethers::contract", + Self::EthersCore => "::ethers::core", + Self::EthersEtherscan => "::ethers::etherscan", + Self::EthersMiddleware => "::ethers::middleware", + Self::EthersProviders => "::ethers::providers", + Self::EthersSigners => "::ethers::signers", + Self::EthersSolc => "::ethers::solc", + } + } + + /// The path on the file system, from an `ethers-rs` root directory. + #[inline] + pub const fn fs_path(self) -> &'static str { + match self { + Self::EthersContractAbigen => "ethers-contract/ethers-contract-abigen", + Self::EthersContractDerive => "ethers-contract/ethers-contract-derive", + Self::EthersDeriveEip712 => "ethers-core/ethers-derive-eip712", + _ => self.crate_name(), + } + } + + /// `` + #[inline] + pub fn path_names() -> impl Iterator { + Self::iter().map(|x| (x, x.path_name())) + } + + /// `` + #[inline] + pub fn ethers_path_names() -> impl Iterator { + Self::iter().map(|x| (x, x.ethers_path_name())) + } + + /// Returns the [`Path`][syn::Path] in the current project. + #[inline] + pub fn get_path(&self) -> syn::Path { + let name = ETHERS_CRATE_NAMES[self]; + syn::parse_str(name).unwrap() + } +} + +/// `path.file_stem() == s` +#[inline] +fn file_stem_eq, U: AsRef>(path: T, s: U) -> bool { + if let Some(stem) = path.as_ref().file_stem() { + if let Some(stem) = stem.to_str() { + return stem == s.as_ref() + } + } + false +} + +#[cfg(test)] +mod tests { + use super::*; + use rand::{ + distributions::{Distribution, Standard}, + thread_rng, Rng, + }; + use std::{ + collections::{BTreeMap, HashSet}, + env, fs, + }; + use tempfile::TempDir; + + impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> EthersCrate { + const RANGE: std::ops::Range = 0..EthersCrate::COUNT as u8; + // SAFETY: generates in the safe range + unsafe { std::mem::transmute(rng.gen_range(RANGE)) } + } + } + + #[test] + fn test_names() { + fn assert_names(s: &ProjectEnvironment, ethers: bool, dependencies: &[EthersCrate]) { + write_manifest(s, ethers, dependencies); + + // speeds up consecutive runs by not having to re-create and delete the lockfile + // this is tested separately: test_lock_file + std::fs::write(s.manifest_dir.join("Cargo.lock"), "").unwrap(); + + let names = s + .determine_ethers_crates() + .unwrap_or_else(|| EthersCrate::ethers_path_names().collect()); + + let krate = s.crate_name.as_ref().and_then(|x| x.parse::().ok()); + let is_internal = krate.is_some(); + let mut expected: CrateNames = match (is_internal, ethers) { + // internal + (true, _) => EthersCrate::path_names().collect(), + + // ethers + (_, true) => EthersCrate::ethers_path_names().collect(), + + // no ethers + (_, false) => { + let mut n: CrateNames = EthersCrate::ethers_path_names().collect(); + for &dep in dependencies { + n.insert(dep, dep.path_name()); + } + n + } + }; + + if is_internal { + expected.insert(krate.unwrap(), "crate"); + } + + // don't use assert for a better custom message + if names != expected { + // BTreeMap sorts the keys + let names: BTreeMap<_, _> = names.into_iter().collect(); + let expected: BTreeMap<_, _> = expected.into_iter().collect(); + panic!("\nCase failed: (`{:?}`, `{ethers}`, `{dependencies:?}`)\nNames: {names:#?}\nExpected: {expected:#?}\n", s.crate_name); + } + } + + fn gen_unique() -> [EthersCrate; N] { + assert!(N < EthersCrate::COUNT); + let rng = &mut thread_rng(); + let mut set = HashSet::with_capacity(N); + while set.len() < N { + set.insert(rng.gen()); + } + let vec: Vec<_> = set.into_iter().collect(); + vec.try_into().unwrap() + } + + let (s, _dir) = test_project(); + // crate_name -> represents an external crate + // "ethers-contract" -> represents an internal crate + for name in [s.crate_name.as_ref().unwrap(), "ethers-contract"] { + let s = ProjectEnvironment::new(&s.manifest_dir, name); + // only ethers + assert_names(&s, true, &[]); + + // only others + assert_names(&s, false, gen_unique::<3>().as_slice()); + + // ethers and others + assert_names(&s, true, gen_unique::<3>().as_slice()); + } + } + + #[test] + fn test_lock_file() { + let (s, _dir) = test_project(); + write_manifest(&s, true, &[]); + let lock_file = s.manifest_dir.join("Cargo.lock"); + + assert!(!lock_file.exists()); + s.determine_ethers_crates(); + assert!(!lock_file.exists()); + + std::fs::write(&lock_file, "").unwrap(); + + assert!(lock_file.exists()); + s.determine_ethers_crates(); + assert!(lock_file.exists()); + assert!(!std::fs::read(lock_file).unwrap().is_empty()); + } + + #[test] + fn test_is_crate_root() { + let (s, _dir) = test_project(); + assert!(s.is_crate_root()); + + // `CARGO_MANIFEST_DIR` + // complex path has `/{dir_name}/` in the path + // name or path validity not checked + let s = ProjectEnvironment::new( + s.manifest_dir.join("examples/complex_examples"), + "complex-examples", + ); + assert!(!s.is_crate_root()); + let s = ProjectEnvironment::new( + s.manifest_dir.join("benches/complex_benches"), + "complex-benches", + ); + assert!(!s.is_crate_root()); + } + + #[test] + fn test_is_crate_name_in_dirs() { + let (s, _dir) = test_project(); + let root = &s.manifest_dir; + + for dir_name in DIRS { + for ty in ["simple", "complex"] { + let s = ProjectEnvironment::new(root, format!("{ty}_{dir_name}")); + assert!(s.is_crate_name_in_dirs(), "{s:?}"); + } + } + + let s = ProjectEnvironment::new(root, "non_existant"); + assert!(!s.is_crate_name_in_dirs()); + let s = ProjectEnvironment::new(root.join("does-not-exist"), "foo_bar"); + assert!(!s.is_crate_name_in_dirs()); + } + + #[test] + fn test_file_stem_eq() { + let path = Path::new("/tmp/foo.rs"); + assert!(file_stem_eq(path, "foo")); + assert!(!file_stem_eq(path, "tmp")); + assert!(!file_stem_eq(path, "foo.rs")); + assert!(!file_stem_eq(path, "fo")); + assert!(!file_stem_eq(path, "f")); + assert!(!file_stem_eq(path, "")); + + let path = Path::new("/tmp/foo/"); + assert!(file_stem_eq(path, "foo")); + assert!(!file_stem_eq(path, "tmp")); + assert!(!file_stem_eq(path, "fo")); + assert!(!file_stem_eq(path, "f")); + assert!(!file_stem_eq(path, "")); + } + + // utils + + /// Creates: + /// + /// ```text + /// - new_dir + /// - src + /// - main.rs + /// - {dir_name} for dir_name in DIRS + /// - simple_{dir_name}.rs + /// - complex_{dir_name} + /// - src if not "tests" + /// - main.rs + /// - module.rs + /// ``` + fn test_project() -> (ProjectEnvironment, TempDir) { + // change the prefix to one without the default `.` because it is not a valid crate name + let dir = tempfile::Builder::new().prefix("tmp").tempdir().unwrap(); + let root = dir.path(); + let name = root.file_name().unwrap().to_str().unwrap(); + + // No Cargo.toml, git + fs::create_dir_all(root).unwrap(); + let src = root.join("src"); + fs::create_dir(&src).unwrap(); + fs::write(src.join("main.rs"), "fn main(){}").unwrap(); + + for dir_name in DIRS { + let new_dir = root.join(dir_name); + fs::create_dir(&new_dir).unwrap(); + + let simple = new_dir.join(format!("simple_{dir_name}.rs")); + fs::write(simple, "").unwrap(); + + let mut complex = new_dir.join(format!("complex_{dir_name}")); + if dir_name != "tests" { + fs::create_dir(&complex).unwrap(); + fs::write(complex.join("Cargo.toml"), "").unwrap(); + complex.push("src"); + } + fs::create_dir(&complex).unwrap(); + fs::write(complex.join("main.rs"), "").unwrap(); + fs::write(complex.join("module.rs"), "").unwrap(); + } + + // create target dirs + let target = root.join("target"); + fs::create_dir(&target).unwrap(); + fs::create_dir_all(target.join("tmp")).unwrap(); + + (ProjectEnvironment::new(root, name), dir) + } + + /// Writes a test manifest to `{root}/Cargo.toml`. + fn write_manifest(s: &ProjectEnvironment, ethers: bool, dependencies: &[EthersCrate]) { + // use paths to avoid downloading dependencies + const ETHERS_CORE: &str = env!("CARGO_MANIFEST_DIR"); + let ethers_root = Path::new(ETHERS_CORE).parent().unwrap(); + let mut dependencies_toml = + String::with_capacity(150 * (ethers as usize + dependencies.len())); + + if ethers { + let ethers = format!("ethers = {{ path = {ethers_root:?} }}\n"); + dependencies_toml.push_str(ðers); + } + + for dep in dependencies.iter() { + let path = ethers_root.join(dep.fs_path()); + let dep = format!("{dep} = {{ path = {path:?} }}\n"); + dependencies_toml.push_str(&dep); + } + + let contents = format!( + r#" +[package] +name = "{}" +version = "0.0.0" +edition = "2021" + +[dependencies] +{dependencies_toml} +"#, + s.crate_name.as_ref().unwrap() + ); + fs::write(s.manifest_dir.join("Cargo.toml"), contents).unwrap(); + } } diff --git a/ethers-core/src/macros/mod.rs b/ethers-core/src/macros/mod.rs index 6a857b11..456f27bd 100644 --- a/ethers-core/src/macros/mod.rs +++ b/ethers-core/src/macros/mod.rs @@ -1,2 +1,2 @@ mod ethers_crate; -pub use ethers_crate::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate}; +pub use ethers_crate::*;