Repository: gakonst/ethers-rs
Pull request #2163 (2023-02-17 22:29:34)

Include deployed bytecode in abigen output 
## Motivation

Currently, abigen output includes `bytecode`. However, there are certain use cases when having the `deployedBytecode` is also useful. For example, when using [eth_call state overrides](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-eth), it's useful to inject `deployedBytecode` into an account before executing the call. A common use-case for this is writing a contract with view functions that allow you to batch-read some state onchain in order to reduce the number of RPC calls you make. 

## Solution

Add functionality to include `deployedBytecode` in the abigen output. 

## PR Checklist

-   [x] Added Tests
-   [ ] Added Documentation
-   [ ] Updated the changelog
-   [ ] Breaking changes



Patch:
diff --git a/ethers-contract/ethers-contract-abigen/src/contract.rs b/ethers-contract/ethers-contract-abigen/src/contract.rs
index 99e83c67..051fdb89 100644
--- a/ethers-contract/ethers-contract-abigen/src/contract.rs
+++ b/ethers-contract/ethers-contract-abigen/src/contract.rs
@@ -110,6 +110,9 @@ pub struct Context {
 
     /// Bytecode extracted from the abi string input, if present.
     contract_bytecode: Option<Bytes>,
+
+    /// Deployed bytecode extracted from the abi string input, if present.
+    contract_deployed_bytecode: Option<Bytes>,
 }
 
 impl Context {
@@ -193,6 +196,9 @@ impl Context {
         // holds the bytecode parsed from the abi_str, if present
         let mut contract_bytecode = None;
 
+        // holds the deployed bytecode parsed from the abi_str, if present
+        let mut contract_deployed_bytecode = None;
+
         let (abi, human_readable, abi_parser) = parse_abi(&abi_str).wrap_err_with(|| {
             eyre::eyre!("error parsing abi for contract: {}", args.contract_name)
         })?;
@@ -220,6 +226,7 @@ impl Context {
                     // part of the json object in the contract binding
                     abi_str = serde_json::to_string(&obj.abi)?;
                     contract_bytecode = obj.bytecode;
+                    contract_deployed_bytecode = obj.deployed_bytecode;
                     InternalStructs::new(obj.abi)
                 }
                 JsonAbi::Array(abi) => InternalStructs::new(abi),
@@ -291,6 +298,7 @@ impl Context {
             contract_ident,
             contract_name: args.contract_name,
             contract_bytecode,
+            contract_deployed_bytecode,
             method_aliases,
             error_aliases: Default::default(),
             event_derives,
@@ -313,6 +321,11 @@ impl Context {
         format_ident!("{}_BYTECODE", self.contract_name.to_uppercase())
     }
 
+    /// Name of the `Lazy` that stores the Deployed Bytecode.
+    pub(crate) fn inline_deployed_bytecode_ident(&self) -> Ident {
+        format_ident!("{}_DEPLOYED_BYTECODE", self.contract_name.to_uppercase())
+    }
+
     /// Returns a reference to the internal ABI struct mapping table.
     pub fn internal_structs(&self) -> &InternalStructs {
         &self.internal_structs
diff --git a/ethers-contract/ethers-contract-abigen/src/contract/common.rs b/ethers-contract/ethers-contract-abigen/src/contract/common.rs
index 08178908..ed777d84 100644
--- a/ethers-contract/ethers-contract-abigen/src/contract/common.rs
+++ b/ethers-contract/ethers-contract-abigen/src/contract/common.rs
@@ -72,6 +72,18 @@ pub(crate) fn struct_declaration(cx: &Context) -> TokenStream {
         }
     });
 
+    let deployed_bytecode = cx.contract_deployed_bytecode.as_ref().map(|bytecode| {
+        let bytecode = bytecode.iter().copied().map(Literal::u8_unsuffixed);
+        let bytecode_name = cx.inline_deployed_bytecode_ident();
+        quote! {
+            #[rustfmt::skip]
+            const __DEPLOYED_BYTECODE: &[u8] = &[ #( #bytecode ),* ];
+
+            #[doc = "The deployed bytecode of the contract."]
+            pub static #bytecode_name: #ethers_core::types::Bytes = #ethers_core::types::Bytes::from_static(__DEPLOYED_BYTECODE);
+        }
+    });
+
     quote! {
         // The `Lazy` ABI
         #abi
@@ -79,6 +91,9 @@ pub(crate) fn struct_declaration(cx: &Context) -> TokenStream {
         // The static Bytecode, if present
         #bytecode
 
+         // The static deployed Bytecode, if present
+         #deployed_bytecode
+
         // Struct declaration
         pub struct #name<M>(#ethers_contract::Contract<M>);
 


Test patch:
diff --git a/ethers-contract/tests/it/abigen.rs b/ethers-contract/tests/it/abigen.rs
index 7658a841..4c6093ee 100644
--- a/ethers-contract/tests/it/abigen.rs
+++ b/ethers-contract/tests/it/abigen.rs
@@ -769,3 +769,14 @@ fn convert_uses_correct_abi() {
     // is incorrectly using the `Foo` ABI internally).
     bar.bar().call();
 }
+
+#[test]
+fn generates_non_zero_bytecode() {
+    abigen!(Greeter, "ethers-contract/tests/solidity-contracts/greeter_with_struct.json");
+    //check that the bytecode is not empty
+    assert!(GREETER_BYTECODE.len() > 0);
+    assert!(GREETER_DEPLOYED_BYTECODE.len() > 0);
+    //sanity check that the bytecode is not the same
+    assert_ne!(GREETER_BYTECODE, GREETER_DEPLOYED_BYTECODE);
+
+}