Repository: gakonst/ethers-rs (rust)

Issue:
ethers-solc: support previous hardhat builds
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
* ethers-solc and hardhat use different artifact layout but same cache format, so if a user has built with hardhat and then again with forge, we fail to decode the artifact because the cache tells us nothing has changed

**Describe the solution you'd like**
A clear and concise description of what you want to happen.
I believe we can simply change our format identifier and check if it's the hardhat one or ethers-solc one

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.
https://github.com/gakonst/foundry/issues/209

Pull request:
feat(solc): add hardhat artifact support
<!--
Thank you for your Pull Request. Please provide a description above and review
the requirements below.

Bug fixes and new features should include tests.

Contributors guide: https://github.com/gakonst/ethers-rs/blob/master/CONTRIBUTING.md

The contributors guide includes instructions for running rustfmt and building the
documentation.
-->

## Motivation
Add support for hardhat style artifacts
Closes #676
<!--
Explain the context and why you're making that change. What is the problem
you're trying to solve? In some cases there is not a problem and this can be
thought of as being the motivation for your change.
-->

## Solution
* Add artifact bindings + handler
* Add additional Artifacthandler `MinimalCombinedArtifactsHardhatFallback` that tries to deserialize hardhat artifacts if the previous attempt failed. Alternatively this behaviour could be moved to a bool flag in the existing `MinimalCombinedArtifacts` handler and enabled by default?
<!--
Summarize the solution and provide any necessary context needed to understand
the code change.
-->

## PR Checklist

- [x] Added Tests
- [x] Added Documentation
- [x] Updated the changelog


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1c5cfa39..1099c294 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@
 
 ### Unreleased
 
