diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 708910a9..9e45e770 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,7 +94,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: live tests - run: cargo test -p ethers --test live --all-features + run: cargo test -p ethers --all-features # TODO: [#2191](https://github.com/gakonst/ethers-rs/issues/2191) # feature-checks: diff --git a/Cargo.lock b/Cargo.lock index 96a617dc..d4bde293 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1219,6 +1219,7 @@ dependencies = [ "ethers-providers", "ethers-signers", "ethers-solc", + "serde", "tokio", ] @@ -1241,7 +1242,6 @@ dependencies = [ "ethers-core", "ethers-derive-eip712", "ethers-providers", - "ethers-signers", "ethers-solc", "futures-util", "hex", diff --git a/ethers-contract/Cargo.toml b/ethers-contract/Cargo.toml index a0c65795..dc0d5483 100644 --- a/ethers-contract/Cargo.toml +++ b/ethers-contract/Cargo.toml @@ -43,8 +43,8 @@ ethers-contract-derive = { workspace = true, optional = true } ethers-derive-eip712 = { workspace = true, optional = true } [dev-dependencies] -ethers-signers.workspace = true ethers-solc.workspace = true +ethers-providers = { workspace = true, features = ["ws"] } [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] tokio = { workspace = true, features = ["macros"] } diff --git a/ethers-contract/src/contract.rs b/ethers-contract/src/contract.rs index 2fa4f888..eae39a46 100644 --- a/ethers-contract/src/contract.rs +++ b/ethers-contract/src/contract.rs @@ -75,7 +75,6 @@ pub type Contract = ContractInstance, M>; /// }; /// use ethers_contract::Contract; /// use ethers_providers::{Provider, Http}; -/// use ethers_signers::Wallet; /// use std::{convert::TryFrom, sync::Arc}; /// /// # async fn foo() -> Result<(), Box> { @@ -122,7 +121,6 @@ pub type Contract = ContractInstance, M>; /// use ethers_core::{abi::Abi, types::Address}; /// use ethers_contract::{Contract, EthEvent}; /// use ethers_providers::{Provider, Http, Middleware}; -/// use ethers_signers::Wallet; /// use std::{convert::TryFrom, sync::Arc}; /// use ethers_core::abi::{Detokenize, Token, InvalidOutputType}; /// # // this is a fake address used just for this example diff --git a/ethers-contract/src/factory.rs b/ethers-contract/src/factory.rs index b396ef0b..711e44a2 100644 --- a/ethers-contract/src/factory.rs +++ b/ethers-contract/src/factory.rs @@ -323,7 +323,6 @@ where /// use ethers_solc::Solc; /// use ethers_contract::ContractFactory; /// use ethers_providers::{Provider, Http}; -/// use ethers_signers::Wallet; /// use std::convert::TryFrom; /// /// # async fn foo() -> Result<(), Box> { diff --git a/ethers-contract/tests/it/contract.rs b/ethers-contract/tests/it/contract.rs index 8d4dd44e..88f04ed5 100644 --- a/ethers-contract/tests/it/contract.rs +++ b/ethers-contract/tests/it/contract.rs @@ -1,18 +1,14 @@ use crate::common::*; use ethers_contract::{ - abigen, ContractFactory, ContractInstance, EthAbiType, EthEvent, LogMeta, Multicall, - MulticallError, MulticallVersion, + abigen, ContractFactory, ContractInstance, EthEvent, LogMeta, Multicall, MulticallError, + MulticallVersion, }; use ethers_core::{ abi::{encode, AbiEncode, Token, Tokenizable}, - types::{ - transaction::eip712::Eip712, Address, BlockId, Bytes, Filter, ValueOrArray, H160, H256, - I256, U256, - }, + types::{Address, BlockId, Bytes, Filter, ValueOrArray, H160, H256, U256}, utils::{keccak256, Anvil}, }; use ethers_providers::{Http, Middleware, MiddlewareError, Provider, StreamExt}; -use ethers_signers::{LocalWallet, Signer}; use std::{sync::Arc, time::Duration}; #[derive(Debug)] @@ -330,7 +326,6 @@ async fn call_past_hash_test() { } #[tokio::test] -#[cfg(feature = "abigen")] async fn watch_events() { let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); let anvil = Anvil::new().spawn(); @@ -784,146 +779,3 @@ async fn multicall_aggregate() { assert_eq!(bytes[..4], keccak256("CustomErrorWithData(string)")[..4]); assert_eq!(bytes[4..], encode(&[Token::String("Data".to_string())])); } - -#[tokio::test] -#[cfg(feature = "eip712")] -async fn test_derive_eip712() { - use ethers_derive_eip712::*; - - // Generate Contract ABI Bindings - abigen!( - DeriveEip712Test, - "./ethers-contract/tests/solidity-contracts/derive_eip712_abi.json", - event_derives(serde::Deserialize, serde::Serialize) - ); - - // Create derived structs - - #[derive(Debug, Clone, Eip712, EthAbiType)] - #[eip712( - name = "Eip712Test", - version = "1", - chain_id = 1, - verifying_contract = "0x0000000000000000000000000000000000000001", - salt = "eip712-test-75F0CCte" - )] - struct FooBar { - foo: I256, - bar: U256, - fizz: Bytes, - buzz: [u8; 32], - far: String, - out: Address, - } - - // get ABI and bytecode for the DeriveEip712Test contract - let (abi, bytecode) = compile_contract("DeriveEip712Test", "DeriveEip712Test.sol"); - - // launch the network & connect to it - let anvil = Anvil::new().spawn(); - let from = anvil.addresses()[0]; - let provider = Provider::try_from(anvil.endpoint()) - .unwrap() - .with_sender(from) - .interval(std::time::Duration::from_millis(10)); - let client = Arc::new(provider); - - let wallet: LocalWallet = anvil.keys()[0].clone().into(); - - let factory = ContractFactory::new(abi.clone(), bytecode.clone(), client.clone()); - - let contract = factory - .deploy(()) - .expect("failed to deploy DeriveEip712Test contract") - .legacy() - .send() - .await - .expect("failed to instantiate factory for DeriveEip712 contract"); - - let addr = contract.address(); - - let contract = DeriveEip712Test::new(addr, client.clone()); - - let foo_bar = FooBar { - foo: I256::from(10u64), - bar: U256::from(20u64), - fizz: b"fizz".into(), - buzz: keccak256("buzz"), - far: String::from("space"), - out: Address::from([0; 20]), - }; - - let derived_foo_bar = derive_eip_712_test::FooBar { - foo: foo_bar.foo, - bar: foo_bar.bar, - fizz: foo_bar.fizz.clone(), - buzz: foo_bar.buzz, - far: foo_bar.far.clone(), - out: foo_bar.out, - }; - - let sig = wallet.sign_typed_data(&foo_bar).await.expect("failed to sign typed data"); - - let r = <[u8; 32]>::try_from(sig.r) - .expect("failed to parse 'r' value from signature into [u8; 32]"); - let s = <[u8; 32]>::try_from(sig.s) - .expect("failed to parse 's' value from signature into [u8; 32]"); - let v = u8::try_from(sig.v).expect("failed to parse 'v' value from signature into u8"); - - let domain_separator = contract - .domain_separator() - .call() - .await - .expect("failed to retrieve domain_separator from contract"); - let type_hash = - contract.type_hash().call().await.expect("failed to retrieve type_hash from contract"); - let struct_hash = contract - .struct_hash(derived_foo_bar.clone()) - .call() - .await - .expect("failed to retrieve struct_hash from contract"); - let encoded = contract - .encode_eip_712(derived_foo_bar.clone()) - .call() - .await - .expect("failed to retrieve eip712 encoded hash from contract"); - let verify = contract - .verify_foo_bar(wallet.address(), derived_foo_bar, r, s, v) - .call() - .await - .expect("failed to verify signed typed data eip712 payload"); - - assert_eq!( - domain_separator, - foo_bar - .domain() - .expect("failed to return domain_separator from Eip712 implemented struct") - .separator(), - "domain separator does not match contract domain separator!" - ); - - assert_eq!( - type_hash, - FooBar::type_hash().expect("failed to return type_hash from Eip712 implemented struct"), - "type hash does not match contract struct type hash!" - ); - - assert_eq!( - struct_hash, - foo_bar - .clone() - .struct_hash() - .expect("failed to return struct_hash from Eip712 implemented struct"), - "struct hash does not match contract struct hash!" - ); - - assert_eq!( - encoded, - foo_bar - .encode_eip712() - .expect("failed to return domain_separator from Eip712 implemented struct"), - "Encoded value does not match!" - ); - - assert!(verify, "typed data signature failed!"); -} diff --git a/ethers-solc/src/artifact_output/mod.rs b/ethers-solc/src/artifact_output/mod.rs index 17da2b87..5f7b2bc3 100644 --- a/ethers-solc/src/artifact_output/mod.rs +++ b/ethers-solc/src/artifact_output/mod.rs @@ -659,6 +659,7 @@ pub trait ArtifactOutput { if let Ok(stripped) = rel_candidate.strip_prefix(artifacts_folder) { rel_candidate = stripped.to_path_buf(); } + #[allow(clippy::redundant_clone)] // false positive let mut candidate = rel_candidate.clone(); let contract_file = contract_file.as_ref(); let mut current_parent = contract_file.parent(); diff --git a/ethers/Cargo.toml b/ethers/Cargo.toml index 7dff1300..8a5b36ed 100644 --- a/ethers/Cargo.toml +++ b/ethers/Cargo.toml @@ -26,14 +26,7 @@ all-features = true [features] default = ["abigen", "rustls"] -celo = [ - "ethers-core/celo", - "ethers-providers/celo", - "ethers-signers/celo", - "ethers-contract/celo", - "ethers-middleware/celo", - "legacy", -] +celo = ["ethers-core/celo", "ethers-providers/celo", "ethers-signers/celo", "ethers-contract/celo", "ethers-middleware/celo", "legacy"] legacy = ["ethers-core/legacy", "ethers-contract/legacy"] @@ -43,20 +36,8 @@ eip712 = ["ethers-contract/eip712", "ethers-core/eip712"] ## providers ws = ["ethers-providers/ws"] ipc = ["ethers-providers/ipc"] -rustls = [ - "ethers-middleware/rustls", - "ethers-providers/rustls", - "ethers-etherscan/rustls", - "ethers-contract/rustls", - "ethers-solc/rustls", -] -openssl = [ - "ethers-middleware/openssl", - "ethers-providers/openssl", - "ethers-etherscan/openssl", - "ethers-contract/openssl", - "ethers-solc/openssl", -] +rustls = ["ethers-middleware/rustls", "ethers-providers/rustls", "ethers-etherscan/rustls", "ethers-contract/rustls", "ethers-solc/rustls"] +openssl = ["ethers-middleware/openssl", "ethers-providers/openssl", "ethers-etherscan/openssl", "ethers-contract/openssl", "ethers-solc/openssl"] dev-rpc = ["ethers-providers/dev-rpc"] ## signers ledger = ["ethers-signers/ledger"] @@ -67,9 +48,8 @@ abigen = ["ethers-contract/abigen"] ### abigen without reqwest abigen-offline = ["ethers-contract/abigen-offline"] ## solc -ethers-solc = ["dep:ethers-solc", "ethers-etherscan/ethers-solc"] -solc-full = ["ethers-solc?/full"] -solc-tests = ["ethers-solc?/tests"] +solc-full = ["ethers-solc/full"] +solc-tests = ["ethers-solc/tests"] # Deprecated solc-sha2-asm = [] @@ -78,12 +58,14 @@ solc-sha2-asm = [] ethers-addressbook.workspace = true ethers-contract.workspace = true ethers-core.workspace = true -ethers-etherscan.workspace = true +ethers-etherscan = { workspace = true, features = ["ethers-solc"] } ethers-middleware.workspace = true ethers-providers.workspace = true ethers-signers.workspace = true - -ethers-solc = { workspace = true, optional = true } +ethers-solc = { workspace = true } [dev-dependencies] +serde.workspace = true tokio = { workspace = true, features = ["macros", "rt"] } +ethers-contract = { workspace = true, features = ["eip712"] } +ethers-providers = { workspace = true, features = ["rustls"] } # allow https connections diff --git a/ethers/src/lib.rs b/ethers/src/lib.rs index 8cdc5983..3d94e43d 100644 --- a/ethers/src/lib.rs +++ b/ethers/src/lib.rs @@ -98,7 +98,6 @@ pub use ethers_providers as providers; #[doc(inline)] pub use ethers_signers as signers; #[doc(inline)] -#[cfg(feature = "ethers-solc")] pub use ethers_solc as solc; #[doc(inline)] @@ -121,7 +120,6 @@ pub mod prelude { pub use super::signers::*; - #[cfg(feature = "ethers-solc")] pub use super::solc::*; } diff --git a/ethers-contract/tests/solidity-contracts/DeriveEip712Test.sol b/ethers/tests/DeriveEip712Test.sol similarity index 100% rename from ethers-contract/tests/solidity-contracts/DeriveEip712Test.sol rename to ethers/tests/DeriveEip712Test.sol diff --git a/ethers/tests/live/celo.rs b/ethers/tests/celo.rs similarity index 96% rename from ethers/tests/live/celo.rs rename to ethers/tests/celo.rs index 7457e0dd..0931decf 100644 --- a/ethers/tests/live/celo.rs +++ b/ethers/tests/celo.rs @@ -1,7 +1,8 @@ -use crate::simple_storage::SimpleStorage; use ethers::prelude::*; use std::{sync::Arc, time::Duration}; +ethers::contract::abigen!(SimpleStorage, "../testdata/SimpleStorage.json"); + static CELO_TESTNET_URL: &str = "https://alfajores-forno.celo-testnet.org"; #[tokio::test] diff --git a/ethers/tests/eip712.rs b/ethers/tests/eip712.rs new file mode 100644 index 00000000..f23d8295 --- /dev/null +++ b/ethers/tests/eip712.rs @@ -0,0 +1,157 @@ +use ethers::{ + contract::{abigen, ContractFactory, Eip712, EthAbiType}, + core::{ + types::{transaction::eip712::Eip712, Address, Bytes, I256, U256}, + utils::{keccak256, Anvil}, + }, + providers::Provider, + signers::LocalWallet, + solc::Solc, +}; +use std::{path::PathBuf, sync::Arc}; + +#[tokio::test] +async fn test_derive_eip712() { + // Generate Contract ABI Bindings + abigen!( + DeriveEip712Test, + "./ethers-contract/tests/solidity-contracts/derive_eip712_abi.json", + event_derives(serde::Deserialize, serde::Serialize) + ); + + // Create derived structs + + #[derive(Debug, Clone, Eip712, EthAbiType)] + #[eip712( + name = "Eip712Test", + version = "1", + chain_id = 1, + verifying_contract = "0x0000000000000000000000000000000000000001", + salt = "eip712-test-75F0CCte" + )] + struct FooBar { + foo: I256, + bar: U256, + fizz: Bytes, + buzz: [u8; 32], + far: String, + out: Address, + } + + // get ABI and bytecode for the DeriveEip712Test contract + let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests"); + let result = Solc::default().compile_source(path).unwrap(); + let (abi, bytecode, _) = result + .find("DeriveEip712Test") + .expect("failed to get DeriveEip712Test contract") + .into_parts_or_default(); + + // launch the network & connect to it + let anvil = Anvil::new().spawn(); + let from = anvil.addresses()[0]; + let provider = Provider::try_from(anvil.endpoint()) + .unwrap() + .with_sender(from) + .interval(std::time::Duration::from_millis(10)); + let client = Arc::new(provider); + + let factory = ContractFactory::new(abi.clone(), bytecode.clone(), client.clone()); + + let contract = factory + .deploy(()) + .expect("failed to deploy DeriveEip712Test contract") + .legacy() + .send() + .await + .expect("failed to instantiate factory for DeriveEip712 contract"); + + let addr = contract.address(); + + let contract = DeriveEip712Test::new(addr, client.clone()); + + let foo_bar = FooBar { + foo: I256::from(10u64), + bar: U256::from(20u64), + fizz: b"fizz".into(), + buzz: keccak256("buzz"), + far: String::from("space"), + out: Address::from([0; 20]), + }; + + let derived_foo_bar = derive_eip_712_test::FooBar { + foo: foo_bar.foo, + bar: foo_bar.bar, + fizz: foo_bar.fizz.clone(), + buzz: foo_bar.buzz, + far: foo_bar.far.clone(), + out: foo_bar.out, + }; + + use ethers::signers::Signer; + + let wallet: LocalWallet = anvil.keys()[0].clone().into(); + let sig = wallet.sign_typed_data(&foo_bar).await.expect("failed to sign typed data"); + + let r = <[u8; 32]>::try_from(sig.r) + .expect("failed to parse 'r' value from signature into [u8; 32]"); + let s = <[u8; 32]>::try_from(sig.s) + .expect("failed to parse 's' value from signature into [u8; 32]"); + let v = u8::try_from(sig.v).expect("failed to parse 'v' value from signature into u8"); + + let domain_separator = contract + .domain_separator() + .call() + .await + .expect("failed to retrieve domain_separator from contract"); + let type_hash = + contract.type_hash().call().await.expect("failed to retrieve type_hash from contract"); + let struct_hash = contract + .struct_hash(derived_foo_bar.clone()) + .call() + .await + .expect("failed to retrieve struct_hash from contract"); + let encoded = contract + .encode_eip_712(derived_foo_bar.clone()) + .call() + .await + .expect("failed to retrieve eip712 encoded hash from contract"); + let verify = contract + .verify_foo_bar(wallet.address(), derived_foo_bar, r, s, v) + .call() + .await + .expect("failed to verify signed typed data eip712 payload"); + + assert_eq!( + domain_separator, + foo_bar + .domain() + .expect("failed to return domain_separator from Eip712 implemented struct") + .separator(), + "domain separator does not match contract domain separator!" + ); + + assert_eq!( + type_hash, + FooBar::type_hash().expect("failed to return type_hash from Eip712 implemented struct"), + "type hash does not match contract struct type hash!" + ); + + assert_eq!( + struct_hash, + foo_bar + .clone() + .struct_hash() + .expect("failed to return struct_hash from Eip712 implemented struct"), + "struct hash does not match contract struct hash!" + ); + + assert_eq!( + encoded, + foo_bar + .encode_eip712() + .expect("failed to return domain_separator from Eip712 implemented struct"), + "Encoded value does not match!" + ); + + assert!(verify, "typed data signature failed!"); +} diff --git a/ethers/tests/live/main.rs b/ethers/tests/live/main.rs deleted file mode 100644 index a8828646..00000000 --- a/ethers/tests/live/main.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Ethers live tests. -//! -//! If a feature or external binary is added, like Solc, please also update -//! `.github/workflows/ci.yml` at `job.live-test`. - -#![cfg(not(target_arch = "wasm32"))] - -#[cfg(feature = "celo")] -mod celo; - -pub(crate) mod simple_storage { - ethers::contract::abigen!(SimpleStorage, "../testdata/SimpleStorage.json"); -} diff --git a/ethers/tests/main.rs b/ethers/tests/main.rs new file mode 100644 index 00000000..7d10133c --- /dev/null +++ b/ethers/tests/main.rs @@ -0,0 +1,7 @@ +//! Ethers integration tests. +#![cfg(not(target_arch = "wasm32"))] + +#[cfg(feature = "celo")] +mod celo; + +mod eip712; diff --git a/examples/contracts/Cargo.toml b/examples/contracts/Cargo.toml index 3fd9bb5f..8cc0618f 100644 --- a/examples/contracts/Cargo.toml +++ b/examples/contracts/Cargo.toml @@ -13,7 +13,7 @@ default = ["legacy"] legacy = ["ethers/legacy"] [dev-dependencies] -ethers = { workspace = true, features = ["abigen", "ethers-solc", "rustls", "ws"] } +ethers = { workspace = true, features = ["abigen", "rustls", "ws"] } tokio = { workspace = true, features = ["macros"] }