From 275f7179bfc754c60316ce47707b80e3f57d4a76 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 11 Dec 2021 18:39:39 +0100 Subject: [PATCH] feat(solc): add hardhat artifact support (#677) * feat: add hardhat artifact support * test: add hh test * feat: add hh fallback artifact handler * add format detection * chore: update changelog * chore: typos --- CHANGELOG.md | 1 + ethers-solc/src/artifacts.rs | 7 + ethers-solc/src/cache.rs | 21 ++- ethers-solc/src/config.rs | 111 ++++++++------- ethers-solc/src/error.rs | 6 + ethers-solc/src/hh.rs | 129 ++++++++++++++++++ ethers-solc/src/lib.rs | 28 ++-- .../test-data/hh-greeter-artifact.json | 48 +++++++ 8 files changed, 284 insertions(+), 67 deletions(-) create mode 100644 ethers-solc/src/hh.rs create mode 100644 ethers-solc/test-data/hh-greeter-artifact.json 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) -> Result { @@ -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( &self, artifacts_root: &Path, @@ -215,7 +232,7 @@ impl SolFilesCacheBuilder { } pub fn insert_files(self, sources: Sources, dest: Option) -> Result { - 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, Option, Option) - where - Self: Sized, - { - self.into_compact_contract().into_parts() - } + fn into_parts(self) -> (Option, Option, Option); } -impl Artifact for CompactContract { +impl> Artifact for T { fn into_inner(self) -> (Option, Option) { - (self.abi, self.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, Option) { - self.into_compact_contract().into_inner() + let artifact = self.into_compact_contract(); + (artifact.abi, artifact.bin.and_then(|bin| bin.into_bytes())) } fn into_compact_contract(self) -> CompactContract { self.into() } + + fn into_parts(self) -> (Option, Option, Option) { + 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) -> Result; + fn read_cached_artifact(path: impl AsRef) -> Result { + 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(files: I) -> Result> @@ -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 { 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) -> Result { - 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) -> Result { - todo!("Hardhat style artifacts not yet implemented") + fn read_cached_artifact(path: impl AsRef) -> Result { + 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::(&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) -> Self { SolcError::SolcError(msg.into()) } + pub(crate) fn msg(msg: impl Into) -> 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, + /// 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>>, + /// 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>>, +} + +impl From 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::(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 ProjectCompileOutput { } /// Finds the first contract with the given name and removes it from the set - pub fn remove(&mut self, contract: impl AsRef) -> Option { - let contract = contract.as_ref(); + pub fn remove(&mut self, contract_name: impl AsRef) -> Option { + 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 ProjectCompileOutput { .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) -> Option> { - let contract = contract.as_ref(); + pub fn find(&self, contract_name: impl AsRef) -> Option> { + 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)) }) } } 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": {} +}