+- Add support for hardhat artifacts [#677](https://github.com/gakonst/ethers-rs/pull/677)
 - Add more utility functions to the `Artifact` trait [#673](https://github.com/gakonst/ethers-rs/pull/673)
 - Return cached artifacts from project `compile` when the cache only contains
   some files
diff --git a/ethers-solc/src/artifacts.rs b/ethers-solc/src/artifacts.rs
index b55be734..b0fbeceb 100644
--- a/ethers-solc/src/artifacts.rs
+++ b/ethers-solc/src/artifacts.rs
@@ -940,6 +940,13 @@ impl BytecodeObject {
     }
 }
 
+// Returns a not deployable bytecode by default as "0x"
+impl Default for BytecodeObject {
+    fn default() -> Self {
+        BytecodeObject::Unlinked("0x".to_string())
+    }
+}
+
 impl AsRef<[u8]> for BytecodeObject {
     fn as_ref(&self) -> &[u8] {
         match self {
diff --git a/ethers-solc/src/cache.rs b/ethers-solc/src/cache.rs
index 4a5957e6..31cc7578 100644
--- a/ethers-solc/src/cache.rs
+++ b/ethers-solc/src/cache.rs
@@ -16,6 +16,13 @@ use std::{
 /// Hardhat format version
 const HH_FORMAT_VERSION: &str = "hh-sol-cache-2";
 
+/// ethers-rs format version
+///
+/// `ethers-solc` uses a different format version id, but the actual format is consistent with
+/// hardhat This allows ethers-solc to detect if the cache file was written by hardhat or
+/// `ethers-solc`
+const ETHERS_FORMAT_VERSION: &str = "ethers-rs-sol-cache-1";
+
 /// The file name of the default cache file
 pub const SOLIDITY_FILES_CACHE_FILENAME: &str = "solidity-files-cache.json";
 
@@ -42,6 +49,16 @@ impl SolFilesCache {
         SolFilesCacheBuilder::default()
     }
 
+    /// Whether this cache's format is the hardhat format identifier
+    pub fn is_hardhat_format(&self) -> bool {
+        self.format == HH_FORMAT_VERSION
+    }
+
+    /// Whether this cache's format is our custom format identifier
+    pub fn is_ethers_format(&self) -> bool {
+        self.format == ETHERS_FORMAT_VERSION
+    }
+
     /// Reads the cache json file from the given path
     #[tracing::instrument(skip_all, name = "sol-files-cache::read")]
     pub fn read(path: impl AsRef<Path>) -> Result<Self> {
@@ -161,7 +178,7 @@ impl SolFilesCache {
         })
     }
 
-    /// Reads all cached artifacts from disk
+    /// Reads all cached artifacts from disk using the given ArtifactOutput handler
     pub fn read_artifacts<T: ArtifactOutput>(
         &self,
         artifacts_root: &Path,
@@ -215,7 +232,7 @@ impl SolFilesCacheBuilder {
     }
 
     pub fn insert_files(self, sources: Sources, dest: Option<PathBuf>) -> Result<SolFilesCache> {
-        let format = self.format.unwrap_or_else(|| HH_FORMAT_VERSION.to_string());
+        let format = self.format.unwrap_or_else(|| ETHERS_FORMAT_VERSION.to_string());
         let solc_config =
             self.solc_config.map(Ok).unwrap_or_else(|| SolcConfig::builder().build())?;
 
diff --git a/ethers-solc/src/config.rs b/ethers-solc/src/config.rs
index 69feb75c..dd76774b 100644
--- a/ethers-solc/src/config.rs
+++ b/ethers-solc/src/config.rs
@@ -1,12 +1,13 @@
 use crate::{
     artifacts::{CompactContract, CompactContractRef, Contract, Settings},
     cache::SOLIDITY_FILES_CACHE_FILENAME,
-    error::Result,
+    error::{Result, SolcError},
+    hh::HardhatArtifact,
     remappings::Remapping,
     CompilerOutput,
 };
 use ethers_core::{abi::Abi, types::Bytes};
-use serde::{Deserialize, Serialize};
+use serde::{de::DeserializeOwned, Deserialize, Serialize};
 use std::{
     collections::BTreeMap,
     convert::TryFrom,
@@ -225,37 +226,27 @@ pub trait Artifact {
     fn into_compact_contract(self) -> CompactContract;
 
     /// Returns the contents of this type as a single tuple of abi, bytecode and deployed bytecode
-    fn into_parts(self) -> (Option<Abi>, Option<Bytes>, Option<Bytes>)
-    where
-        Self: Sized,
-    {
-        self.into_compact_contract().into_parts()
-    }
+    fn into_parts(self) -> (Option<Abi>, Option<Bytes>, Option<Bytes>);
 }
 
-impl Artifact for CompactContract {
+impl<T: Into<CompactContract>> Artifact for T {
     fn into_inner(self) -> (Option<Abi>, Option<Bytes>) {
-        (self.abi, self.bin.and_then(|bin| bin.into_bytes()))
+        let artifact = self.into_compact_contract();
+        (artifact.abi, artifact.bin.and_then(|bin| bin.into_bytes()))
     }
 
     fn into_compact_contract(self) -> CompactContract {
-        self
-    }
-}
-
-impl Artifact for serde_json::Value {
-    fn into_inner(self) -> (Option<Abi>, Option<Bytes>) {
-        self.into_compact_contract().into_inner()
+        self.into()
     }
 
-    fn into_compact_contract(self) -> CompactContract {
-        self.into()
+    fn into_parts(self) -> (Option<Abi>, Option<Bytes>, Option<Bytes>) {
+        self.into_compact_contract().into_parts()
     }
 }
 
 pub trait ArtifactOutput {
     /// How Artifacts are stored
-    type Artifact: Artifact;
+    type Artifact: Artifact + DeserializeOwned;
 
     /// Handle the compiler output.
     fn on_output(output: &CompilerOutput, layout: &ProjectPathsConfig) -> Result<()>;
@@ -295,7 +286,11 @@ pub trait ArtifactOutput {
         root.as_ref().join(Self::output_file(contract_file, name)).exists()
     }
 
-    fn read_cached_artifact(path: impl AsRef<Path>) -> Result<Self::Artifact>;
+    fn read_cached_artifact(path: impl AsRef<Path>) -> Result<Self::Artifact> {
+        let file = fs::File::open(path.as_ref())?;
+        let file = io::BufReader::new(file);
+        Ok(serde_json::from_reader(file)?)
+    }
 
     /// Read the cached artifacts from disk
     fn read_cached_artifacts<T, I>(files: I) -> Result<BTreeMap<PathBuf, Self::Artifact>>
@@ -313,21 +308,22 @@ pub trait ArtifactOutput {
     }
 
     /// Convert a contract to the artifact type
-    fn contract_to_artifact(contract: Contract) -> Self::Artifact;
+    fn contract_to_artifact(file: &str, name: &str, contract: Contract) -> Self::Artifact;
 
     /// Convert the compiler output into a set of artifacts
     fn output_to_artifacts(output: CompilerOutput) -> Artifacts<Self::Artifact> {
         output
             .contracts
             .into_iter()
-            .map(|(s, contracts)| {
-                (
-                    s,
-                    contracts
-                        .into_iter()
-                        .map(|(s, c)| (s, Self::contract_to_artifact(c)))
-                        .collect(),
-                )
+            .map(|(file, contracts)| {
+                let contracts = contracts
+                    .into_iter()
+                    .map(|(name, c)| {
+                        let contract = Self::contract_to_artifact(&file, &name, c);
+                        (name, contract)
+                    })
+                    .collect();
+                (file, contracts)
             })
             .collect()
     }
@@ -350,49 +346,60 @@ impl ArtifactOutput for MinimalCombinedArtifacts {
     type Artifact = CompactContract;
 
     fn on_output(output: &CompilerOutput, layout: &ProjectPathsConfig) -> Result<()> {
-        fs::create_dir_all(&layout.artifacts)?;
+        fs::create_dir_all(&layout.artifacts)
+            .map_err(|err| SolcError::msg(format!("Failed to create artifacts dir: {}", err)))?;
         for (file, contracts) in output.contracts.iter() {
             for (name, contract) in contracts {
                 let artifact = Self::output_file(file, name);
                 let file = layout.artifacts.join(artifact);
                 if let Some(parent) = file.parent() {
-                    fs::create_dir_all(parent)?;
+                    fs::create_dir_all(parent).map_err(|err| {
+                        SolcError::msg(format!(
+                            "Failed to create artifact parent folder \"{}\": {}",
+                            parent.display(),
+                            err
+                        ))
+                    })?;
                 }
                 let min = CompactContractRef::from(contract);
-                fs::write(file, serde_json::to_vec_pretty(&min)?)?
+                fs::write(&file, serde_json::to_vec_pretty(&min)?)?
             }
         }
         Ok(())
     }
 
-    fn read_cached_artifact(path: impl AsRef<Path>) -> Result<Self::Artifact> {
-        let file = fs::File::open(path.as_ref())?;
-        let file = io::BufReader::new(file);
-        Ok(serde_json::from_reader(file)?)
-    }
-
-    fn contract_to_artifact(contract: Contract) -> Self::Artifact {
-        CompactContract::from(contract)
+    fn contract_to_artifact(_file: &str, _name: &str, contract: Contract) -> Self::Artifact {
+        Self::Artifact::from(contract)
     }
 }
 
-/// Hardhat style artifacts
+/// An Artifacts handler implementation that works the same as `MinimalCombinedArtifacts` but also
+/// supports reading hardhat artifacts if an initial attempt to deserialize an artifact failed
 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub struct HardhatArtifacts;
+pub struct MinimalCombinedArtifactsHardhatFallback;
 
-impl ArtifactOutput for HardhatArtifacts {
-    type Artifact = serde_json::Value;
+impl ArtifactOutput for MinimalCombinedArtifactsHardhatFallback {
+    type Artifact = CompactContract;
 
-    fn on_output(_output: &CompilerOutput, _layout: &ProjectPathsConfig) -> Result<()> {
-        todo!("Hardhat style artifacts not yet implemented")
+    fn on_output(output: &CompilerOutput, layout: &ProjectPathsConfig) -> Result<()> {
+        MinimalCombinedArtifacts::on_output(output, layout)
     }
 
-    fn read_cached_artifact(_path: impl AsRef<Path>) -> Result<Self::Artifact> {
-        todo!("Hardhat style artifacts not yet implemented")
+    fn read_cached_artifact(path: impl AsRef<Path>) -> Result<Self::Artifact> {
+        let content = fs::read_to_string(path)?;
+        if let Ok(a) = serde_json::from_str(&content) {
+            Ok(a)
+        } else {
+            tracing::error!("Failed to deserialize compact artifact");
+            tracing::trace!("Fallback to hardhat artifact deserialization");
+            let artifact = serde_json::from_str::<HardhatArtifact>(&content)?;
+            tracing::trace!("successfully deserialized hardhat artifact");
+            Ok(artifact.into_compact_contract())
+        }
     }
 
-    fn contract_to_artifact(_contract: Contract) -> Self::Artifact {
-        todo!("Hardhat style artifacts not yet implemented")
+    fn contract_to_artifact(file: &str, name: &str, contract: Contract) -> Self::Artifact {
+        MinimalCombinedArtifacts::contract_to_artifact(file, name, contract)
     }
 }
 
diff --git a/ethers-solc/src/error.rs b/ethers-solc/src/error.rs
index 287c10a7..7fa86a5b 100644
--- a/ethers-solc/src/error.rs
+++ b/ethers-solc/src/error.rs
@@ -29,10 +29,16 @@ pub enum SolcError {
     NoContracts(String),
     #[error(transparent)]
     PatternError(#[from] glob::PatternError),
+    /// General purpose message
+    #[error("{0}")]
+    Message(String),
 }
 
 impl SolcError {
     pub(crate) fn solc(msg: impl Into<String>) -> Self {
         SolcError::SolcError(msg.into())
     }
+    pub(crate) fn msg(msg: impl Into<String>) -> Self {
+        SolcError::Message(msg.into())
+    }
 }
diff --git a/ethers-solc/src/hh.rs b/ethers-solc/src/hh.rs
new file mode 100644
index 00000000..bcb3b41b
--- /dev/null
+++ b/ethers-solc/src/hh.rs
@@ -0,0 +1,129 @@
+//! Hardhat support
+
+use crate::{
+    artifacts::{BytecodeObject, CompactContract, Contract, Offsets},
+    error::{Result, SolcError},
+    ArtifactOutput, CompilerOutput, ProjectPathsConfig,
+};
+use ethers_core::abi::Abi;
+use serde::{Deserialize, Serialize};
+use std::{collections::BTreeMap, fs};
+
+const HH_ARTIFACT_VERSION: &str = "hh-sol-artifact-1";
+
+/// A hardhat artifact
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct HardhatArtifact {
+    #[serde(rename = "_format")]
+    pub format: String,
+    /// A string with the contract's name.
+    pub contract_name: String,
+    /// The source name of this contract in the workspace like `contracts/Greeter.sol`
+    pub source_name: String,
+    /// The contract's ABI
+    pub abi: Abi,
+    /// A "0x"-prefixed hex string of the unlinked deployment bytecode. If the contract is not
+    /// deployable, this has the string "0x"
+    pub bytecode: BytecodeObject,
+    /// A "0x"-prefixed hex string of the unlinked runtime/deployed bytecode. If the contract is
+    /// not deployable, this has the string "0x"
+    pub deployed_bytecode: Option<BytecodeObject>,
+    /// The bytecode's link references object as returned by solc. If the contract doesn't need to
+    /// be linked, this value contains an empty object.
+    #[serde(default)]
+    pub link_references: BTreeMap<String, BTreeMap<String, Vec<Offsets>>>,
+    /// The deployed bytecode's link references object as returned by solc. If the contract doesn't
+    /// need to be linked, this value contains an empty object.
+    #[serde(default)]
+    pub deployed_link_references: BTreeMap<String, BTreeMap<String, Vec<Offsets>>>,
+}
+
+impl From<HardhatArtifact> for CompactContract {
+    fn from(artifact: HardhatArtifact) -> Self {
+        CompactContract {
+            abi: Some(artifact.abi),
+            bin: Some(artifact.bytecode),
+            bin_runtime: artifact.deployed_bytecode,
+        }
+    }
+}
+
+/// Hardhat style artifacts handler
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct HardhatArtifacts;
+
+impl ArtifactOutput for HardhatArtifacts {
+    type Artifact = HardhatArtifact;
+
+    fn on_output(output: &CompilerOutput, layout: &ProjectPathsConfig) -> Result<()> {
+        fs::create_dir_all(&layout.artifacts)
+            .map_err(|err| SolcError::msg(format!("Failed to create artifacts dir: {}", err)))?;
+        for (file, contracts) in output.contracts.iter() {
+            for (name, contract) in contracts {
+                let artifact = Self::output_file(file, name);
+                let artifact_file = layout.artifacts.join(artifact);
+                if let Some(parent) = artifact_file.parent() {
+                    fs::create_dir_all(parent).map_err(|err| {
+                        SolcError::msg(format!(
+                            "Failed to create artifact parent folder \"{}\": {}",
+                            parent.display(),
+                            err
+                        ))
+                    })?;
+                }
+                let artifact = Self::contract_to_artifact(file, name, contract.clone());
+                fs::write(&artifact_file, serde_json::to_vec_pretty(&artifact)?)?
+            }
+        }
+        Ok(())
+    }
+
+    fn contract_to_artifact(file: &str, name: &str, contract: Contract) -> Self::Artifact {
+        let (bytecode, link_references, deployed_bytecode, deployed_link_references) =
+            if let Some(evm) = contract.evm {
+                let (deployed_bytecode, deployed_link_references) =
+                    if let Some(code) = evm.deployed_bytecode.and_then(|code| code.bytecode) {
+                        (Some(code.object), code.link_references)
+                    } else {
+                        (None, Default::default())
+                    };
+
+                (
+                    evm.bytecode.object,
+                    evm.bytecode.link_references,
+                    deployed_bytecode,
+                    deployed_link_references,
+                )
+            } else {
+                (Default::default(), Default::default(), None, Default::default())
+            };
+
+        HardhatArtifact {
+            format: HH_ARTIFACT_VERSION.to_string(),
+            contract_name: name.to_string(),
+            source_name: file.to_string(),
+            abi: contract.abi.unwrap_or_default(),
+            bytecode,
+            deployed_bytecode,
+            link_references,
+            deployed_link_references,
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::Artifact;
+
+    #[test]
+    fn can_parse_hh_artifact() {
+        let s = include_str!("../test-data/hh-greeter-artifact.json");
+        let artifact = serde_json::from_str::<HardhatArtifact>(s).unwrap();
+        let compact = artifact.into_compact_contract();
+        assert!(compact.abi.is_some());
+        assert!(compact.bin.is_some());
+        assert!(compact.bin_runtime.is_some());
+    }
+}
diff --git a/ethers-solc/src/lib.rs b/ethers-solc/src/lib.rs
index 040c69d4..5ee6af31 100644
--- a/ethers-solc/src/lib.rs
+++ b/ethers-solc/src/lib.rs
@@ -6,6 +6,7 @@ pub use artifacts::{CompilerInput, CompilerOutput, EvmVersion};
 use std::collections::btree_map::Entry;
 
 pub mod cache;
+pub mod hh;
 
 mod compile;
 
@@ -763,14 +764,12 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
     }
 
     /// Finds the first contract with the given name and removes it from the set
-    pub fn remove(&mut self, contract: impl AsRef<str>) -> Option<T::Artifact> {
-        let contract = contract.as_ref();
+    pub fn remove(&mut self, contract_name: impl AsRef<str>) -> Option<T::Artifact> {
+        let contract_name = contract_name.as_ref();
         if let Some(output) = self.compiler_output.as_mut() {
-            if let contract @ Some(_) = output
-                .contracts
-                .values_mut()
-                .find_map(|c| c.remove(contract).map(T::contract_to_artifact))
-            {
+            if let contract @ Some(_) = output.contracts.iter_mut().find_map(|(file, c)| {
+                c.remove(contract_name).map(|c| T::contract_to_artifact(file, contract_name, c))
+            }) {
                 return contract
             }
         }
@@ -778,7 +777,7 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
             .artifacts
             .iter()
             .find_map(|(path, _)| {
-                T::contract_name(path).filter(|name| name == contract).map(|_| path)
+                T::contract_name(path).filter(|name| name == contract_name).map(|_| path)
             })?
             .clone();
         self.artifacts.remove(&key)
@@ -790,17 +789,20 @@ where
     T::Artifact: Clone,
 {
     /// Finds the first contract with the given name
-    pub fn find(&self, contract: impl AsRef<str>) -> Option<Cow<T::Artifact>> {
-        let contract = contract.as_ref();
+    pub fn find(&self, contract_name: impl AsRef<str>) -> Option<Cow<T::Artifact>> {
+        let contract_name = contract_name.as_ref();
         if let Some(output) = self.compiler_output.as_ref() {
-            if let contract @ Some(_) = output.contracts.values().find_map(|c| {
-                c.get(contract).map(|c| T::contract_to_artifact(c.clone())).map(Cow::Owned)
+            if let contract @ Some(_) = output.contracts.iter().find_map(|(file, contracts)| {
+                contracts
+                    .get(contract_name)
+                    .map(|c| T::contract_to_artifact(file, contract_name, c.clone()))
+                    .map(Cow::Owned)
             }) {
                 return contract
             }
         }
         self.artifacts.iter().find_map(|(path, art)| {
-            T::contract_name(path).filter(|name| name == contract).map(|_| Cow::Borrowed(art))
+            T::contract_name(path).filter(|name| name == contract_name).map(|_| Cow::Borrowed(art))
         })
     }
 }


Test patch:
diff --git a/ethers-solc/test-data/hh-greeter-artifact.json b/ethers-solc/test-data/hh-greeter-artifact.json
new file mode 100644
index 00000000..f182f4ee
--- /dev/null
+++ b/ethers-solc/test-data/hh-greeter-artifact.json
@@ -0,0 +1,48 @@
+{
+  "_format": "hh-sol-artifact-1",
+  "contractName": "Greeter",
+  "sourceName": "contracts/Greeter.sol",
+  "abi": [
+    {
+      "inputs": [
+        {
+          "internalType": "string",
+          "name": "_greeting",
+          "type": "string"
+        }
+      ],
+      "stateMutability": "nonpayable",
+      "type": "constructor"
+    },
+    {
+      "inputs": [],
+      "name": "greet",
+      "outputs": [
+        {
+          "internalType": "string",
+          "name": "",
+          "type": "string"
+        }
+      ],
+      "stateMutability": "view",
+      "type": "function"
+    },
+    {
+      "inputs": [
+        {
+          "internalType": "string",
+          "name": "_greeting",
+          "type": "string"
+        }
+      ],
+      "name": "setGreeting",
+      "outputs": [],
+      "stateMutability": "nonpayable",
+      "type": "function"
+    }
+  ],
+  "bytecode": "0x60806040523480156200001157600080fd5b5060405162000c3238038062000c32833981810160405281019062000037919062000278565b6200006760405180606001604052806022815260200162000c1060229139826200008760201b620001ce1760201c565b80600090805190602001906200007f92919062000156565b5050620004c5565b620001298282604051602401620000a0929190620002fe565b6040516020818303038152906040527f4b5c4277000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506200012d60201b60201c565b5050565b60008151905060006a636f6e736f6c652e6c6f679050602083016000808483855afa5050505050565b8280546200016490620003ea565b90600052602060002090601f016020900481019282620001885760008555620001d4565b82601f10620001a357805160ff1916838001178555620001d4565b82800160010185558215620001d4579182015b82811115620001d3578251825591602001919060010190620001b6565b5b509050620001e39190620001e7565b5090565b5b8082111562000202576000816000905550600101620001e8565b5090565b60006200021d620002178462000362565b62000339565b9050828152602081018484840111156200023657600080fd5b62000243848285620003b4565b509392505050565b600082601f8301126200025d57600080fd5b81516200026f84826020860162000206565b91505092915050565b6000602082840312156200028b57600080fd5b600082015167ffffffffffffffff811115620002a657600080fd5b620002b4848285016200024b565b91505092915050565b6000620002ca8262000398565b620002d68185620003a3565b9350620002e8818560208601620003b4565b620002f381620004b4565b840191505092915050565b600060408201905081810360008301526200031a8185620002bd565b90508181036020830152620003308184620002bd565b90509392505050565b60006200034562000358565b905062000353828262000420565b919050565b6000604051905090565b600067ffffffffffffffff82111562000380576200037f62000485565b5b6200038b82620004b4565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b60005b83811015620003d4578082015181840152602081019050620003b7565b83811115620003e4576000848401525b50505050565b600060028204905060018216806200040357607f821691505b602082108114156200041a576200041962000456565b5b50919050565b6200042b82620004b4565b810181811067ffffffffffffffff821117156200044d576200044c62000485565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b61073b80620004d56000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a41368621461003b578063cfae321714610057575b600080fd5b6100556004803603810190610050919061043d565b610075565b005b61005f61013c565b60405161006c91906104b7565b60405180910390f35b6101226040518060600160405280602381526020016106e3602391396000805461009e90610610565b80601f01602080910402602001604051908101604052809291908181526020018280546100ca90610610565b80156101175780601f106100ec57610100808354040283529160200191610117565b820191906000526020600020905b8154815290600101906020018083116100fa57829003601f168201915b50505050508361026a565b8060009080519060200190610138929190610332565b5050565b60606000805461014b90610610565b80601f016020809104026020016040519081016040528092919081815260200182805461017790610610565b80156101c45780601f10610199576101008083540402835291602001916101c4565b820191906000526020600020905b8154815290600101906020018083116101a757829003601f168201915b5050505050905090565b61026682826040516024016101e49291906104d9565b6040516020818303038152906040527f4b5c4277000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050610309565b5050565b61030483838360405160240161028293929190610510565b6040516020818303038152906040527f2ced7cef000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050610309565b505050565b60008151905060006a636f6e736f6c652e6c6f679050602083016000808483855afa5050505050565b82805461033e90610610565b90600052602060002090601f01602090048101928261036057600085556103a7565b82601f1061037957805160ff19168380011785556103a7565b828001600101855582156103a7579182015b828111156103a657825182559160200191906001019061038b565b5b5090506103b491906103b8565b5090565b5b808211156103d15760008160009055506001016103b9565b5090565b60006103e86103e384610581565b61055c565b90508281526020810184848401111561040057600080fd5b61040b8482856105ce565b509392505050565b600082601f83011261042457600080fd5b81356104348482602086016103d5565b91505092915050565b60006020828403121561044f57600080fd5b600082013567ffffffffffffffff81111561046957600080fd5b61047584828501610413565b91505092915050565b6000610489826105b2565b61049381856105bd565b93506104a38185602086016105dd565b6104ac816106d1565b840191505092915050565b600060208201905081810360008301526104d1818461047e565b905092915050565b600060408201905081810360008301526104f3818561047e565b90508181036020830152610507818461047e565b90509392505050565b6000606082019050818103600083015261052a818661047e565b9050818103602083015261053e818561047e565b90508181036040830152610552818461047e565b9050949350505050565b6000610566610577565b90506105728282610642565b919050565b6000604051905090565b600067ffffffffffffffff82111561059c5761059b6106a2565b5b6105a5826106d1565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b82818337600083830152505050565b60005b838110156105fb5780820151818401526020810190506105e0565b8381111561060a576000848401525b50505050565b6000600282049050600182168061062857607f821691505b6020821081141561063c5761063b610673565b5b50919050565b61064b826106d1565b810181811067ffffffffffffffff8211171561066a576106696106a2565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f830116905091905056fe4368616e67696e67206772656574696e672066726f6d202725732720746f2027257327a2646970667358221220d8b702fd211fe2829242f362fda35c636e2590000ac3fa2b44f0ac2fb1183cd564736f6c634300080400334465706c6f79696e67206120477265657465722077697468206772656574696e673a",
+  "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063a41368621461003b578063cfae321714610057575b600080fd5b6100556004803603810190610050919061043d565b610075565b005b61005f61013c565b60405161006c91906104b7565b60405180910390f35b6101226040518060600160405280602381526020016106e3602391396000805461009e90610610565b80601f01602080910402602001604051908101604052809291908181526020018280546100ca90610610565b80156101175780601f106100ec57610100808354040283529160200191610117565b820191906000526020600020905b8154815290600101906020018083116100fa57829003601f168201915b50505050508361026a565b8060009080519060200190610138929190610332565b5050565b60606000805461014b90610610565b80601f016020809104026020016040519081016040528092919081815260200182805461017790610610565b80156101c45780601f10610199576101008083540402835291602001916101c4565b820191906000526020600020905b8154815290600101906020018083116101a757829003601f168201915b5050505050905090565b61026682826040516024016101e49291906104d9565b6040516020818303038152906040527f4b5c4277000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050610309565b5050565b61030483838360405160240161028293929190610510565b6040516020818303038152906040527f2ced7cef000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050610309565b505050565b60008151905060006a636f6e736f6c652e6c6f679050602083016000808483855afa5050505050565b82805461033e90610610565b90600052602060002090601f01602090048101928261036057600085556103a7565b82601f1061037957805160ff19168380011785556103a7565b828001600101855582156103a7579182015b828111156103a657825182559160200191906001019061038b565b5b5090506103b491906103b8565b5090565b5b808211156103d15760008160009055506001016103b9565b5090565b60006103e86103e384610581565b61055c565b90508281526020810184848401111561040057600080fd5b61040b8482856105ce565b509392505050565b600082601f83011261042457600080fd5b81356104348482602086016103d5565b91505092915050565b60006020828403121561044f57600080fd5b600082013567ffffffffffffffff81111561046957600080fd5b61047584828501610413565b91505092915050565b6000610489826105b2565b61049381856105bd565b93506104a38185602086016105dd565b6104ac816106d1565b840191505092915050565b600060208201905081810360008301526104d1818461047e565b905092915050565b600060408201905081810360008301526104f3818561047e565b90508181036020830152610507818461047e565b90509392505050565b6000606082019050818103600083015261052a818661047e565b9050818103602083015261053e818561047e565b90508181036040830152610552818461047e565b9050949350505050565b6000610566610577565b90506105728282610642565b919050565b6000604051905090565b600067ffffffffffffffff82111561059c5761059b6106a2565b5b6105a5826106d1565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b82818337600083830152505050565b60005b838110156105fb5780820151818401526020810190506105e0565b8381111561060a576000848401525b50505050565b6000600282049050600182168061062857607f821691505b6020821081141561063c5761063b610673565b5b50919050565b61064b826106d1565b810181811067ffffffffffffffff8211171561066a576106696106a2565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f830116905091905056fe4368616e67696e67206772656574696e672066726f6d202725732720746f2027257327a2646970667358221220d8b702fd211fe2829242f362fda35c636e2590000ac3fa2b44f0ac2fb1183cd564736f6c63430008040033",
+  "linkReferences": {},
+  "deployedLinkReferences": {}
+}