diff --git a/Cargo.lock b/Cargo.lock index 5c3d439e..774c4b35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -681,6 +681,7 @@ dependencies = [ "ethabi-next", "ethereum-types", "ethers", + "funty", "generic-array", "glob", "hex", diff --git a/ethers-contract/ethers-contract-abigen/src/contract.rs b/ethers-contract/ethers-contract-abigen/src/contract.rs index 00a851c4..4e1c5301 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract.rs @@ -107,7 +107,7 @@ impl Context { // heuristic for parsing the human readable format // replace bad chars - let abi_str = abi_str.replace('[', "").replace(']', "").replace(',', ""); + let abi_str = abi_str.replace('[', "").replace(']', ""); // split lines and get only the non-empty things let split: Vec<&str> = abi_str .split('\n') diff --git a/ethers-contract/ethers-contract-abigen/src/contract/common.rs b/ethers-contract/ethers-contract-abigen/src/contract/common.rs index 38633fdc..a67954a1 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/common.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/common.rs @@ -36,7 +36,7 @@ pub(crate) fn struct_declaration(cx: &Context, abi_name: &proc_macro2::Ident) -> } else { quote! { pub static #abi_name: Lazy = Lazy::new(|| { - let abi_str = #abi.replace('[', "").replace(']', "").replace(',', ""); + let abi_str = #abi.replace('[', "").replace(']', ""); // split lines and get only the non-empty things let split: Vec<&str> = abi_str .split("\n") diff --git a/ethers-contract/ethers-contract-abigen/src/contract/events.rs b/ethers-contract/ethers-contract-abigen/src/contract/events.rs index f1ab0fe7..7c28f19e 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/events.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/events.rs @@ -30,7 +30,7 @@ impl Context { .abi .events() .map(|event| expand_filter(event)) - .collect::>>()?; + .collect::>(); if data_types.is_empty() { return Ok(quote! {}); @@ -43,7 +43,7 @@ impl Context { } /// Expands into a single method for contracting an event stream. -fn expand_filter(event: &Event) -> Result { +fn expand_filter(event: &Event) -> TokenStream { // append `filter` to disambiguate with potentially conflicting // function names let name = util::safe_ident(&format!("{}_filter", event.name.to_snake_case())); @@ -53,13 +53,13 @@ fn expand_filter(event: &Event) -> Result { let ev_name = Literal::string(&event.name); let doc = util::expand_doc(&format!("Gets the contract's `{}` event", event.name)); - Ok(quote! { + quote! { #doc pub fn #name(&self) -> Event { self.0.event(#ev_name).expect("event not found (this should never happen)") } - }) + } } /// Expands an ABI event into a single event data type. This can expand either @@ -317,7 +317,7 @@ mod tests { anonymous: false, }; - assert_quote!(expand_filter(&event).unwrap(), { + assert_quote!(expand_filter(&event), { #[doc = "Gets the contract's `Transfer` event"] pub fn transfer_filter(&self) -> Event { self.0 diff --git a/ethers-core/Cargo.toml b/ethers-core/Cargo.toml index 8984ff74..386ccb1f 100644 --- a/ethers-core/Cargo.toml +++ b/ethers-core/Cargo.toml @@ -32,12 +32,17 @@ glob = { version = "0.3.0", default-features = false } bytes = { version = "1.0.1", features = ["serde"] } hex = { version = "0.4.2", default-features = false, features = ["std"] } +# bitvec compilation issue +# https://github.com/bitvecto-rs/bitvec/issues/105#issuecomment-778570981 +funty = "=1.1.0" + + [dev-dependencies] ethers = { version = "0.2", path = "../ethers" } serde_json = { version = "1.0.62", default-features = false } bincode = { version = "1.2.1", default-features = false } -once_cell = { version = "1.5.2", default-features = false } +once_cell = { version = "1.5.2" } [features] diff --git a/ethers-middleware/src/signer.rs b/ethers-middleware/src/signer.rs index 1249f99c..e8e7a7f5 100644 --- a/ethers-middleware/src/signer.rs +++ b/ethers-middleware/src/signer.rs @@ -248,15 +248,13 @@ where mut tx: TransactionRequest, block: Option, ) -> Result, Self::Error> { - if let Some(ref to) = tx.to { - if let NameOrAddress::Name(ens_name) = to { - let addr = self - .inner - .resolve_name(&ens_name) - .await - .map_err(SignerMiddlewareError::MiddlewareError)?; - tx.to = Some(addr.into()) - } + if let Some(NameOrAddress::Name(ens_name)) = tx.to { + let addr = self + .inner + .resolve_name(&ens_name) + .await + .map_err(SignerMiddlewareError::MiddlewareError)?; + tx.to = Some(addr.into()) } // fill any missing fields diff --git a/ethers-middleware/src/transformer/middleware.rs b/ethers-middleware/src/transformer/middleware.rs index 0a7fc994..09d49987 100644 --- a/ethers-middleware/src/transformer/middleware.rs +++ b/ethers-middleware/src/transformer/middleware.rs @@ -59,15 +59,13 @@ where block: Option, ) -> Result, Self::Error> { // resolve the to field if that's an ENS name. - if let Some(ref to) = tx.to { - if let NameOrAddress::Name(ens_name) = to { - let addr = self - .inner - .resolve_name(&ens_name) - .await - .map_err(TransformerMiddlewareError::MiddlewareError)?; - tx.to = Some(addr.into()) - } + if let Some(NameOrAddress::Name(ens_name)) = tx.to { + let addr = self + .inner + .resolve_name(&ens_name) + .await + .map_err(TransformerMiddlewareError::MiddlewareError)?; + tx.to = Some(addr.into()) } // construct the appropriate proxy tx. diff --git a/ethers-providers/src/pending_transaction.rs b/ethers-providers/src/pending_transaction.rs index b5b292db..5c6c6096 100644 --- a/ethers-providers/src/pending_transaction.rs +++ b/ethers-providers/src/pending_transaction.rs @@ -73,12 +73,8 @@ impl<'a, P: JsonRpcClient> Future for PendingTransaction<'a, P> { ctx.waker().wake_by_ref(); } PendingTxState::GettingReceipt(fut) => { - if let Ok(receipt) = futures_util::ready!(fut.as_mut().poll(ctx)) { - if let Some(receipt) = receipt { - *this.state = PendingTxState::CheckingReceipt(Box::new(receipt)) - } else { - *this.state = PendingTxState::PausedGettingReceipt - } + if let Ok(Some(receipt)) = futures_util::ready!(fut.as_mut().poll(ctx)) { + *this.state = PendingTxState::CheckingReceipt(Box::new(receipt)) } else { *this.state = PendingTxState::PausedGettingReceipt } diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs index 1e070d35..edd5ad4f 100644 --- a/ethers-providers/src/provider.rs +++ b/ethers-providers/src/provider.rs @@ -293,14 +293,12 @@ impl Middleware for Provider

{ tx.gas = Some(self.estimate_gas(&tx).await?); } - if let Some(ref to) = tx.to { - if let NameOrAddress::Name(ens_name) = to { - // resolve to an address - let addr = self.resolve_name(&ens_name).await?; + if let Some(NameOrAddress::Name(ref ens_name)) = tx.to { + // resolve to an address + let addr = self.resolve_name(&ens_name).await?; - // set the value - tx.to = Some(addr.into()) - } + // set the value + tx.to = Some(addr.into()) } let tx_hash = self.request("eth_sendTransaction", [tx]).await?; diff --git a/ethers/examples/contract_abi.json b/ethers/examples/contract_abi.json new file mode 100644 index 00000000..12de10c8 --- /dev/null +++ b/ethers/examples/contract_abi.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"string","name":"value","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"author","type":"address"},{"indexed":false,"internalType":"string","name":"oldValue","type":"string"},{"indexed":false,"internalType":"string","name":"newValue","type":"string"}],"name":"ValueChanged","type":"event"},{"inputs":[],"name":"getValue","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"}],"name":"setValue","outputs":[],"stateMutability":"nonpayable","type":"function"}] diff --git a/ethers/examples/contract.rs b/ethers/examples/contract_human_readable.rs similarity index 92% rename from ethers/examples/contract.rs rename to ethers/examples/contract_human_readable.rs index 918fdc97..3b6b4ca3 100644 --- a/ethers/examples/contract.rs +++ b/ethers/examples/contract_human_readable.rs @@ -12,7 +12,7 @@ abigen!( r#"[ function setValue(string) function getValue() external view (string) - event ValueChanged(address indexed author, address indexed oldAuthor, string oldValue, string newValue) + event ValueChanged(address indexed author, string oldValue, string newValue) ]"#, event_derives(serde::Deserialize, serde::Serialize) ); @@ -20,7 +20,7 @@ abigen!( #[tokio::main] async fn main() -> Result<()> { // 1. compile the contract (note this requires that you are inside the `ethers/examples` directory) - let compiled = Solc::new("./contract.sol").build()?; + let compiled = Solc::new("**/contract.sol").build()?; let contract = compiled .get("SimpleStorage") .expect("could not find contract"); diff --git a/ethers/examples/contract_with_abi.rs b/ethers/examples/contract_with_abi.rs new file mode 100644 index 00000000..fa07aaa8 --- /dev/null +++ b/ethers/examples/contract_with_abi.rs @@ -0,0 +1,72 @@ +use anyhow::Result; +use ethers::{ + prelude::*, + utils::{Ganache, Solc}, +}; +use std::{convert::TryFrom, sync::Arc, time::Duration}; + +// Generate the type-safe contract bindings by providing the ABI +// definition in human readable format +abigen!( + SimpleContract, + "./ethers/examples/contract_abi.json", + event_derives(serde::Deserialize, serde::Serialize) +); + +#[tokio::main] +async fn main() -> Result<()> { + // 1. compile the contract (note this requires that you are inside the `ethers/examples` directory) + let compiled = Solc::new("**/contract.sol").build()?; + let contract = compiled + .get("SimpleStorage") + .expect("could not find contract"); + dbg!("OK"); + + // 2. launch ganache + let ganache = Ganache::new().spawn(); + + // 3. instantiate our wallet + let wallet: LocalWallet = ganache.keys()[0].clone().into(); + + // 4. connect to the network + let provider = + Provider::::try_from(ganache.endpoint())?.interval(Duration::from_millis(10u64)); + + // 5. instantiate the client with the wallet + let client = SignerMiddleware::new(provider, wallet); + let client = Arc::new(client); + + // 6. create a factory which will be used to deploy instances of the contract + let factory = ContractFactory::new( + contract.abi.clone(), + contract.bytecode.clone(), + client.clone(), + ); + + // 7. deploy it with the constructor arguments + let contract = factory.deploy("initial value".to_string())?.send().await?; + + // 8. get the contract's address + let addr = contract.address(); + + // 9. instantiate the contract + let contract = SimpleContract::new(addr, client.clone()); + + // 10. call the `setValue` method + // (first `await` returns a PendingTransaction, second one waits for it to be mined) + let _receipt = contract.set_value("hi".to_owned()).send().await?.await?; + + // 11. get all events + let logs = contract + .value_changed_filter() + .from_block(0u64) + .query() + .await?; + + // 12. get the new value + let value = contract.get_value().call().await?; + + println!("Value: {}. Logs: {}", value, serde_json::to_string(&logs)?); + + Ok(()) +}