diff --git a/ethers-contract/ethers-contract-abigen/src/contract/methods.rs b/ethers-contract/ethers-contract-abigen/src/contract/methods.rs index a9ca643a..bf782967 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/methods.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/methods.rs @@ -44,57 +44,57 @@ impl Context { /// Returns all deploy (constructor) implementations pub(crate) fn deployment_methods(&self) -> TokenStream { - if self.contract_bytecode.is_some() { - let ethers_core = ethers_core_crate(); - let ethers_contract = ethers_contract_crate(); - - let abi_name = self.inline_abi_ident(); - let get_abi = quote! { - #abi_name.clone() - }; - - let bytecode_name = self.inline_bytecode_ident(); - let get_bytecode = quote! { - #bytecode_name.clone().into() - }; - - let deploy = quote! { - /// Constructs the general purpose `Deployer` instance based on the provided constructor arguments and sends it. - /// Returns a new instance of a deployer that returns an instance of this contract after sending the transaction - /// - /// Notes: - /// 1. If there are no constructor arguments, you should pass `()` as the argument. - /// 1. The default poll duration is 7 seconds. - /// 1. The default number of confirmations is 1 block. - /// - /// - /// # Example - /// - /// Generate contract bindings with `abigen!` and deploy a new contract instance. - /// - /// *Note*: this requires a `bytecode` and `abi` object in the `greeter.json` artifact. - /// - /// ```ignore - /// # async fn deploy(client: ::std::sync::Arc) { - /// abigen!(Greeter,"../greeter.json"); - /// - /// let greeter_contract = Greeter::deploy(client, "Hello world!".to_string()).unwrap().send().await.unwrap(); - /// let msg = greeter_contract.greet().call().await.unwrap(); - /// # } - /// ``` - pub fn deploy(client: ::std::sync::Arc, constructor_args: T) -> Result<#ethers_contract::builders::ContractDeployer, #ethers_contract::ContractError> { - let factory = #ethers_contract::ContractFactory::new(#get_abi, #get_bytecode, client); - let deployer = factory.deploy(constructor_args)?; - let deployer = #ethers_contract::ContractDeployer::new(deployer); - Ok(deployer) - } - - }; - - return deploy + if self.contract_bytecode.is_none() { + // don't generate deploy if no bytecode + return quote! {} } + let ethers_core = ethers_core_crate(); + let ethers_contract = ethers_contract_crate(); - quote! {} + let abi_name = self.inline_abi_ident(); + let get_abi = quote! { + #abi_name.clone() + }; + + let bytecode_name = self.inline_bytecode_ident(); + let get_bytecode = quote! { + #bytecode_name.clone().into() + }; + + let deploy = quote! { + /// Constructs the general purpose `Deployer` instance based on the provided constructor arguments and sends it. + /// Returns a new instance of a deployer that returns an instance of this contract after sending the transaction + /// + /// Notes: + /// 1. If there are no constructor arguments, you should pass `()` as the argument. + /// 1. The default poll duration is 7 seconds. + /// 1. The default number of confirmations is 1 block. + /// + /// + /// # Example + /// + /// Generate contract bindings with `abigen!` and deploy a new contract instance. + /// + /// *Note*: this requires a `bytecode` and `abi` object in the `greeter.json` artifact. + /// + /// ```ignore + /// # async fn deploy(client: ::std::sync::Arc) { + /// abigen!(Greeter,"../greeter.json"); + /// + /// let greeter_contract = Greeter::deploy(client, "Hello world!".to_string()).unwrap().send().await.unwrap(); + /// let msg = greeter_contract.greet().call().await.unwrap(); + /// # } + /// ``` + pub fn deploy(client: ::std::sync::Arc, constructor_args: T) -> Result<#ethers_contract::builders::ContractDeployer, #ethers_contract::ContractError> { + let factory = #ethers_contract::ContractFactory::new(#get_abi, #get_bytecode, client); + let deployer = factory.deploy(constructor_args)?; + let deployer = #ethers_contract::ContractDeployer::new(deployer); + Ok(deployer) + } + + }; + + deploy } /// Expands to the corresponding struct type based on the inputs of the given function diff --git a/ethers-contract/ethers-contract-abigen/src/rawabi.rs b/ethers-contract/ethers-contract-abigen/src/rawabi.rs index 501ecc8a..086bec4c 100644 --- a/ethers-contract/ethers-contract-abigen/src/rawabi.rs +++ b/ethers-contract/ethers-contract-abigen/src/rawabi.rs @@ -168,10 +168,18 @@ impl<'de> Visitor<'de> for AbiObjectVisitor { abi = Some(RawAbi(map.next_value::>()?)); } "bytecode" | "byteCode" => { - bytecode = map.next_value::().ok().map(|obj| obj.object); + bytecode = map + .next_value::() + .ok() + .map(|obj| obj.object) + .filter(|bytecode| !bytecode.0.is_empty()); } "bin" => { - bytecode = map.next_value::().ok().map(|b| b.0); + bytecode = map + .next_value::() + .ok() + .map(|b| b.0) + .filter(|b| !b.0.is_empty()); } _ => { map.next_value::()?; @@ -185,7 +193,7 @@ impl<'de> Visitor<'de> for AbiObjectVisitor { } impl<'de> Deserialize<'de> for AbiObject { - fn deserialize(deserializer: D) -> std::result::Result + fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { @@ -269,4 +277,30 @@ mod tests { let artifact = include_str!("../../tests/solidity-contracts/greeter.json"); assert_has_bytecode(artifact); } + + #[test] + fn ignores_empty_bytecode() { + let abi_str = r#"[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"number","type":"uint64"}],"name":"MyEvent","type":"event"},{"inputs":[],"name":"greet","outputs":[],"stateMutability":"nonpayable","type":"function"}]"#; + let s = format!(r#"{{"abi": {}, "bin" : "0x" }}"#, abi_str); + + match serde_json::from_str::(&s).unwrap() { + JsonAbi::Object(abi) => { + assert!(abi.bytecode.is_none()); + } + _ => { + panic!("expected abi object") + } + } + + let s = format!(r#"{{"abi": {}, "bytecode" : {{ "object": "0x" }} }}"#, abi_str); + + match serde_json::from_str::(&s).unwrap() { + JsonAbi::Object(abi) => { + assert!(abi.bytecode.is_none()); + } + _ => { + panic!("expected abi object") + } + } + } }