diff --git a/Cargo.lock b/Cargo.lock
index 13a22a7d2ada..c1a5b924c291 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1935,6 +1935,7 @@ dependencies = [
"foundry-config",
"foundry-evm",
"foundry-test-utils",
+ "foundry-tweak",
"foundry-wallets",
"futures",
"indicatif",
@@ -3336,6 +3337,7 @@ dependencies = [
"foundry-evm",
"foundry-linking",
"foundry-test-utils",
+ "foundry-tweak",
"foundry-wallets",
"futures",
"globset",
@@ -3447,6 +3449,7 @@ dependencies = [
"foundry-debugger",
"foundry-evm",
"foundry-linking",
+ "foundry-tweak",
"foundry-wallets",
"futures",
"indicatif",
@@ -4057,6 +4060,31 @@ dependencies = [
"walkdir",
]
+[[package]]
+name = "foundry-tweak"
+version = "0.2.0"
+dependencies = [
+ "alloy-primitives",
+ "alloy-provider",
+ "alloy-rpc-types",
+ "eyre",
+ "foundry-block-explorers",
+ "foundry-cli",
+ "foundry-common",
+ "foundry-compilers",
+ "foundry-config",
+ "foundry-evm",
+ "indicatif",
+ "revm",
+ "serde",
+ "serde_json",
+ "tempfile",
+ "tokio",
+ "toml 0.8.14",
+ "vergen",
+ "yansi",
+]
+
[[package]]
name = "foundry-wallets"
version = "0.2.0"
diff --git a/Cargo.toml b/Cargo.toml
index 5c3ae60d585b..9701bfe7afaf 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,6 +22,7 @@ members = [
"crates/forge/",
"crates/macros/",
"crates/test-utils/",
+ "crates/tweak/"
]
resolver = "2"
@@ -153,6 +154,7 @@ foundry-evm-fuzz = { path = "crates/evm/fuzz" }
foundry-evm-traces = { path = "crates/evm/traces" }
foundry-macros = { path = "crates/macros" }
foundry-test-utils = { path = "crates/test-utils" }
+foundry-tweak = { path = "crates/tweak" }
foundry-wallets = { path = "crates/wallets" }
foundry-linking = { path = "crates/linking" }
diff --git a/README.md b/README.md
index 6ca5e9a6ee2b..418bc39a1b39 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,8 @@
![Github Actions][gha-badge] [![Telegram Chat][tg-badge]][tg-url] [![Telegram Support][tg-support-badge]][tg-support-url]
+### Foundry tweak is available to tweak the code of on-chain contracts, please see [TWEAK.md](./TWEAK.md)!
+
[gha-badge]: https://img.shields.io/github/actions/workflow/status/foundry-rs/foundry/test.yml?branch=master
[tg-badge]: https://img.shields.io/endpoint?color=neon&logo=telegram&label=chat&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Ffoundry_rs
[tg-url]: https://t.me/foundry_rs
diff --git a/TWEAK.md b/TWEAK.md
new file mode 100644
index 000000000000..7e15db2b7787
--- /dev/null
+++ b/TWEAK.md
@@ -0,0 +1,72 @@
+# Foundry with on-chain contract Tweak!
+
+**Tweak** allows users/developers to alter the code of an on-chain contract with the on-chain state untouched.
+Almost any modification is allowed, as long as the storage layout is preserved.
+
+With **Tweak**, you can:
+- Add `console.log()` in any on-chain contracts with source code available on Etherscan.
+- Change the logic of an on-chain contract (e.g., fixing bugs, conduct what-if analysis, etc.)
+
+Tweaking on-chain contracts are especially useful when:
+- inspecting the execution on-chain transactions;
+- investigating the logic of a complex contract (e.g., UniswapV3) on chain;
+- debug the interaction with on-chain contracts which produce unexpected revert errors.
+
+Here's the demo!
+[![asciicast](https://asciinema.org/a/650466.svg)](https://asciinema.org/a/650466)
+
+## Installation
+
+This project is a fork of foundry.
+The easiest way to install is to compile this project and replace the foundry toolchain originally on your machine.
+
+```
+cargo build
+```
+
+The executables are available in `target/debug` folder.
+All the tools that you are familiar in foundry are still there.
+
+What is enhanced is the `cast` and `forge` command.
+
+## Usage 1: Clone a verified contract code from Etherscan
+
+```
+forge clone
+```
+
+`forge clone` is a new feature that downloads the source code of a verified contract at `` from Etherscan (or other block explorers supported by foundry) and save as a foundry project in the specified ``.
+
+The cloned project is fully compilable foundry project, guaranteed to generate exactly the same bytecode as the on-chain contract.
+
+## Usage 2: Tweak one on-chain contract and replay a historical transaction invoking it
+
+```
+cd path/to/cloned/contract/project
+forge replay
+```
+
+If the current working directory is a foundry project created by `forge clone`, then the command `forge replay` is available to replay a historical transaction with the on-chain contract's code tweaked by the current project.
+Note that the cloned project "knows" which contract address it should tweak.
+
+You may want to modify the code of the cloned contract (e.g., add `console.log`s) before running `forge replay`.
+
+## Usage 3: Tweak multiple on-chain contracts and replay a historical transaction invoking them
+
+```
+cast run --tweak --tweak
+```
+
+`cast run` is also enhanced by allowing specified cloned projects to tweak the code of multiple on-chain contracts.
+Note that each cloned project "knows" which contract address it should tweak.
+
+## Disclaimer
+
+The **Tweak** feature is still under active development as part of the __EtherDebug__ project, whose ultimate goal is improve the overall development and debugging experience of EVM-compatibla smart contracts.
+
+The functionality is not stable yet and may suffer from breaking change in the future.
+Any contribution (bug reports, PRs, documentation, etc.) is welcome and greatly appreciated.
+
+## Acknowledgement
+
+Contributed by @Troublor and @ZhangZhuoSJTU with passion.
\ No newline at end of file
diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml
index 602ec57ae495..07de3b3ca2f1 100644
--- a/crates/cast/Cargo.toml
+++ b/crates/cast/Cargo.toml
@@ -32,6 +32,7 @@ foundry-compilers.workspace = true
foundry-config.workspace = true
foundry-evm.workspace = true
foundry-wallets.workspace = true
+foundry-tweak.workspace = true
alloy-chains.workspace = true
alloy-consensus = { workspace = true, features = ["serde", "kzg"] }
diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs
index c830f5ab1aa4..ad960d3087d6 100644
--- a/crates/cast/bin/cmd/run.rs
+++ b/crates/cast/bin/cmd/run.rs
@@ -1,7 +1,9 @@
+use std::path::PathBuf;
+
use alloy_primitives::U256;
use alloy_provider::Provider;
use alloy_rpc_types::BlockTransactions;
-use cast::{revm::primitives::EnvWithHandlerCfg, traces::TraceKind};
+use cast::{decode::decode_console_logs, revm::primitives::EnvWithHandlerCfg, traces::TraceKind};
use clap::Parser;
use eyre::{Result, WrapErr};
use foundry_cli::{
@@ -16,6 +18,7 @@ use foundry_evm::{
opts::EvmOpts,
utils::configure_tx_env,
};
+use foundry_tweak::tweak_backend;
/// CLI arguments for `cast run`.
#[derive(Clone, Debug, Parser)]
@@ -32,6 +35,7 @@ pub struct RunArgs {
trace_printer: bool,
/// Executes the transaction only with the state from the previous block.
+ /// Note that this also include transactions that are used for tweaking code.
///
/// May result in different results than the live execution!
#[arg(long, short)]
@@ -71,6 +75,13 @@ pub struct RunArgs {
/// See also, https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second
#[arg(long, value_name = "NO_RATE_LIMITS", visible_alias = "no-rpc-rate-limit")]
pub no_rate_limit: bool,
+
+ /// One `forge clone`d project that will be used to tweak the code of the corresponding
+ /// on-chain contract.
+ ///
+ /// This option can be used multiple times to tweak multiple contracts.
+ #[arg(long, value_name = "CLONED_PROJECT")]
+ pub tweak: Vec,
}
impl RunArgs {
@@ -80,8 +91,8 @@ impl RunArgs {
///
/// Note: This executes the transaction(s) as is: Cheatcodes are disabled
pub async fn run(self) -> Result<()> {
- let figment =
- Config::figment_with_root(find_project_root_path(None).unwrap()).merge(self.rpc);
+ let figment = Config::figment_with_root(find_project_root_path(None).unwrap())
+ .merge(self.rpc.clone());
let evm_opts = figment.extract::()?;
let mut config = Config::try_from(figment)?.sanitized();
@@ -143,6 +154,24 @@ impl RunArgs {
}
let mut executor = TracingExecutor::new(env.clone(), fork, evm_version, self.debug);
+ if !self.tweak.is_empty() {
+ // If user specified tweak projects, we need to tweak the code of the contracts
+ let mut cloned_projects: Vec = vec![];
+ for path in self.tweak.iter() {
+ let path = dunce::canonicalize(path)
+ .map_err(|e| eyre::eyre!("failed to load tweak project: {:?}", e))?;
+ let project =
+ foundry_tweak::ClonedProject::load_with_root(&path).wrap_err_with(|| {
+ format!("failed to load tweak project from path: {:?}", &path)
+ })?;
+ cloned_projects.push(project);
+ }
+ let tweak_map =
+ foundry_tweak::build_tweak_data(&cloned_projects, &self.rpc, self.quick).await?;
+ tweak_backend(executor.backend_mut(), &tweak_map)?;
+ }
+ println!("Executing transaction: {:?}", tx.hash);
+
let mut env =
EnvWithHandlerCfg::new_with_spec_id(Box::new(env.clone()), executor.spec_id());
@@ -158,6 +187,10 @@ impl RunArgs {
return Err(eyre::eyre!("Could not get block txs"))
};
+ let txs = txs.into_iter().take_while(|tx| tx.hash != tx_hash).collect::>();
+ let pb = init_progress(txs.len() as u64, "tx");
+ pb.set_position(0);
+
for (index, tx) in txs.into_iter().enumerate() {
// System transactions such as on L2s don't contain any pricing info so
// we skip them otherwise this would cause
@@ -206,22 +239,44 @@ impl RunArgs {
}
// Execute our transaction
- let result = {
- executor.set_trace_printer(self.trace_printer);
-
+ let (result, console_logs) = {
configure_tx_env(&mut env, &tx);
if let Some(to) = tx.to {
trace!(tx=?tx.hash, to=?to, "executing call transaction");
- TraceResult::from_raw(executor.transact_with_env(env)?, TraceKind::Execution)
+ let result = executor.transact_with_env(env)?;
+ let logs = decode_console_logs(&result.logs);
+ (TraceResult::from_raw(result, TraceKind::Execution), logs)
} else {
trace!(tx=?tx.hash, "executing create transaction");
- TraceResult::try_from(executor.deploy_with_env(env, None))?
+ let deploy_result = executor.deploy_with_env(env, None);
+ match deploy_result {
+ Ok(res) => {
+ let logs = decode_console_logs(&res.logs);
+ (TraceResult::from(res), logs)
+ }
+ Err(ref err) => {
+ let logs = match &err {
+ EvmError::Execution(e) => decode_console_logs(&e.logs),
+ _ => vec![],
+ };
+ (TraceResult::try_from(deploy_result)?, logs)
+ }
+ }
}
};
handle_traces(result, &config, chain, self.label, self.debug).await?;
+ // print logs if any
+ if !console_logs.is_empty() {
+ println!("Logs:");
+ for log in console_logs {
+ println!(" {log}");
+ }
+ println!();
+ }
+
Ok(())
}
}
diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs
index 32403dceb51f..fe8c742f072b 100644
--- a/crates/evm/evm/src/executors/mod.rs
+++ b/crates/evm/evm/src/executors/mod.rs
@@ -136,6 +136,11 @@ impl Executor {
&self.env.env
}
+ /// Returns a reference to the EVM environment with the handler configuration.
+ pub fn env_with_handler_cfg(&self) -> &EnvWithHandlerCfg {
+ &self.env
+ }
+
/// Returns a mutable reference to the EVM environment.
pub fn env_mut(&mut self) -> &mut Env {
&mut self.env.env
diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml
index 93b15ed61d00..eedc6c5f47b8 100644
--- a/crates/forge/Cargo.toml
+++ b/crates/forge/Cargo.toml
@@ -33,6 +33,7 @@ foundry-config.workspace = true
foundry-evm.workspace = true
foundry-wallets.workspace = true
foundry-linking.workspace = true
+foundry-tweak.workspace = true
ethers-contract-abigen = { workspace = true, features = ["providers"] }
diff --git a/crates/forge/bin/cmd/debug.rs b/crates/forge/bin/cmd/debug.rs
index 8fe1d2e32a25..eab4d970ab22 100644
--- a/crates/forge/bin/cmd/debug.rs
+++ b/crates/forge/bin/cmd/debug.rs
@@ -29,6 +29,20 @@ pub struct DebugArgs {
#[arg(long, short, default_value = "run()", value_name = "SIGNATURE")]
pub sig: String,
+ /// One `forge clone`d project that will be used to tweak the code of the corresponding
+ /// on-chain contract.
+ ///
+ /// This option can be used multiple times to tweak multiple contracts.
+ #[arg(long, value_name = "CLONED_PROJECT")]
+ pub tweak: Vec,
+
+ /// When tweaking the contract, use the quick mode where we replay the creation transaction
+ /// only with the state from the previous block.
+ ///
+ /// May result in different results than the live execution!
+ #[arg(long, requires = "tweak")]
+ pub tweak_quick: bool,
+
/// Open the script in the debugger.
#[arg(long)]
pub debug: bool,
@@ -50,8 +64,10 @@ impl DebugArgs {
gas_estimate_multiplier: 130,
opts: self.opts,
evm_opts: self.evm_opts,
- debug: true,
+ debug: self.debug,
retry: RETRY_VERIFY_ON_CREATE,
+ tweak: self.tweak,
+ tweak_quick: self.tweak_quick,
..Default::default()
};
script.run_script().await
diff --git a/crates/forge/bin/cmd/mod.rs b/crates/forge/bin/cmd/mod.rs
index d3e2f8b6d314..0b05f2480936 100644
--- a/crates/forge/bin/cmd/mod.rs
+++ b/crates/forge/bin/cmd/mod.rs
@@ -57,6 +57,7 @@ pub mod inspect;
pub mod install;
pub mod remappings;
pub mod remove;
+pub mod replay;
pub mod selectors;
pub mod snapshot;
pub mod soldeer;
diff --git a/crates/forge/bin/cmd/replay.rs b/crates/forge/bin/cmd/replay.rs
new file mode 100644
index 000000000000..18f1b170d690
--- /dev/null
+++ b/crates/forge/bin/cmd/replay.rs
@@ -0,0 +1,374 @@
+use std::collections::BTreeMap;
+
+use alloy_network::TransactionResponse;
+use alloy_primitives::{Address, Bytes, TxHash, U128, U256};
+use alloy_provider::Provider;
+use alloy_rpc_types::{Block, BlockTransactions, BlockTransactionsKind, Transaction};
+use eyre::{eyre, Context, Result};
+use forge::{
+ decode::decode_console_logs,
+ executors::{DeployResult, EvmError, Executor, ExecutorBuilder, RawCallResult},
+ revm::primitives::EnvWithHandlerCfg,
+ traces::TraceKind,
+ utils::configure_tx_env,
+};
+use foundry_cli::{
+ opts::RpcOpts,
+ utils::{handle_traces, init_progress, TraceResult},
+};
+use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE};
+use foundry_compilers::artifacts::EvmVersion;
+use foundry_evm::opts::EvmOpts;
+
+use clap::Parser;
+use foundry_config::{find_project_root_path, Config};
+use foundry_tweak::{build_tweaked_backend, ClonedProject};
+
+/// Replays an on-chain historical transaction locally on a fork of the blockchain with the on-chain
+/// contract tweaked by the current cloned project. In other words, `forge replay`:
+/// 1. Fork the blockchain, and replace the code of the on-chain contract associated with the
+/// current cloned project (address defined in `.clone.meta`).
+/// 2. Replays the historical transaction on the forked blockchain.
+///
+/// NOTE: `forge replay` can only be used on a `forge clone`d project, which contains the metadata
+/// of the on-chain instance of the contract.
+#[derive(Clone, Debug, Parser, Default)]
+pub struct ReplayArgs {
+ #[arg()]
+ transaction: String,
+
+ /// Executes the transaction only with the state from the previous block.
+ /// Note that this also include transactions that are used for tweaking code.
+ ///
+ /// May result in different results than the live execution!
+ #[arg(long, short)]
+ quick: bool,
+
+ /// Sets the number of assumed available compute units per second for this provider
+ ///
+ /// default value: 330
+ ///
+ /// See also, https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second
+ #[arg(long, alias = "cups", value_name = "CUPS")]
+ pub compute_units_per_second: Option,
+
+ /// Disables rate limiting for this node's provider.
+ ///
+ /// default value: false
+ ///
+ /// See also, https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second
+ #[arg(long, value_name = "NO_RATE_LIMITS", visible_alias = "no-rpc-rate-limit")]
+ pub no_rate_limit: bool,
+
+ /// Overrides the gas limit for the transaction.
+ #[arg(
+ long,
+ short,
+ value_name = "GAS",
+ help = "Override the gas limit for the transaction. If not set, two times of the gas limit from the transaction is used."
+ )]
+ pub gas: Option,
+
+ /// Overrides the gas price (or base fee in EIP1559) used for the transaction
+ #[arg(
+ long,
+ value_name = "GAS_PRICE",
+ help = "Override the gas price for the transaction. If not set, the gas price from the transaction is used."
+ )]
+ pub gas_price: Option,
+
+ /// The EVM version to use.
+ ///
+ /// Overrides the version specified in the config.
+ #[arg(long, short)]
+ evm_version: Option,
+
+ #[command(flatten)]
+ pub rpc: RpcOpts,
+}
+
+impl ReplayArgs {
+ /// Runs the `forge replay` command.
+ /// Logic is akin to the `cast run` command.
+ pub async fn run(mut self) -> Result<()> {
+ self.rpc.url = self.rpc.url.or(Some("http://localhost:8545".to_string()));
+ let root = find_project_root_path(None).unwrap();
+ let root = dunce::canonicalize(root).expect("failed to convert to absolute path");
+ let cloned_project = ClonedProject::load_with_root(&root)
+ .map_err(|e| eyre!("failed to load the cloned project: {}", e))?;
+ let tweaked_addr = cloned_project.metadata.address;
+ let tweaked_code = cloned_project.tweaked_code(&self.rpc, self.quick).await?;
+
+ let figment = Config::figment_with_root(&root).merge(&self.rpc);
+ let evm_opts = figment.extract::()?;
+ let mut config = Config::try_from(figment)?.sanitized();
+ config.evm_version = self.evm_version.unwrap_or_default();
+ let compute_units_per_second =
+ if self.no_rate_limit { Some(u64::MAX) } else { self.compute_units_per_second };
+
+ let tx_hash: TxHash = self.transaction.parse().wrap_err("invalid transaction hash")?;
+ let r = self
+ .replay_tx_hash(
+ &config,
+ &evm_opts,
+ tx_hash,
+ &vec![(tweaked_addr, tweaked_code)].into_iter().collect(),
+ compute_units_per_second,
+ )
+ .await?;
+ let console_logs = r.console_logs();
+ // print call trace
+ let r = r.trace_result()?;
+ handle_traces(r, &config, config.chain, vec![], false).await?;
+
+ // print logs if any
+ if !console_logs.is_empty() {
+ println!("Logs:");
+ for log in console_logs {
+ println!(" {log}");
+ }
+ println!();
+ }
+
+ Ok(())
+ }
+
+ async fn replay_tx_hash(
+ &self,
+ config: &Config,
+ evm_opts: &EvmOpts,
+ tx_hash: TxHash,
+ tweaks: &BTreeMap,
+ compute_units_per_second: Option,
+ ) -> Result {
+ let quick = self.quick;
+ let gas = self.gas;
+ let gas_price = self.gas_price;
+
+ let mut config = config.clone();
+
+ // construct JSON-RPC provider
+ let provider = foundry_common::provider::ProviderBuilder::new(
+ &config.get_rpc_url_or_localhost_http()?,
+ )
+ .compute_units_per_second_opt(compute_units_per_second)
+ .build()?;
+
+ // get transactiond data
+ let mut tx = provider
+ .get_transaction_by_hash(tx_hash)
+ .await
+ .unwrap()
+ .ok_or(eyre::eyre!("no such transactikon"))?;
+ let tx_block_number: u64 =
+ tx.block_number.ok_or(eyre!("transaction may still be pending"))?;
+ // If the gas is not specified, we use 2 times of the gas limit from the transaction
+ tx.gas = gas.map(|x| x as u128).unwrap_or(tx.gas.saturating_add(tx.gas));
+ // Set the gas price if specified
+ tx.gas_price = gas_price.map(|x| x.to()).or(tx.gas_price);
+
+ // get preceeding transactions in the same block
+ let block =
+ provider.get_block(tx_block_number.into(), BlockTransactionsKind::Full).await.unwrap();
+ let block = block.ok_or(eyre::eyre!("block not found"))?;
+
+ config.fork_block_number = Some(tx_block_number - 1); // fork from the previous block
+
+ // build the executor
+ let mut evm_opts = evm_opts.clone();
+ evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned());
+ evm_opts.fork_block_number = config.fork_block_number;
+ let env = evm_opts.evm_env().await?;
+ let fork = evm_opts.get_fork(&config, env.clone());
+ let backend = build_tweaked_backend(fork, tweaks)?;
+ let mut executor = ExecutorBuilder::new()
+ .inspectors(|stack| stack.trace(true))
+ .spec(config.evm_spec_id())
+ .build(env, backend);
+
+ // set the state to the moment right before the transaction
+ // we execute all transactions before the target transaction
+ let mut env = executor.env_with_handler_cfg().clone();
+ adjust_block_env(&mut env, &block);
+ env.block.number = U256::from(tx_block_number);
+ let BlockTransactions::Full(txs) = block.transactions else {
+ return Err(eyre::eyre!("block transactions not found"));
+ };
+ if !quick {
+ trace!("Executing transactions before the target transaction in the same block...");
+ let txs = txs
+ .into_iter()
+ .take_while(|tx: &Transaction| tx.tx_hash() != tx_hash)
+ .collect::>();
+ let pb = init_progress(txs.len() as u64, "replaying preceeding txs");
+ pb.set_position(0);
+ for (index, tx) in txs.into_iter().enumerate() {
+ pb.set_position(index as u64 + 1);
+
+ // System transactions such as on L2s don't contain any pricing info so
+ // we skip them otherwise this would cause
+ // reverts
+ if is_known_system_sender(tx.from) ||
+ tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE)
+ {
+ continue;
+ }
+
+ if tx.hash == tx_hash {
+ // we reach the target transaction
+ break;
+ }
+
+ // execute the transaction
+ trace!("Executing transaction: {:?}", tx.hash);
+ let _ = execute_tx(&mut executor, env.clone(), &tx)?;
+ }
+ }
+
+ // execute the target transaction
+ trace!("Executing target transaction: {:?}", tx.hash);
+ let r = execute_tx(&mut executor, env, &tx)?;
+ Ok(r)
+ }
+}
+
+fn adjust_block_env(env: &mut EnvWithHandlerCfg, block: &Block) {
+ env.block.timestamp = U256::from(block.header.timestamp);
+ env.block.coinbase = block.header.miner;
+ env.block.difficulty = block.header.difficulty;
+ env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default());
+ env.block.basefee = U256::from(block.header.base_fee_per_gas.unwrap_or_default());
+ env.block.gas_limit = U256::from(block.header.gas_limit);
+ if let Some(excess_blob_gas) = block.header.excess_blob_gas {
+ env.block.set_blob_excess_gas_and_price(excess_blob_gas as u64);
+ }
+}
+
+enum ExecuteResult {
+ Call(RawCallResult),
+ Create(DeployResult),
+ Revert(EvmError),
+}
+
+impl ExecuteResult {
+ pub fn trace_result(self) -> Result {
+ match self {
+ Self::Call(r) => Ok(TraceResult::from_raw(r, TraceKind::Execution)),
+ Self::Create(r) => Ok(TraceResult::from(r)),
+ Self::Revert(e) => TraceResult::try_from(Err(e)).map_err(|e| eyre!("revert: {}", e)),
+ }
+ }
+
+ pub fn console_logs(&self) -> Vec {
+ let raw_logs = match self {
+ Self::Call(r) => &r.logs,
+ Self::Create(r) => &r.raw.logs,
+ Self::Revert(EvmError::Execution(e)) => &e.raw.logs,
+ _ => return vec![],
+ };
+ decode_console_logs(raw_logs)
+ }
+}
+
+fn execute_tx(
+ executor: &mut Executor,
+ mut env: EnvWithHandlerCfg,
+ tx: &Transaction,
+) -> Result {
+ configure_tx_env(&mut env, tx);
+ // in case users overrides gas price below EIP1559 base fee, we disable base fee for the
+ // transaction
+ env.cfg.disable_block_gas_limit = true;
+ // in case users overrides gas price below the original gas price, we reset the gas priority fee
+ if env.tx.gas_price.lt(&env.tx.gas_priority_fee.unwrap_or_default()) {
+ env.tx.gas_priority_fee = Some(U256::ZERO);
+ }
+ // disable base fee when necessary
+ if env
+ .tx
+ .gas_price
+ .saturating_sub(env.tx.gas_priority_fee.unwrap_or_default())
+ .lt(&env.block.basefee)
+ {
+ env.cfg.disable_base_fee = true;
+ }
+ if tx.to.is_some() {
+ let r = executor.transact_with_env(env.clone()).wrap_err_with(|| {
+ format!("Failed to execute transaction: {:?} in block {}", tx.hash, env.block.number)
+ })?;
+ Ok(ExecuteResult::Call(r))
+ } else {
+ let r = executor.deploy_with_env(env, None);
+ match r {
+ Ok(r) => Ok(ExecuteResult::Create(r)),
+ Err(e) => Ok(ExecuteResult::Revert(e)),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::collections::BTreeMap;
+
+ use alloy_primitives::{Address, Bytes, TxHash};
+ use foundry_cli::utils::handle_traces;
+ use foundry_compilers::artifacts::EvmVersion;
+ use foundry_config::{figment::Figment, find_project_root_path, Config};
+ use foundry_evm::opts::EvmOpts;
+
+ #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+ async fn test_replay_tx_hash() {
+ let rpc = std::env::var("ETH_RPC_URL").unwrap_or("http://localhost:8545".to_string());
+ let tx: TxHash =
+ "0xbfa440cd7df20320fe8400e4f61113379a018e3904eef7cf6085cf6cf22bcdb9".parse().unwrap();
+
+ let figment = Config::figment_with_root(find_project_root_path(None).unwrap());
+ let evm_opts = figment.extract::().unwrap();
+ let mut config = Config::try_from(figment).unwrap().sanitized();
+ config.eth_rpc_url = Some(rpc);
+ config.evm_version = EvmVersion::Shanghai;
+
+ let args =
+ super::ReplayArgs { quick: false, gas: None, gas_price: None, ..Default::default() };
+ let r =
+ args.replay_tx_hash(&config, &evm_opts, tx, &BTreeMap::default(), None).await.unwrap();
+ let super::ExecuteResult::Call(result) = &r else {
+ panic!("expected ExecuteResult::Call");
+ };
+ assert!(!result.reverted);
+ assert_eq!(result.gas_used, 163_955);
+ let r = r.trace_result().unwrap();
+ handle_traces(r, &config, config.chain, vec![], false).await.unwrap();
+ }
+
+ #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+ async fn test_replay_tx_hash_with_tweak() {
+ let rpc = std::env::var("ETH_RPC_URL").unwrap_or("http://localhost:8545".to_string());
+ let tx: TxHash =
+ "0xbfa440cd7df20320fe8400e4f61113379a018e3904eef7cf6085cf6cf22bcdb9".parse().unwrap();
+ let factory: Address = "0x8d1fA935E5e8a5440c9Efc96C0d9fF387eBb179B".parse().unwrap();
+ let tweaked_code: Bytes = "0xfe".parse().unwrap();
+
+ let mut config = Config::default();
+ let figment: Figment = config.clone().into();
+ let evm_opts = figment.extract::().unwrap();
+ config.eth_rpc_url = Some(rpc);
+
+ let args =
+ super::ReplayArgs { quick: false, gas: None, gas_price: None, ..Default::default() };
+
+ let r = args
+ .replay_tx_hash(
+ &config,
+ &evm_opts,
+ tx,
+ &BTreeMap::from([(factory, tweaked_code)]),
+ None,
+ )
+ .await
+ .unwrap();
+ let r = r.trace_result().unwrap();
+ assert!(!r.success);
+ handle_traces(r, &config, config.chain, vec![], false).await.unwrap();
+ }
+}
diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs
index aff2ad530d96..52a8cb47ebe3 100644
--- a/crates/forge/bin/main.rs
+++ b/crates/forge/bin/main.rs
@@ -65,6 +65,7 @@ fn main() -> Result<()> {
ForgeSubcommand::Install(cmd) => cmd.run(),
ForgeSubcommand::Remove(cmd) => cmd.run(),
ForgeSubcommand::Remappings(cmd) => cmd.run(),
+ ForgeSubcommand::Replay(cmd) => utils::block_on(cmd.run()),
ForgeSubcommand::Init(cmd) => cmd.run(),
ForgeSubcommand::Completions { shell } => {
generate(shell, &mut Forge::command(), "forge", &mut std::io::stdout());
diff --git a/crates/forge/bin/opts.rs b/crates/forge/bin/opts.rs
index a449bd75f46f..cf96e2d611fe 100644
--- a/crates/forge/bin/opts.rs
+++ b/crates/forge/bin/opts.rs
@@ -2,7 +2,7 @@ use crate::cmd::{
bind::BindArgs, build::BuildArgs, cache::CacheArgs, clone::CloneArgs, config, coverage,
create::CreateArgs, debug::DebugArgs, doc::DocArgs, flatten, fmt::FmtArgs, geiger, generate,
init::InitArgs, inspect, install::InstallArgs, remappings::RemappingArgs, remove::RemoveArgs,
- selectors::SelectorsSubcommands, snapshot, soldeer, test, tree, update,
+ replay::ReplayArgs, selectors::SelectorsSubcommands, snapshot, soldeer, test, tree, update,
};
use clap::{Parser, Subcommand, ValueHint};
use forge_script::ScriptArgs;
@@ -79,6 +79,9 @@ pub enum ForgeSubcommand {
#[command(visible_alias = "re")]
Remappings(RemappingArgs),
+ #[command(visible_alias = "rp")]
+ Replay(ReplayArgs),
+
/// Verify smart contracts on Etherscan.
#[command(visible_alias = "v")]
VerifyContract(VerifyArgs),
diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml
index 3ef1f6b68bcf..becf3fccbb3e 100644
--- a/crates/script/Cargo.toml
+++ b/crates/script/Cargo.toml
@@ -23,6 +23,7 @@ foundry-debugger.workspace = true
foundry-cheatcodes.workspace = true
foundry-wallets.workspace = true
foundry-linking.workspace = true
+foundry-tweak.workspace = true
serde.workspace = true
eyre.workspace = true
diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs
index bfa9de0d2bc1..e78e2f55109e 100644
--- a/crates/script/src/execute.rs
+++ b/crates/script/src/execute.rs
@@ -10,9 +10,12 @@ use alloy_json_abi::{Function, InternalType, JsonAbi};
use alloy_primitives::{Address, Bytes};
use alloy_provider::Provider;
use async_recursion::async_recursion;
-use eyre::{OptionExt, Result};
+use eyre::{eyre, Context, OptionExt, Result};
use foundry_cheatcodes::ScriptWallets;
-use foundry_cli::utils::{ensure_clean_constructor, needs_setup};
+use foundry_cli::{
+ opts::RpcOpts,
+ utils::{ensure_clean_constructor, needs_setup},
+};
use foundry_common::{
fmt::{format_token, format_token_raw},
provider::get_http_provider,
@@ -29,6 +32,7 @@ use foundry_evm::{
render_trace_arena, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind,
},
};
+use foundry_tweak::{build_tweak_data, tweak_backend, TweakData};
use futures::future::join_all;
use itertools::Itertools;
use std::collections::{HashMap, HashSet};
@@ -70,6 +74,34 @@ impl LinkedState {
ensure_clean_constructor(&target_contract.abi)?;
+ // prepare tweak
+ let tweak = if !args.tweak.is_empty() {
+ let cloned_projects = args
+ .tweak
+ .iter()
+ .map(|path| {
+ let path = dunce::canonicalize(path)
+ .map_err(|e| eyre!("Failed to load tweak project: {:?}", e))?;
+ foundry_tweak::ClonedProject::load_with_root(&path).wrap_err_with(|| {
+ format!("Failed to load tweak project from path: {:?}", &path)
+ })
+ })
+ .collect::, _>>()?;
+
+ let rpc = RpcOpts {
+ url: script_config.evm_opts.fork_url.clone().or(args
+ .evm_opts
+ .fork_url
+ .clone()
+ .or(Some("http://localhost:8545".to_string()))),
+ ..Default::default()
+ };
+
+ build_tweak_data(&cloned_projects, &rpc, args.tweak_quick).await?
+ } else {
+ TweakData::new()
+ };
+
Ok(PreExecutionState {
args,
script_config,
@@ -81,6 +113,7 @@ impl LinkedState {
abi: target_contract.abi.clone(),
},
build_data,
+ tweak_data: tweak,
})
}
}
@@ -93,6 +126,7 @@ pub struct PreExecutionState {
pub script_wallets: ScriptWallets,
pub build_data: LinkedBuildData,
pub execution_data: ExecutionData,
+ pub tweak_data: TweakData,
}
impl PreExecutionState {
@@ -109,6 +143,7 @@ impl PreExecutionState {
self.build_data.build_data.target.clone(),
)
.await?;
+ tweak_backend(runner.executor.backend_mut(), &self.tweak_data)?;
let result = self.execute_with_runner(&mut runner).await?;
// If we have a new sender from execution, we need to use it to deploy libraries and relink
diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs
index f03e15fa2988..9c2dd68d3f17 100644
--- a/crates/script/src/lib.rs
+++ b/crates/script/src/lib.rs
@@ -47,7 +47,7 @@ use foundry_evm::{
};
use foundry_wallets::MultiWalletOpts;
use serde::{Deserialize, Serialize};
-use std::collections::HashMap;
+use std::{collections::HashMap, path::PathBuf};
use yansi::Paint;
mod broadcast;
@@ -178,6 +178,27 @@ pub struct ScriptArgs {
)]
pub with_gas_price: Option,
+ /// One `forge clone`d project that will be used to tweak the code of the corresponding
+ /// on-chain contract.
+ ///
+ /// This option can be used multiple times to tweak multiple contracts.
+ #[arg(
+ long,
+ value_name = "CLONED_PROJECT",
+ conflicts_with = "skip_simulation",
+ conflicts_with = "broadcast",
+ conflicts_with = "resume",
+ conflicts_with = "verify"
+ )]
+ pub tweak: Vec,
+
+ /// When tweaking the contract, use the quick mode where we replay the creation transaction
+ /// only with the state from the previous block.
+ ///
+ /// May result in different results than the live execution!
+ #[arg(long, requires = "tweak")]
+ pub tweak_quick: bool,
+
#[command(flatten)]
pub opts: CoreBuildArgs,
diff --git a/crates/tweak/Cargo.toml b/crates/tweak/Cargo.toml
new file mode 100644
index 000000000000..b1d5fe609167
--- /dev/null
+++ b/crates/tweak/Cargo.toml
@@ -0,0 +1,112 @@
+[package]
+name = "foundry-tweak"
+
+version.workspace = true
+edition.workspace = true
+rust-version.workspace = true
+authors.workspace = true
+license.workspace = true
+homepage.workspace = true
+repository.workspace = true
+
+[build-dependencies]
+vergen = { workspace = true, default-features = false, features = [
+ "build",
+ "git",
+ "gitcl",
+] }
+
+[dependencies]
+# lib
+foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] }
+foundry-common.workspace = true
+foundry-compilers = { workspace = true, features = ["full"] }
+foundry-config.workspace = true
+foundry-evm.workspace = true
+# foundry-wallets.workspace = true
+# foundry-linking.workspace = true
+
+# ethers-contract.workspace = true
+# ethers-core.workspace = true
+# ethers-middleware.workspace = true
+# ethers-providers.workspace = true
+# ethers-signers.workspace = true
+
+revm = { workspace = true, default-features = false, features = [
+ "std",
+ "serde",
+ "memory_limit",
+ "optional_eip3607",
+ "optional_block_gas_limit",
+ "optional_no_base_fee",
+ "arbitrary",
+] }
+# revm-inspectors.workspace = true
+
+# comfy-table = "7"
+eyre.workspace = true
+# proptest = "1"
+# rayon = "1"
+serde.workspace = true
+# tracing.workspace = true
+yansi.workspace = true
+
+# # bin
+# forge-doc.workspace = true
+# forge-fmt.workspace = true
+# forge-verify.workspace = true
+# forge-script.workspace = true
+foundry-cli.workspace = true
+# foundry-debugger.workspace = true
+
+# alloy-dyn-abi.workspace = true
+# alloy-json-abi.workspace = true
+alloy-primitives = { workspace = true, features = ["serde"] }
+alloy-provider.workspace = true
+alloy-rpc-types.workspace = true
+
+# async-trait = "0.1"
+# clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] }
+# clap_complete = "4"
+# clap_complete_fig = "4"
+# dialoguer = { version = "0.11", default-features = false }
+# dunce = "1"
+# futures = "0.3"
+# hex.workspace = true
+indicatif = "0.17"
+# itertools.workspace = true
+# once_cell = "1"
+# parking_lot = "0.12"
+# regex = { version = "1", default-features = false }
+# reqwest = { version = "0.11", default-features = false, features = ["json"] }
+# semver = "1"
+serde_json.workspace = true
+# similar = { version = "2", features = ["inline"] }
+# solang-parser.workspace = true
+# strum = { workspace = true, features = ["derive"] }
+# thiserror = "1"
+tokio = { version = "1", features = ["time"] }
+toml = { version = "0.8", features = ["preserve_order"] }
+# toml_edit = "0.21"
+# watchexec = "2.3.2"
+# evm-disassembler.workspace = true
+# rustc-hash.workspace = true
+
+# # doc server
+# axum = { workspace = true, features = ["ws"] }
+# hyper.workspace = true
+# tower-http = { workspace = true, features = ["fs"] }
+# opener = "0.6"
+
+# [dev-dependencies]
+# anvil.workspace = true
+# foundry-test-utils.workspace = true
+
+# criterion = "0.5"
+# globset = "0.4"
+# paste = "1.0"
+# path-slash = "0.2"
+# pretty_assertions.workspace = true
+# svm = { package = "svm-rs", version = "0.4", default-features = false, features = ["rustls"] }
+tempfile = "3"
+# tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] }
diff --git a/crates/tweak/README.md b/crates/tweak/README.md
new file mode 100644
index 000000000000..e77ddeec963e
--- /dev/null
+++ b/crates/tweak/README.md
@@ -0,0 +1,38 @@
+# foundry-tweak
+
+The functionalities to allow tweak the code of an on-chain contract.
+
+## Use Scenario
+
+1. Given an address of a contract on a blockchain (as well as a JSON-RPC endpoint), and a foundry project.
+2. Find the creation transaction of that contract.
+3. Resimulate the deployment of that contract with the original deployment arguments but with the code of the foundry project, and obtain the new deployed bytecode.
+4. fork the blockchain and replace the code of the on-chain contract.
+5. What is provided is a tweaked EVM runtime where the code of the contract is tweaked.
+6. A lot of things can be done afterwards, including replay transaction or send a new transaction to invoke the tweaked contract.
+
+## Specification
+
+### Input
+
+- the local foundry project (and specify contract)
+- contract address to tweak
+- json-rpc to fork the chain
+- etherscan API key (optional)
+
+### Pipeline
+
+0. Ensure that the local foundry project is compatible with the on-chain contract (no storage collision).
+1. Fetch constructor arguments from etherscan
+2. Fetch creation transaction from etherscan
+3. Replay and inspect the creation transaction, and find the message call that CREATEs the contract.
+4. Replay the creation transaction again and hijack the CREATE message call, replacing the call data with new creation bytecode and original arguments.
+
+### Output
+
+- the tweaked code of the contract
+
+### Downstream
+
+- Anvil may use this crate to tweak a forked blockchain.
+- Cast may use this crate to simulate a transaction on a tweaked contract.
\ No newline at end of file
diff --git a/crates/tweak/build.rs b/crates/tweak/build.rs
new file mode 100644
index 000000000000..c2f550fb6f82
--- /dev/null
+++ b/crates/tweak/build.rs
@@ -0,0 +1,3 @@
+fn main() {
+ vergen::EmitBuilder::builder().build_timestamp().git_sha(true).emit().unwrap();
+}
diff --git a/crates/tweak/src/code.rs b/crates/tweak/src/code.rs
new file mode 100644
index 000000000000..b9597e249384
--- /dev/null
+++ b/crates/tweak/src/code.rs
@@ -0,0 +1,585 @@
+//! Code generation for the contract we are going to tweak.
+//! The contract should from a cloned project created by `forge clone` command.
+//! The generation has to happen after the compatibility check.
+
+use alloy_primitives::{Address, Bytes, TxHash, U256};
+use alloy_provider::Provider;
+use alloy_rpc_types::{
+ serde_helpers::WithOtherFields, AnyReceiptEnvelope, BlockId, BlockTransactions,
+ BlockTransactionsKind, Log, Transaction, TransactionReceipt,
+};
+use eyre::{eyre, Context, Result};
+use foundry_cli::{opts::RpcOpts, p_println, utils::init_progress};
+use foundry_common::{
+ cli_warn, is_known_system_sender, provider::ProviderBuilder, SYSTEM_TRANSACTION_TYPE,
+};
+use foundry_compilers::{artifacts::ConfigurableContractArtifact, Artifact};
+use foundry_config::{figment::Figment, Config, NamedChain};
+use foundry_evm::{
+ backend::Backend,
+ executors::{EvmError, Executor, ExecutorBuilder},
+ opts::EvmOpts,
+ utils::configure_tx_env,
+ InspectorExt,
+};
+use revm::{
+ interpreter::{CreateInputs, CreateOutcome, Interpreter, OpCode},
+ primitives::{BlockEnv, EnvWithHandlerCfg, HashSet, SpecId},
+ EvmContext, Inspector,
+};
+use std::borrow::Cow;
+
+use crate::{constants::NonStandardPrecompiled, ClonedProject};
+
+#[derive(Debug)]
+struct TweakInspctor {
+ creation_count: u64,
+ creation_stack_depth: u64,
+ contract_address: Option,
+ target_creation_tag: Option<(u64, u64)>,
+ tweaked_creation_code: Option,
+ tweaked_code: Option,
+
+ // used to modify address(this)
+ to_update_address: bool,
+ created_address_in_tweak: Option,
+ observed_created_addresses: HashSet,
+}
+
+type TransactionExt = WithOtherFields;
+type TransactionReceiptExt = WithOtherFields>>;
+
+impl TweakInspctor {
+ pub fn new(contract_address: Option, tweaked_creation_code: Option) -> Self {
+ Self {
+ creation_count: 0,
+ creation_stack_depth: 0,
+ contract_address,
+ target_creation_tag: None,
+ tweaked_creation_code,
+ tweaked_code: None,
+
+ to_update_address: false,
+ created_address_in_tweak: None,
+ observed_created_addresses: HashSet::new(),
+ }
+ }
+
+ pub fn prepare_for_pinpoint(&mut self) -> Result<()> {
+ self.creation_count = 0;
+ self.creation_stack_depth = 0;
+
+ self.to_update_address = false;
+ self.created_address_in_tweak = None;
+ self.observed_created_addresses.clear();
+
+ eyre::ensure!(self.contract_address.is_some(), "the contract address is not found");
+
+ Ok(())
+ }
+
+ pub fn prepare_for_tweak(&mut self) -> Result<()> {
+ self.creation_count = 0;
+ self.creation_stack_depth = 0;
+
+ self.to_update_address = false;
+ self.created_address_in_tweak = None;
+ self.observed_created_addresses.clear();
+
+ eyre::ensure!(self.contract_address.is_some(), "the contract address is not found");
+ eyre::ensure!(self.target_creation_tag.is_some(), "the target creation count is not found");
+ eyre::ensure!(
+ self.tweaked_creation_code.is_some(),
+ "the tweaked creation code is not found"
+ );
+
+ Ok(())
+ }
+}
+
+impl InspectorExt<&mut Backend> for TweakInspctor {}
+
+impl Inspector<&mut Backend> for TweakInspctor {
+ #[inline]
+ fn create(
+ &mut self,
+ _: &mut EvmContext<&mut Backend>,
+ inputs: &mut CreateInputs,
+ ) -> Option {
+ // first update the creation_count and creation_stack_depth
+ self.creation_count += 1;
+ self.creation_stack_depth += 1;
+
+ // we then check creation count as the target one
+ if Some((self.creation_count, self.creation_stack_depth)) == self.target_creation_tag {
+ // we are going to tweak the creation code
+ if let Some(tweaked_creation_code) = &self.tweaked_creation_code {
+ inputs.init_code = tweaked_creation_code.clone();
+ }
+ }
+
+ None
+ }
+
+ #[inline]
+ fn create_end(
+ &mut self,
+ context: &mut EvmContext<&mut Backend>,
+ _: &CreateInputs,
+ outcome: CreateOutcome,
+ ) -> CreateOutcome {
+ // we shall first distinguish the replay stage
+ if let Some((target_count, target_stack_depth)) = self.target_creation_tag {
+ // record the tweaked contract address
+ if self.creation_count == target_count &&
+ self.creation_stack_depth == target_stack_depth
+ {
+ if let Some(address) = outcome.address {
+ if let Ok((code, _)) = context.code(address) {
+ self.tweaked_code = Some(code.clone());
+ }
+ }
+ }
+ } else {
+ // we are here to find the target creation count
+ if outcome.address == self.contract_address {
+ self.target_creation_tag = Some((self.creation_count, self.creation_stack_depth));
+ }
+ }
+
+ // we only decresae the creation_stack_depth
+ self.creation_stack_depth -= 1;
+ outcome
+ }
+
+ #[inline]
+ fn step(&mut self, interp: &mut Interpreter, _: &mut EvmContext<&mut Backend>) {
+ if Some((self.creation_count, self.creation_stack_depth)) == self.target_creation_tag {
+ if self.created_address_in_tweak.is_none() {
+ // When the first time the creation_count and creation_stack_depth match,
+ // we know that we are in the CREATE call that creates the target contract.
+ // We record the address of the created contract, so that we can replace it
+ // with the original address of contract being tweaked.
+ self.created_address_in_tweak.replace(interp.contract().target_address);
+ }
+ // the `step_end` hook should replace the output of ADDRESS code only if the
+ // current execution is in the context of the target contract
+ self.to_update_address = interp.current_opcode() == OpCode::ADDRESS.get() &&
+ self.created_address_in_tweak.unwrap() == interp.contract().target_address;
+ }
+ }
+
+ #[inline]
+ fn step_end(&mut self, interp: &mut Interpreter, _: &mut EvmContext<&mut Backend>) {
+ if Some((self.creation_count, self.creation_stack_depth)) == self.target_creation_tag &&
+ self.to_update_address
+ {
+ // we hook into the ADDRESS opcode to update the address
+ // note that we only update the address for the target contract
+ if let Some(original_address) = self.contract_address {
+ let stack = &mut interp.stack;
+ self.observed_created_addresses
+ .insert(Address::from_word(stack.pop().unwrap().into()));
+ stack.push(original_address.into_word().into()).unwrap();
+ }
+ }
+ }
+}
+
+pub async fn generate_tweaked_code(
+ rpc: &RpcOpts,
+ project: &ClonedProject,
+ quick: bool,
+) -> Result {
+ println!("Tweaking the contract at {}...", project.metadata.address);
+ p_println!(!quick => "It may take time if the RPC has rate limits.");
+ // prepare the deployment bytecode (w/ parameters)
+ let artifact = project.main_artifact()?;
+ let tweaked_creation_code = prepare_tweaked_creation_code(project, &artifact)?;
+
+ // let's tweak!
+ tweak(rpc, project, tweaked_creation_code, quick).await
+}
+
+// tweak the contract creation code
+async fn tweak(
+ rpc: &RpcOpts,
+ project: &ClonedProject,
+ tweaked_creation_code: Bytes,
+ quick: bool,
+) -> Result {
+ // prepare the execution backend
+ let (mut db, mut env) = prepare_backend(rpc, project, quick).await?;
+
+ // let hook into the creation process
+ let mut inspector =
+ TweakInspctor::new(Some(project.metadata.address), Some(tweaked_creation_code));
+
+ // round 1: pinpoint the target creation count
+ inspector.prepare_for_pinpoint()?;
+ let rv = db.inspect(&mut env.clone(), &mut inspector)?;
+ eyre::ensure!(
+ rv.result.is_success(),
+ "failed to pinpoint the target creation: {:?}",
+ rv.result
+ );
+ eyre::ensure!(
+ inspector.creation_stack_depth == 0,
+ "unexpected creation count: {}",
+ inspector.creation_stack_depth
+ );
+
+ // round 2: tweak the creation code
+ inspector.prepare_for_tweak()?;
+ // disable gas_limit and decrease gas_fee for the inspector
+ env.cfg.disable_block_gas_limit = true;
+ env.cfg.disable_base_fee = true;
+ // increase gas_limit and decrease gas_price for the transaction
+ env.tx.gas_limit = env.tx.gas_limit.checked_mul(2).ok_or(eyre!("gas limit overflow"))?;
+ env.tx.gas_price =
+ env.tx.gas_price.checked_div(U256::from(2)).ok_or(eyre!("divided by zero"))?;
+ env.tx.gas_priority_fee = Some(U256::ZERO);
+ // we do not care about the execution result in this round
+ db.inspect(&mut env, &mut inspector)?;
+ eyre::ensure!(
+ inspector.observed_created_addresses.len() <= 1,
+ "hooked ADDRESS opcodes return different addresses"
+ );
+
+ inspector.tweaked_code.ok_or(eyre!("the tweaked code is not generated"))
+}
+
+fn prepare_tweaked_creation_code(
+ project: &ClonedProject,
+ artifact: &ConfigurableContractArtifact,
+) -> Result {
+ let bytecode = artifact.get_bytecode().ok_or(eyre!("the contract does not have bytecode"))?;
+ let deployment_bytecode =
+ bytecode.bytes().ok_or(eyre!("the bytecode transformation failed"))?;
+ let constructor_arguments = &project.metadata.constructor_arguments;
+
+ // concate the deployment bytecode with the constructor arguments
+ let mut tweaked_creation_code = deployment_bytecode.to_vec();
+ tweaked_creation_code.extend_from_slice(&constructor_arguments[..]);
+
+ Ok(Bytes::from(tweaked_creation_code))
+}
+
+async fn prepare_backend(
+ rpc: &RpcOpts,
+ project: &ClonedProject,
+ quick: bool,
+) -> Result<(Backend, EnvWithHandlerCfg)> {
+ // get rpc_url
+ let rpc_url =
+ &rpc.url(Some(&project.config))?.unwrap_or(Cow::Borrowed("http://localhost:8545"));
+
+ // prepare the RPC provider
+ let provider = ProviderBuilder::new(rpc_url)
+ .chain(
+ NamedChain::try_from(project.metadata.chain_id)
+ .map_err(|_| eyre!("invalid chain id"))?,
+ )
+ .build()?;
+
+ // get block number
+ let tx_receipt = provider
+ .get_transaction_receipt(project.metadata.creation_transaction)
+ .await?
+ .ok_or(eyre!("the transaction is not mined"))?;
+ let block_number = tx_receipt.block_number.ok_or(eyre!("the transaction is not mined"))?;
+
+ // then, we are going to replay all transactions before the creation transaction
+ let block = provider
+ .get_block(BlockId::Number(block_number.into()), BlockTransactionsKind::Full)
+ .await?
+ .ok_or(eyre!("block not found"))?;
+
+ // prepare the block env
+ let mut block_env = BlockEnv {
+ number: U256::from(
+ block.header.number.expect("block number is not found. Maybe it is not mined yet?"),
+ ),
+ timestamp: U256::from(block.header.timestamp),
+ coinbase: block.header.miner,
+ difficulty: block.header.difficulty,
+ prevrandao: Some(block.header.mix_hash.unwrap_or_default()),
+ basefee: U256::from(block.header.base_fee_per_gas.unwrap_or_default()),
+ gas_limit: U256::from(block.header.gas_limit),
+ blob_excess_gas_and_price: None,
+ };
+ if let Some(excess_blob_gas) = block.header.excess_blob_gas {
+ block_env.set_blob_excess_gas_and_price(excess_blob_gas as u64);
+ }
+
+ // get all transactions with receipts
+ let txs_with_receipt = if !quick {
+ let BlockTransactions::Full(txs) = block.transactions else {
+ return Err(eyre::eyre!("block transactions not found"));
+ };
+ let Some(recipts) = provider.get_block_receipts(block_number.into()).await? else {
+ return Err(eyre::eyre!("block receipts not found"));
+ };
+ txs.into_iter()
+ .zip(recipts.into_iter())
+ .map(|(a, b)| (WithOtherFields::new(a), b))
+ .collect()
+ } else {
+ vec![(
+ provider
+ .get_transaction_by_hash(project.metadata.creation_transaction)
+ .await?
+ .ok_or(eyre!("the transaction is not mined"))?,
+ tx_receipt,
+ )]
+ };
+
+ // get the figment from the cloned project's config
+ let mut config = project.config.clone();
+ config.fork_block_number = Some(block_number - 1);
+ let figment: Figment = config.into();
+ let figment = figment.merge(rpc);
+
+ // set evm options
+ let mut evm_opts = figment.extract::()?;
+ evm_opts.fork_url = Some(rpc_url.to_string());
+ evm_opts.fork_block_number = Some(block_number - 1);
+
+ // get an updated config
+ let config = Config::try_from(figment)?.sanitized();
+
+ // get env
+ let env = evm_opts.evm_env().await?;
+
+ // a loop to probe the proper EVM version
+ let mut spec_id = config.evm_spec_id();
+ let chain_id =
+ NamedChain::try_from(project.metadata.chain_id).map_err(|_| eyre!("invalid chain id"))?;
+
+ if chain_id == NamedChain::BinanceSmartChain || chain_id == NamedChain::BinanceSmartChainTestnet
+ {
+ cli_warn!("Tweaking contracts on BinanceSmartChain (BSC) may not work due to its non-standard gas consumption. Please see more at https://github.com/foundry-rs/foundry/issues/4784#issuecomment-1520402694");
+ }
+
+ if !quick {
+ loop {
+ // get backend and executor
+ let db = Backend::spawn(evm_opts.get_fork(&config, env.clone()));
+ // create the executor and the corresponding env
+ let executor = ExecutorBuilder::new().spec(spec_id).build(env.clone(), db);
+
+ match probe_evm_version(chain_id, executor, &block_env, &txs_with_receipt, None) {
+ Ok(_) => {
+ break;
+ }
+ Err(_) => {
+ spec_id = SpecId::try_from_u8((spec_id as u8) + 1)
+ .ok_or(eyre!("failed to probe a proper EVM version. You may want to use --quick option to skip EVM version probing."))?;
+ }
+ }
+ }
+ }
+
+ let db = Backend::spawn(evm_opts.get_fork(&config, env.clone()));
+ let executor = ExecutorBuilder::new().spec(spec_id).build(env.clone(), db);
+ probe_evm_version(
+ chain_id,
+ executor,
+ &block_env,
+ &txs_with_receipt,
+ Some(project.metadata.creation_transaction),
+ )
+}
+
+fn probe_evm_version(
+ chain_id: NamedChain,
+ mut executor: Executor,
+ block_env: &BlockEnv,
+ txs: &[(TransactionExt, TransactionReceiptExt)],
+ target_tx: Option,
+) -> Result<(Backend, EnvWithHandlerCfg)> {
+ let mut env = executor.env_with_handler_cfg().clone();
+
+ env.block = block_env.clone();
+
+ let non_standard_precompiled = NonStandardPrecompiled::get_precomiled_address(chain_id);
+
+ let pb =
+ init_progress(txs.len() as u64, format!("investigating {:?}", executor.spec_id()).as_str());
+ pb.set_position(0);
+
+ for (index, (tx, receipt)) in txs.iter().enumerate() {
+ pb.set_position(index as u64 + 1);
+
+ // skip system transactions
+ if is_known_system_sender(tx.from) || tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE) {
+ continue;
+ }
+
+ // skip non-standard precompiled contracts
+ if let Some(precompiled) = non_standard_precompiled.as_ref() {
+ if precompiled.contains(&tx.to.unwrap_or_default()) {
+ continue;
+ }
+ }
+
+ // always configure the transaction environment
+ configure_tx_env(&mut env, tx);
+
+ // find the creation transaction
+ if Some(tx.hash) == target_tx {
+ return Ok((executor.backend().clone(), env));
+ }
+
+ // get the actual gas used
+ let real_gas_used = receipt.gas_used as u64;
+
+ if tx.to.is_some() {
+ let rv = executor.transact_with_env(env.clone());
+
+ let rv = rv.wrap_err("Executing transaction fails")?;
+
+ // check gas used
+ eyre::ensure!(
+ rv.gas_used == real_gas_used,
+ "Gas used mismatch: expected {}, got {} ({:?})",
+ real_gas_used,
+ rv.gas_used,
+ tx.hash
+ );
+
+ // check transaction status
+ if (**receipt).inner.inner.receipt.status.coerce_status() {
+ eyre::ensure!(rv.exit_reason.is_ok(), "Transaction should succeed ({:?})", tx.hash);
+ } else {
+ eyre::ensure!(!rv.exit_reason.is_ok(), "Transaction should fail ({:?})", tx.hash);
+ }
+ } else {
+ match executor.deploy_with_env(env.clone(), None) {
+ // Reverted transactions should be skipped
+ Err(EvmError::Execution(error)) => {
+ eyre::ensure!(
+ error.gas_used == real_gas_used,
+ "Gas used mismatch: expected {}, got {} ({:?})",
+ real_gas_used,
+ error.gas_used,
+ tx.hash
+ );
+ eyre::ensure!(
+ !(**receipt).inner.inner.receipt.status.coerce_status(),
+ "Transaction should fail ({:?})",
+ tx.hash
+ );
+ }
+ Err(error) => {
+ return Err(error).wrap_err_with(|| {
+ format!(
+ "Failed to deploy transaction: {:?} in block {}",
+ tx.hash, env.block.number
+ )
+ })
+ }
+ Ok(rv) => {
+ eyre::ensure!(
+ rv.gas_used == real_gas_used,
+ "Gas used mismatch: expected {}, got {} ({:?})",
+ real_gas_used,
+ rv.gas_used,
+ tx.hash
+ );
+ eyre::ensure!(
+ (**receipt).inner.inner.receipt.status.coerce_status(),
+ "Transaction should succeed ({:?})",
+ tx.hash
+ );
+ }
+ }
+ }
+ }
+
+ if target_tx.is_none() {
+ Ok((executor.backend().clone(), env))
+ } else {
+ Err(eyre!("the target transaction is not found"))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::str::FromStr;
+
+ use crate::{code::tweak, metadata::CloneMetadata, ClonedProject};
+
+ use alloy_primitives::Bytes;
+ use foundry_cli::opts::RpcOpts;
+ use foundry_compilers::artifacts::EvmVersion;
+ use foundry_config::Config;
+
+ fn get_fake_project(address: &str, tx: &str) -> ClonedProject {
+ let fake_root = tempfile::tempdir().unwrap().path().to_path_buf();
+
+ ClonedProject {
+ root: fake_root,
+ config: Config { evm_version: EvmVersion::Paris, ..Default::default() },
+ metadata: CloneMetadata {
+ path: "src/FakeContract.sol".into(),
+ target_contract: "FakeContract".into(),
+ chain_id: 1,
+ address: address.parse().unwrap(),
+ creation_transaction: tx.parse().unwrap(),
+ deployer: Default::default(),
+ constructor_arguments: Default::default(),
+ storage_layout: Default::default(),
+ },
+ ..Default::default()
+ }
+ }
+
+ #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+ async fn test_direct_deployment() {
+ let fake_project = get_fake_project(
+ "0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545",
+ "0x79820495643caf5a1e7e96578361c9ddba0e0735cd684ada7450254f6fd58f51",
+ );
+ let rpc_url = std::env::var("ETH_RPC_URL").unwrap_or("http://localhost:8545".to_string());
+ let rpc = RpcOpts { url: Some(rpc_url), ..Default::default() };
+
+ let tweaked_code = format!(
+ "{:?}",
+ tweak(&rpc, &fake_project, Bytes::from_str(FAKE_CREATION_CODE).unwrap(), false)
+ .await
+ .unwrap()
+ );
+
+ assert!(
+ tweaked_code.starts_with(FAKE_DEPLOYED_CODE),
+ "the created code is not the same as the expected one"
+ );
+ }
+
+ #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+ async fn test_factory_deployment() {
+ let fake_project = get_fake_project(
+ "0x6B880d3B1FA2475C30Dc583726c56B4aFc66bD0b",
+ "0x3812e48763d3631516206fb878007ed126223d5c31e8cc433c79659b8afbbf24",
+ );
+ let rpc_url = std::env::var("ETH_RPC_URL").unwrap_or("http://localhost:8545".to_string());
+ let rpc = RpcOpts { url: Some(rpc_url), ..Default::default() };
+
+ let tweaked_code = format!(
+ "{:?}",
+ tweak(&rpc, &fake_project, Bytes::from_str(FAKE_CREATION_CODE).unwrap(), false)
+ .await
+ .unwrap()
+ );
+
+ assert!(
+ tweaked_code.starts_with(FAKE_DEPLOYED_CODE),
+ "the created code is not the same as the expected one"
+ );
+ }
+
+ const FAKE_DEPLOYED_CODE: &str = "0x60806040526004361061032d5760003560e01c80639157921c116101a5578063b2975794116100ec578063d547741f11610095578063dafae4081161006f578063dafae4081461096e578063dff525e11461098e578063e400327c146109ae578063e75235b8146109ce5761033c565b8063d547741f14610901578063d55ed10314610921578063d64af2a61461094e5761033c565b8063cdb67444116100c6578063cdb674441461089c578063cdf64a76146108b4578063d19773d2146108d45761033c565b8063b29757941461082f578063b9c362091461085c578063ca15c8731461087c5761033c565b8063a3912ec81161014e578063affed0e011610128578063affed0e0146107cc578063b1a2567e146107e2578063b1d08a03146108025761033c565b8063a3912ec81461033a578063ab7965661461077f578063ac78dfe8146107ac5761033c565b8063994390891161017f57806399439089146107155780639dcc4da314610735578063a217fddf1461076a5761033c565b80639157921c1461068f57806391d14854146106af57806393c5678f146106f55761033c565b806336568abe116102745780635c975abb1161021d5780637de5dedd116101f75780637de5dedd146106115780638456cb59146106265780638f34e3471461063b5780639010d07c1461066f5761033c565b80635c975abb146105ac5780636932be98146105c45780636c1ce670146105f15761033c565b80634d0d66731161024e5780634d0d66731461052f5780634d493f4e1461054f57806359122f6b1461057f5761033c565b806336568abe146104e75780633f4ba83a146105075780634b14557e1461051c5761033c565b80631d4a7210116102d65780632f2ff15d116102b05780632f2ff15d1461049b578063302d12db146104bb5780633644e515146104d25761033c565b80631d4a721014610428578063248a9ca3146104555780632dfdf0b5146104855761033c565b8063180ff1e911610307578063180ff1e9146103d55780631a8e55b0146103e85780631b6e7594146104085761033c565b806301ffc9a71461034457806317ce2dd41461037957806317fcb39b1461039d5761033c565b3661033c5761033a6109e6565b005b61033a6109e6565b34801561035057600080fd5b5061036461035f366004614843565b610a69565b60405190151581526020015b60405180910390f35b34801561038557600080fd5b5061038f60755481565b604051908152602001610370565b3480156103a957600080fd5b506074546103bd906001600160a01b031681565b6040516001600160a01b039091168152602001610370565b61033a6103e33660046148f4565b610aad565b3480156103f457600080fd5b5061033a6104033660046149e6565b610dbd565b34801561041457600080fd5b5061033a610423366004614a52565b610e8f565b34801561043457600080fd5b5061038f610443366004614aec565b603e6020526000908152604090205481565b34801561046157600080fd5b5061038f610470366004614b09565b60009081526072602052604090206001015490565b34801561049157600080fd5b5061038f60765481565b3480156104a757600080fd5b5061033a6104b6366004614b22565b610f64565b3480156104c757600080fd5b5061038f620f424081565b3480156104de57600080fd5b5060775461038f565b3480156104f357600080fd5b5061033a610502366004614b22565b610f8f565b34801561051357600080fd5b5061033a61101b565b61033a61052a366004614b52565b611083565b34801561053b57600080fd5b5061036461054a366004614b7d565b6110e1565b34801561055b57600080fd5b5061036461056a366004614b09565b607a6020526000908152604090205460ff1681565b34801561058b57600080fd5b5061038f61059a366004614aec565b603a6020526000908152604090205481565b3480156105b857600080fd5b5060005460ff16610364565b3480156105d057600080fd5b5061038f6105df366004614b09565b60796020526000908152604090205481565b3480156105fd57600080fd5b5061036461060c366004614c06565b61118c565b34801561061d57600080fd5b5061038f61119f565b34801561063257600080fd5b5061033a611234565b34801561064757600080fd5b5061038f7f5e5712e902fff5e704bc4d506ad976718319e019e9d2a872528a01a85db433e481565b34801561067b57600080fd5b506103bd61068a366004614c32565b61129c565b34801561069b57600080fd5b5061033a6106aa366004614c54565b6112b4565b3480156106bb57600080fd5b506103646106ca366004614b22565b60009182526072602090815260408084206001600160a01b0393909316845291905290205460ff1690565b34801561070157600080fd5b5061033a6107103660046149e6565b6115ca565b34801561072157600080fd5b506003546103bd906001600160a01b031681565b34801561074157600080fd5b50610755610750366004614c32565b611696565b60408051928352602083019190915201610370565b34801561077657600080fd5b5061038f600081565b34801561078b57600080fd5b5061038f61079a366004614aec565b603c6020526000908152604090205481565b3480156107b857600080fd5b506103646107c7366004614b09565b61172f565b3480156107d857600080fd5b5061038f60045481565b3480156107ee57600080fd5b5061033a6107fd3660046149e6565b6117ce565b34801561080e57600080fd5b5061038f61081d366004614aec565b60396020526000908152604090205481565b34801561083b57600080fd5b5061084f61084a366004614aec565b61189a565b6040516103709190614ca5565b34801561086857600080fd5b50610755610877366004614c32565b611992565b34801561088857600080fd5b5061038f610897366004614b09565b611a17565b3480156108a857600080fd5b50603754603854610755565b3480156108c057600080fd5b5061033a6108cf366004614aec565b611a2e565b3480156108e057600080fd5b5061038f6108ef366004614aec565b603b6020526000908152604090205481565b34801561090d57600080fd5b5061033a61091c366004614b22565b611a97565b34801561092d57600080fd5b5061038f61093c366004614aec565b603d6020526000908152604090205481565b34801561095a57600080fd5b5061033a610969366004614aec565b611abd565b34801561097a57600080fd5b50610364610989366004614b09565b611b26565b34801561099a57600080fd5b5061033a6109a9366004614cd2565b611bbd565b3480156109ba57600080fd5b5061033a6109c93660046149e6565b611cc7565b3480156109da57600080fd5b50600154600254610755565b60005460ff1615610a315760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064015b60405180910390fd5b6074546001600160a01b03163314610a6757610a4b614802565b338152604080820151349101528051610a65908290611d93565b505b565b60006001600160e01b031982167f5a05180f000000000000000000000000000000000000000000000000000000001480610aa75750610aa78261210a565b92915050565b607154610100900460ff16610ac85760715460ff1615610acc565b303b155b610b3e5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610a28565b607154610100900460ff16158015610b60576071805461ffff19166101011790555b610b6b60008d612171565b6075899055610b798b61217b565b610b828a6121dd565b610c29604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f159f52c1e3a2b6a6aad3950adf713516211484e0516dad685ea662a094b7c43b918101919091527fad7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a560608201524660808201523060a082015260c00160408051601f198184030181529190528051602090910120607755565b610c338887612238565b5050610c3f87876122f8565b5050610c496123d3565b6000610c558680614da6565b90501115610d1657610c7e610c6a8680614da6565b610c776020890189614da6565b8787612467565b610ca4610c8b8680614da6565b8660005b602002810190610c9f9190614da6565b612666565b610cca610cb18680614da6565b8660015b602002810190610cc59190614da6565b612779565b610cf0610cd78680614da6565b8660025b602002810190610ceb9190614da6565b61288c565b610d16610cfd8680614da6565b8660035b602002810190610d119190614da6565b612a30565b60005b610d266040870187614da6565b9050811015610d9c57610d8a7f5e5712e902fff5e704bc4d506ad976718319e019e9d2a872528a01a85db433e4610d606040890189614da6565b84818110610d7057610d70614d90565b9050602002016020810190610d859190614aec565b612b43565b80610d9481614e06565b915050610d19565b508015610daf576071805461ff00191690555b505050505050505050505050565b6000805160206157b9833981519152546001600160a01b03163314610e1d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b82610e7d5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e8984848484612779565b50505050565b6000805160206157b9833981519152546001600160a01b03163314610eef5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b84610f4e5760405162461bcd60e51b815260206004820152602960248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220656d60448201526870747920617272617960b81b6064820152608401610a28565b610f5c868686868686612467565b505050505050565b600082815260726020526040902060010154610f808133612b65565b610f8a8383612b43565b505050565b6001600160a01b038116331461100d5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c6600000000000000000000000000000000006064820152608401610a28565b6110178282612be5565b5050565b6000805160206157b9833981519152546001600160a01b0316331461107b5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a67612c07565b60005460ff16156110c95760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610a28565b610a656110db36839003830183614ec0565b33611d93565b6000805460ff16156111285760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610a28565b611184848484808060200260200160405190810160405280939291908181526020016000905b8282101561117a5761116b60608302860136819003810190614f13565b8152602001906001019061114e565b5050505050612ca3565b949350505050565b600061119883836133bc565b9392505050565b600061122f600360009054906101000a90046001600160a01b03166001600160a01b031663926323d56040518163ffffffff1660e01b815260040160206040518083038186803b1580156111f257600080fd5b505afa158015611206573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061122a9190614f89565b613480565b905090565b6000805160206157b9833981519152546001600160a01b031633146112945760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a676134b6565b60008281526073602052604081206111989083613531565b7f5e5712e902fff5e704bc4d506ad976718319e019e9d2a872528a01a85db433e46112df8133612b65565b60006112f86112f336859003850185614ff0565b61353d565b905061130c6112f336859003850185614ff0565b8335600090815260796020526040902054146113765760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c696420726563656044820152621a5c1d60ea1b6064820152608401610a28565b82356000908152607a602052604090205460ff166113fc5760405162461bcd60e51b815260206004820152603160248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220617060448201527f70726f766564207769746864726177616c0000000000000000000000000000006064820152608401610a28565b82356000908152607a602052604090819020805460ff19169055517fd639511b37b3b002cca6cfe6bca0d833945a5af5a045578a0627fc43b79b26309061144690839086906150c4565b60405180910390a160006114606080850160608601614aec565b9050600061147661012086016101008701615151565b600181111561148757611487614c71565b141561154f5760006114a2368690038601610100870161516e565b6001600160a01b0383166000908152603b60205260409020549091506114ce90610140870135906135c6565b604082015260006114e8368790038701610100880161516e565b60408301519091506114ff9061014088013561518a565b604082015260745461151f908390339086906001600160a01b03166135e0565b6115486115326060880160408901614aec565b60745483919086906001600160a01b03166135e0565b505061158b565b61158b6115626060860160408701614aec565b60745483906001600160a01b03166115833689900389016101008a0161516e565b9291906135e0565b7f21e88e956aa3e086f6388e899965cef814688f99ad8bb29b08d396571016372d82856040516115bc9291906150c4565b60405180910390a150505050565b6000805160206157b9833981519152546001600160a01b0316331461162a5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b8261168a5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e8984848484612666565b6000806116b86000805160206157b9833981519152546001600160a01b031690565b6001600160a01b0316336001600160a01b0316146117115760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b61171b84846122f8565b90925090506117286123d3565b9250929050565b6003546040805163926323d560e01b815290516000926001600160a01b03169163926323d5916004808301926020929190829003018186803b15801561177457600080fd5b505afa158015611788573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ac9190614f89565b6037546117b991906151a1565b6038546117c690846151a1565b101592915050565b6000805160206157b9833981519152546001600160a01b0316331461182e5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b8261188e5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e898484848461288c565b60408051808201909152600080825260208201526001600160a01b0382166000908152607860205260409081902081518083019092528054829060ff1660018111156118e8576118e8614c71565b60018111156118f9576118f9614c71565b815290546001600160a01b036101009091048116602092830152908201519192501661198d5760405162461bcd60e51b815260206004820152602560248201527f4d61696e636861696e4761746577617956323a20756e737570706f727465642060448201527f746f6b656e0000000000000000000000000000000000000000000000000000006064820152608401610a28565b919050565b6000806119b46000805160206157b9833981519152546001600160a01b031690565b6001600160a01b0316336001600160a01b031614611a0d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b61171b8484612238565b6000818152607360205260408120610aa790613a13565b6000805160206157b9833981519152546001600160a01b03163314611a8e5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a65816121dd565b600082815260726020526040902060010154611ab38133612b65565b610f8a8383612be5565b6000805160206157b9833981519152546001600160a01b03163314611b1d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a658161217b565b6003546040805163926323d560e01b815290516000926001600160a01b03169163926323d5916004808301926020929190829003018186803b158015611b6b57600080fd5b505afa158015611b7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba39190614f89565b600154611bb091906151a1565b6002546117c690846151a1565b6000805160206157b9833981519152546001600160a01b03163314611c1d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b85611c7c5760405162461bcd60e51b815260206004820152602960248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220656d60448201526870747920617272617960b81b6064820152608401610a28565b611c8a878787878787612467565b611c978787836000610c8f565b611ca48787836001610cb5565b611cb18787836002610cdb565b611cbe8787836003610d01565b50505050505050565b6000805160206157b9833981519152546001600160a01b03163314611d275760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b82611d875760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e8984848484612a30565b604080518082018252600080825260208201526074549184015190916001600160a01b031690611dc290613a1d565b60208401516001600160a01b0316611ee1573484604001516040015114611e375760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c69642072657175604482015262195cdd60ea1b6064820152608401610a28565b611e408161189a565b6040850151519092506001811115611e5a57611e5a614c71565b82516001811115611e6d57611e6d614c71565b14611ecd5760405162461bcd60e51b815260206004820152602a60248201527f4d61696e636861696e4761746577617956323a20696e76616c696420746f6b656044820152691b881cdd185b99185c9960b21b6064820152608401610a28565b6001600160a01b0381166020850152612087565b3415611f3b5760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c69642072657175604482015262195cdd60ea1b6064820152608401610a28565b611f48846020015161189a565b6040850151519092506001811115611f6257611f62614c71565b82516001811115611f7557611f75614c71565b14611fd55760405162461bcd60e51b815260206004820152602a60248201527f4d61696e636861696e4761746577617956323a20696e76616c696420746f6b656044820152691b881cdd185b99185c9960b21b6064820152608401610a28565b60208401516040850151611fec9185903090613ac7565b83602001516001600160a01b0316816001600160a01b031614156120875760408481015181015190517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101919091526001600160a01b03821690632e1a7d4d90602401600060405180830381600087803b15801561206e57600080fd5b505af1158015612082573d6000803e3d6000fd5b505050505b607680546000918261209883614e06565b91905055905060006120bf858386602001516075548a613ce190949392919063ffffffff16565b90507fd7b25068d9dc8d00765254cfb7f5070f98d263c8d68931d937c7362fa738048b6120eb8261353d565b826040516120fa9291906151c0565b60405180910390a1505050505050565b60006001600160e01b031982167f7965db0b000000000000000000000000000000000000000000000000000000001480610aa757507f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b0319831614610aa7565b6110178282612b43565b6074805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527f9d2334c23be647e994f27a72c5eee42a43d5bdcfe15bb88e939103c2b114cbaf906020015b60405180910390a150565b6003805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527fef40dc07567635f84f5edbd2f8dbc16b40d9d282dd8e7e6f4ff58236b6836169906020016121d2565b6000808284111561228b5760405162461bcd60e51b815260206004820152601c60248201527f4761746577617956323a20696e76616c6964207468726573686f6c64000000006044820152606401610a28565b505060018054600280549285905583905560048054919291849186919060006122b383614e06565b9091555060408051868152602081018690527f976f8a9c5bdf8248dec172376d6e2b80a8e3df2f0328e381c6db8e1cf138c0f891015b60405180910390a49250929050565b600080828411156123715760405162461bcd60e51b815260206004820152602760248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c696420746860448201527f726573686f6c64000000000000000000000000000000000000000000000000006064820152608401610a28565b5050603780546038805492859055839055600480549192918491869190600061239983614e06565b9091555060408051868152602081018690527f31312c97b89cc751b832d98fd459b967a2c3eef3b49757d1cf5ebaa12bb6eee191016122e9565b6002546037546123e391906151a1565b6038546001546123f391906151a1565b1115610a675760405162461bcd60e51b815260206004820152602860248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c696420746860448201527f726573686f6c64730000000000000000000000000000000000000000000000006064820152608401610a28565b848314801561247557508481145b6124e75760405162461bcd60e51b815260206004820152602860248201527f4d61696e636861696e4761746577617956323a20696e76616c6964206172726160448201527f79206c656e6774680000000000000000000000000000000000000000000000006064820152608401610a28565b60005b8581101561262c5784848281811061250457612504614d90565b90506020020160208101906125199190614aec565b6078600089898581811061252f5761252f614d90565b90506020020160208101906125449190614aec565b6001600160a01b039081168252602082019290925260400160002080547fffffffffffffffffffffff0000000000000000000000000000000000000000ff1661010093909216929092021790558282828181106125a3576125a3614d90565b90506020020160208101906125b89190615151565b607860008989858181106125ce576125ce614d90565b90506020020160208101906125e39190614aec565b6001600160a01b031681526020810191909152604001600020805460ff19166001838181111561261557612615614c71565b02179055508061262481614e06565b9150506124ea565b507fa4f03cc9c0e0aeb5b71b4ec800702753f65748c2cf3064695ba8e8b46be704448686868686866040516120fa969594939291906152c1565b8281146126c85760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b83811015612743578282828181106126e5576126e5614d90565b905060200201356039600087878581811061270257612702614d90565b90506020020160208101906127179190614aec565b6001600160a01b031681526020810191909152604001600020558061273b81614e06565b9150506126cb565b507f80bc635c452ae67f12f9b6f12ad4daa6dbbc04eeb9ebb87d354ce10c0e210dc0848484846040516115bc9493929190615339565b8281146127db5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b83811015612856578282828181106127f8576127f8614d90565b90506020020135603a600087878581811061281557612815614d90565b905060200201602081019061282a9190614aec565b6001600160a01b031681526020810191909152604001600020558061284e81614e06565b9150506127de565b507f64557254143204d91ba2d95acb9fda1e5fea55f77efd028685765bc1e94dd4b5848484846040516115bc9493929190615339565b8281146128ee5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b838110156129fa57620f424083838381811061290f5761290f614d90565b90506020020135111561298a5760405162461bcd60e51b815260206004820152602860248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c696420706560448201527f7263656e746167650000000000000000000000000000000000000000000000006064820152608401610a28565b82828281811061299c5761299c614d90565b90506020020135603b60008787858181106129b9576129b9614d90565b90506020020160208101906129ce9190614aec565b6001600160a01b03168152602081019190915260400160002055806129f281614e06565b9150506128f1565b507fb05f5de88ae0294ebb6f67c5af2fcbbd593cc6bdfe543e2869794a4c8ce3ea50848484846040516115bc9493929190615339565b828114612a925760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b83811015612b0d57828282818110612aaf57612aaf614d90565b90506020020135603c6000878785818110612acc57612acc614d90565b9050602002016020810190612ae19190614aec565b6001600160a01b0316815260208101919091526040016000205580612b0581614e06565b915050612a95565b507fb5d2963614d72181b4df1f993d45b83edf42fa19710f0204217ba1b3e183bb73848484846040516115bc9493929190615339565b612b4d8282613db6565b6000828152607360205260409020610f8a9082613e58565b60008281526072602090815260408083206001600160a01b038516845290915290205460ff1661101757612ba3816001600160a01b03166014613e6d565b612bae836020613e6d565b604051602001612bbf9291906153d0565b60408051601f198184030181529082905262461bcd60e51b8252610a2891600401615451565b612bef828261404e565b6000828152607360205260409020610f8a90826140d1565b60005460ff16612c595760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610a28565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6000823561014084013582612cbe6080870160608801614aec565b9050612cdb612cd6368890038801610100890161516e565b613a1d565b6001612ced6040880160208901615151565b6001811115612cfe57612cfe614c71565b14612d715760405162461bcd60e51b815260206004820152602860248201527f4d61696e636861696e4761746577617956323a20696e76616c6964207265636560448201527f697074206b696e640000000000000000000000000000000000000000000000006064820152608401610a28565b60808601354614612de95760405162461bcd60e51b8152602060048201526024808201527f4d61696e636861696e4761746577617956323a20696e76616c6964206368616960448201527f6e206964000000000000000000000000000000000000000000000000000000006064820152608401610a28565b6000612dfe61084a6080890160608a01614aec565b9050612e1261012088016101008901615151565b6001811115612e2357612e23614c71565b81516001811115612e3657612e36614c71565b148015612e675750612e4e60e0880160c08901614aec565b6001600160a01b031681602001516001600160a01b0316145b612ebf5760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c696420726563656044820152621a5c1d60ea1b6064820152608401610a28565b60008481526079602052604090205415612f415760405162461bcd60e51b815260206004820152603260248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220707260448201527f6f636573736564207769746864726177616c00000000000000000000000000006064820152608401610a28565b6001612f5561012089016101008a01615151565b6001811115612f6657612f66614c71565b1480612f795750612f7782846133bc565b155b612feb5760405162461bcd60e51b815260206004820152603260248201527f4d61696e636861696e4761746577617956323a2072656163686564206461696c60448201527f79207769746864726177616c206c696d697400000000000000000000000000006064820152608401610a28565b6000612fff6112f3368a90038a018a614ff0565b9050600061300f607754836140e6565b6003549091506001600160a01b0316600061303d6130356101208d016101008e01615151565b878985614142565b60408051606081018252600080825260208201819052918101829052919b50919250819081906000805b8f5181101561323c578f818151811061308257613082614d90565b6020908102919091018101518051818301516040808401518151600081529586018083528f905260ff9093169085015260608401526080830152935060019060a0016020604051602081039080840390855afa1580156130e6573d6000803e3d6000fd5b505050602060405103519450846001600160a01b0316846001600160a01b0316106131795760405162461bcd60e51b815260206004820152602160248201527f4d61696e636861696e4761746577617956323a20696e76616c6964206f72646560448201527f72000000000000000000000000000000000000000000000000000000000000006064820152608401610a28565b6040517f953865650000000000000000000000000000000000000000000000000000000081526001600160a01b03808716600483015286955089169063953865659060240160206040518083038186803b1580156131d657600080fd5b505afa1580156131ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061320e9190614f89565b6132189083615484565b915086821061322a576001955061323c565b8061323481614e06565b915050613067565b50846132b05760405162461bcd60e51b815260206004820152603660248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220696e60448201527f73756666696369656e7420766f746520776569676874000000000000000000006064820152608401610a28565b50505060008a81526079602052604090208690555050881561332c576000888152607a602052604090819020805460ff19166001179055517f89e52969465b1f1866fc5d46fd62de953962e9cb33552443cd999eba05bd20dc906133179086908e906150c4565b60405180910390a15050505050505050610aa7565b6133368688614233565b61337561334960608d0160408e01614aec565b87607460009054906101000a90046001600160a01b03168e61010001803603810190611583919061516e565b7f21e88e956aa3e086f6388e899965cef814688f99ad8bb29b08d396571016372d848c6040516133a69291906150c4565b60405180910390a1505050505050505092915050565b6001600160a01b0382166000908152603a602052604081205482106133e357506000610aa7565b60006133f2620151804261549c565b6001600160a01b0385166000908152603e60205260409020549091508111156134385750506001600160a01b0382166000908152603c6020526040902054811015610aa7565b6001600160a01b0384166000908152603d602052604090205461345c908490615484565b6001600160a01b0385166000908152603c602052604090205411159150610aa79050565b600060025460016002548460015461349891906151a1565b6134a29190615484565b6134ac919061518a565b610aa7919061549c565b60005460ff16156134fc5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610a28565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612c863390565b600061119883836142c3565b60007fb9d1fe7c9deeec5dc90a2f47ff1684239519f2545b2228d3d91fb27df3189eea60001b8260000151836020015161357a85604001516142ed565b61358786606001516142ed565b6135948760800151614350565b6040516020016135a9969594939291906154be565b604051602081830303815290604052805190602001209050919050565b6000620f42406135d683856151a1565b611198919061549c565b6000816001600160a01b0316836001600160a01b031614156136905760408086015190516001600160a01b0386169180156108fc02916000818181858888f1935050505061368b57816001600160a01b031663d0e30db086604001516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561366757600080fd5b505af115801561367b573d6000803e3d6000fd5b505050505061368b858585614393565b613a0c565b6000855160018111156136a5576136a5614c71565b1415613866576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038516906370a082319060240160206040518083038186803b15801561370657600080fd5b505afa15801561371a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061373e9190614f89565b9050856040015181101561385557836001600160a01b03166340c10f193083896040015161376c919061518a565b6040516001600160a01b03909216602483015260448201526064016040516020818303038152906040529060e01b6020820180516001600160e01b0383818316178352505050506040516137c091906154f8565b6000604051808303816000865af19150503d80600081146137fd576040519150601f19603f3d011682016040523d82523d6000602084013e613802565b606091505b505080925050816138555760405162461bcd60e51b815260206004820152601b60248201527f546f6b656e3a204552433230206d696e74696e67206661696c656400000000006044820152606401610a28565b613860868686614393565b50613a0c565b60018551600181111561387b5761387b614c71565b141561399e5761389083858760200151614437565b61368b57602085810151604080516001600160a01b038881166024830152604480830194909452825180830390940184526064909101825292820180516001600160e01b03167f40c10f1900000000000000000000000000000000000000000000000000000000179052519185169161390991906154f8565b6000604051808303816000865af19150503d8060008114613946576040519150601f19603f3d011682016040523d82523d6000602084013e61394b565b606091505b5050809150508061368b5760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e3a20455243373231206d696e74696e67206661696c6564000000006044820152606401610a28565b60405162461bcd60e51b815260206004820152602160248201527f546f6b656e3a20756e737570706f7274656420746f6b656e207374616e64617260448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610a28565b5050505050565b6000610aa7825490565b600081516001811115613a3257613a32614c71565b148015613a43575060008160400151115b8015613a5157506020810151155b80613a7b5750600181516001811115613a6c57613a6c614c71565b148015613a7b57506040810151155b610a655760405162461bcd60e51b815260206004820152601360248201527f546f6b656e3a20696e76616c696420696e666f000000000000000000000000006044820152606401610a28565b600060608186516001811115613adf57613adf614c71565b1415613bbd5760408681015181516001600160a01b038881166024830152878116604483015260648083019390935283518083039093018352608490910183526020820180516001600160e01b03166323b872dd60e01b179052915191851691613b4991906154f8565b6000604051808303816000865af19150503d8060008114613b86576040519150601f19603f3d011682016040523d82523d6000602084013e613b8b565b606091505b509092509050818015613bb6575080511580613bb6575080806020019051810190613bb69190615514565b9150613c84565b600186516001811115613bd257613bd2614c71565b141561399e57602086810151604080516001600160a01b0389811660248301528881166044830152606480830194909452825180830390940184526084909101825292820180516001600160e01b03166323b872dd60e01b1790525191851691613c3c91906154f8565b6000604051808303816000865af19150503d8060008114613c79576040519150601f19603f3d011682016040523d82523d6000602084013e613c7e565b606091505b50909250505b81610f5c57613c92866144e2565b613ca6866001600160a01b03166014613e6d565b613cba866001600160a01b03166014613e6d565b613cce866001600160a01b03166014613e6d565b604051602001612bbf9493929190615536565b613d516040805160a08101825260008082526020808301829052835160608082018652838252818301849052818601849052848601919091528451808201865283815280830184905280860184905281850152845190810185528281529081018290529283015290608082015290565b83815260006020820181905250604080820180516001600160a01b039788169052602080890151825190891690820152905146908301528751606084018051918916909152805195909716940193909352935182015292909201516080820152919050565b60008281526072602090815260408083206001600160a01b038516845290915290205460ff166110175760008281526072602090815260408083206001600160a01b03851684529091529020805460ff19166001179055613e143390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000611198836001600160a01b03841661454f565b60606000613e7c8360026151a1565b613e87906002615484565b67ffffffffffffffff811115613e9f57613e9f614e21565b6040519080825280601f01601f191660200182016040528015613ec9576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110613f0057613f00614d90565b60200101906001600160f81b031916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110613f4b57613f4b614d90565b60200101906001600160f81b031916908160001a9053506000613f6f8460026151a1565b613f7a906001615484565b90505b6001811115613fff577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110613fbb57613fbb614d90565b1a60f81b828281518110613fd157613fd1614d90565b60200101906001600160f81b031916908160001a90535060049490941c93613ff881615606565b9050613f7d565b5083156111985760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610a28565b60008281526072602090815260408083206001600160a01b038516845290915290205460ff16156110175760008281526072602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000611198836001600160a01b03841661459e565b604080517f19010000000000000000000000000000000000000000000000000000000000006020808301919091526022820185905260428083018590528351808403909101815260629092019092528051910120600090611198565b6000806000836001600160a01b031663926323d56040518163ffffffff1660e01b815260040160206040518083038186803b15801561418057600080fd5b505afa158015614194573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141b89190614f89565b90506141c381613480565b925060008760018111156141d9576141d9614c71565b1415614229576001600160a01b038616600090815260396020526040902054851061420a5761420781614691565b92505b6001600160a01b0386166000908152603a602052604090205485101591505b5094509492505050565b6000614242620151804261549c565b6001600160a01b0384166000908152603e6020526040902054909150811115614291576001600160a01b03929092166000908152603e6020908152604080832094909455603d90529190912055565b6001600160a01b0383166000908152603d6020526040812080548492906142b9908490615484565b9091555050505050565b60008260000182815481106142da576142da614d90565b9060005260206000200154905092915050565b805160208083015160408085015190516000946135a9947f353bdd8d69b9e3185b3972e08b03845c0c14a21a390215302776a7a34b0e87649491939192019384526001600160a01b03928316602085015291166040830152606082015260800190565b805160208083015160408085015190516000946135a9947f1e2b74b2a792d5c0f0b6e59b037fa9d43d84fbb759337f0112fcc15ca414fc8d94919391920161561d565b600080845160018111156143a9576143a9614c71565b14156143c5576143be828486604001516146a9565b90506143ef565b6001845160018111156143da576143da614c71565b141561399e576143be82848660200151614437565b80610e89576143fd846144e2565b614411846001600160a01b03166014613e6d565b614425846001600160a01b03166014613e6d565b604051602001612bbf93929190615648565b604080513060248201526001600160a01b038481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092861691614495916154f8565b6000604051808303816000865af19150503d80600081146144d2576040519150601f19603f3d011682016040523d82523d6000602084013e6144d7565b606091505b509095945050505050565b606061450d826000015160018111156144fd576144fd614c71565b6001600160a01b03166001613e6d565b61451a8360200151614795565b6145278460400151614795565b604051602001614539939291906156d9565b6040516020818303038152906040529050919050565b600081815260018301602052604081205461459657508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610aa7565b506000610aa7565b600081815260018301602052604081205480156146875760006145c260018361518a565b85549091506000906145d69060019061518a565b905081811461463b5760008660000182815481106145f6576145f6614d90565b906000526020600020015490508087600001848154811061461957614619614d90565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061464c5761464c6157a2565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610aa7565b6000915050610aa7565b600060385460016038548460375461349891906151a1565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b03167fa9059cbb0000000000000000000000000000000000000000000000000000000017905291516000926060929087169161471f91906154f8565b6000604051808303816000865af19150503d806000811461475c576040519150601f19603f3d011682016040523d82523d6000602084013e614761565b606091505b50909250905081801561478c57508051158061478c57508080602001905181019061478c9190615514565b95945050505050565b6060816147d557505060408051808201909152600481527f3078303000000000000000000000000000000000000000000000000000000000602082015290565b8160005b81156147f857806147e981614e06565b915050600882901c91506147d9565b6111848482613e6d565b604080516060810182526000808252602082015290810161483e6040805160608101909152806000815260200160008152602001600081525090565b905290565b60006020828403121561485557600080fd5b81356001600160e01b03198116811461119857600080fd5b6001600160a01b0381168114610a6557600080fd5b803561198d8161486d565b8060608101831015610aa757600080fd5b8060808101831015610aa757600080fd5b60008083601f8401126148c157600080fd5b50813567ffffffffffffffff8111156148d957600080fd5b6020830191508360208260051b850101111561172857600080fd5b60008060008060008060008060008060006101408c8e03121561491657600080fd5b61491f8c614882565b9a5061492d60208d01614882565b995061493b60408d01614882565b985060608c0135975060808c0135965060a08c0135955060c08c0135945067ffffffffffffffff8060e08e0135111561497357600080fd5b6149838e60e08f01358f0161488d565b9450806101008e0135111561499757600080fd5b6149a88e6101008f01358f0161489e565b9350806101208e013511156149bc57600080fd5b506149ce8d6101208e01358e016148af565b81935080925050509295989b509295989b9093969950565b600080600080604085870312156149fc57600080fd5b843567ffffffffffffffff80821115614a1457600080fd5b614a20888389016148af565b90965094506020870135915080821115614a3957600080fd5b50614a46878288016148af565b95989497509550505050565b60008060008060008060608789031215614a6b57600080fd5b863567ffffffffffffffff80821115614a8357600080fd5b614a8f8a838b016148af565b90985096506020890135915080821115614aa857600080fd5b614ab48a838b016148af565b90965094506040890135915080821115614acd57600080fd5b50614ada89828a016148af565b979a9699509497509295939492505050565b600060208284031215614afe57600080fd5b81356111988161486d565b600060208284031215614b1b57600080fd5b5035919050565b60008060408385031215614b3557600080fd5b823591506020830135614b478161486d565b809150509250929050565b600060a08284031215614b6457600080fd5b50919050565b60006101608284031215614b6457600080fd5b60008060006101808486031215614b9357600080fd5b614b9d8585614b6a565b925061016084013567ffffffffffffffff80821115614bbb57600080fd5b818601915086601f830112614bcf57600080fd5b813581811115614bde57600080fd5b876020606083028501011115614bf357600080fd5b6020830194508093505050509250925092565b60008060408385031215614c1957600080fd5b8235614c248161486d565b946020939093013593505050565b60008060408385031215614c4557600080fd5b50508035926020909101359150565b60006101608284031215614c6757600080fd5b6111988383614b6a565b634e487b7160e01b600052602160045260246000fd5b60028110610a6557634e487b7160e01b600052602160045260246000fd5b81516040820190614cb581614c87565b808352506001600160a01b03602084015116602083015292915050565b60008060008060008060006080888a031215614ced57600080fd5b873567ffffffffffffffff80821115614d0557600080fd5b614d118b838c016148af565b909950975060208a0135915080821115614d2a57600080fd5b614d368b838c016148af565b909750955060408a0135915080821115614d4f57600080fd5b614d5b8b838c016148af565b909550935060608a0135915080821115614d7457600080fd5b50614d818a828b0161489e565b91505092959891949750929550565b634e487b7160e01b600052603260045260246000fd5b6000808335601e19843603018112614dbd57600080fd5b83018035915067ffffffffffffffff821115614dd857600080fd5b6020019150600581901b360382131561172857600080fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415614e1a57614e1a614df0565b5060010190565b634e487b7160e01b600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715614e6857634e487b7160e01b600052604160045260246000fd5b60405290565b60028110610a6557600080fd5b600060608284031215614e8d57600080fd5b614e95614e37565b90508135614ea281614e6e565b80825250602082013560208201526040820135604082015292915050565b600060a08284031215614ed257600080fd5b614eda614e37565b8235614ee58161486d565b81526020830135614ef58161486d565b6020820152614f078460408501614e7b565b60408201529392505050565b600060608284031215614f2557600080fd5b6040516060810181811067ffffffffffffffff82111715614f5657634e487b7160e01b600052604160045260246000fd5b604052823560ff81168114614f6a57600080fd5b8152602083810135908201526040928301359281019290925250919050565b600060208284031215614f9b57600080fd5b5051919050565b600060608284031215614fb457600080fd5b614fbc614e37565b90508135614fc98161486d565b81526020820135614fd98161486d565b806020830152506040820135604082015292915050565b6000610160828403121561500357600080fd5b60405160a0810181811067ffffffffffffffff8211171561503457634e487b7160e01b600052604160045260246000fd5b60405282358152602083013561504981614e6e565b602082015261505b8460408501614fa2565b604082015261506d8460a08501614fa2565b6060820152615080846101008501614e7b565b60808201529392505050565b80356150978161486d565b6001600160a01b0390811683526020820135906150b38261486d565b166020830152604090810135910152565b6000610180820190508382528235602083015260208301356150e581614e6e565b6150ee81614c87565b80604084015250615105606083016040850161508c565b61511560c0830160a0850161508c565b61012061010084013561512781614e6e565b61513081614c87565b81840152830135610140808401919091529092013561016090910152919050565b60006020828403121561516357600080fd5b813561119881614e6e565b60006060828403121561518057600080fd5b6111988383614e7b565b60008282101561519c5761519c614df0565b500390565b60008160001904831182151516156151bb576151bb614df0565b500290565b6000610180820190508382528251602083015260208301516151e181614c87565b6040838101919091528381015180516001600160a01b03908116606086015260208201511660808501529081015160a084015250606083015180516001600160a01b0390811660c085015260208201511660e08401526040810151610100840152506080830151805161525381614c87565b6101208401526020810151610140840152604001516101609092019190915292915050565b8183526000602080850194508260005b858110156152b657813561529b8161486d565b6001600160a01b031687529582019590820190600101615288565b509495945050505050565b6060815260006152d560608301888a615278565b6020838203818501526152e982888a615278565b8481036040860152858152869250810160005b8681101561532a57833561530f81614e6e565b61531881614c87565b825292820192908201906001016152fc565b509a9950505050505050505050565b60408152600061534d604083018688615278565b82810360208401528381527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84111561538557600080fd5b8360051b80866020840137600091016020019081529695505050505050565b60005b838110156153bf5781810151838201526020016153a7565b83811115610e895750506000910152565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516154088160178501602088016153a4565b7f206973206d697373696e6720726f6c652000000000000000000000000000000060179184019182015283516154458160288401602088016153a4565b01602801949350505050565b60208152600082518060208401526154708160408501602087016153a4565b601f01601f19169190910160400192915050565b6000821982111561549757615497614df0565b500190565b6000826154b957634e487b7160e01b600052601260045260246000fd5b500490565b8681526020810186905260c081016154d586614c87565b8560408301528460608301528360808301528260a0830152979650505050505050565b6000825161550a8184602087016153a4565b9190910192915050565b60006020828403121561552657600080fd5b8151801515811461119857600080fd5b7f546f6b656e3a20636f756c64206e6f74207472616e7366657220000000000000815260008551602061556f82601a8601838b016153a4565b7f2066726f6d200000000000000000000000000000000000000000000000000000601a9285019283015286516155aa81838501848b016153a4565b630103a37960e51b92018181019290925285516155cd81602485018985016153a4565b660103a37b5b2b7160cd1b6024939091019283015284516155f481602b85018489016153a4565b91909101602b01979650505050505050565b60008161561557615615614df0565b506000190190565b8481526080810161562d85614c87565b84602083015283604083015282606083015295945050505050565b7f546f6b656e3a20636f756c64206e6f74207472616e736665722000000000000081526000845161568081601a8501602089016153a4565b630103a37960e51b601a9184019182015284516156a481601e8401602089016153a4565b660103a37b5b2b7160cd1b601e929091019182015283516156cc8160258401602088016153a4565b0160250195945050505050565b7f546f6b656e496e666f280000000000000000000000000000000000000000000081526000845161571181600a8501602089016153a4565b80830190507f2c0000000000000000000000000000000000000000000000000000000000000080600a830152855161575081600b850160208a016153a4565b600b920191820152835161576b81600c8401602088016153a4565b7f2900000000000000000000000000000000000000000000000000000000000000600c9290910191820152600d0195945050505050565b634e487b7160e01b600052603160045260246000fdfeb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610348617350726f787941646d696e3a20756e617574686f72697a65642073656e64a2646970667358221220badebb50cc96ecc4d7c364bf224a8d7c089229c1d01abb009e0241a26ff0da2564736f6c63430008090033";
+
+ const FAKE_CREATION_CODE: &str = "608060405234801561001057600080fd5b506000805460ff1916905561582e806200002b6000396000f3fe60806040526004361061032d5760003560e01c80639157921c116101a5578063b2975794116100ec578063d547741f11610095578063dafae4081161006f578063dafae4081461096e578063dff525e11461098e578063e400327c146109ae578063e75235b8146109ce5761033c565b8063d547741f14610901578063d55ed10314610921578063d64af2a61461094e5761033c565b8063cdb67444116100c6578063cdb674441461089c578063cdf64a76146108b4578063d19773d2146108d45761033c565b8063b29757941461082f578063b9c362091461085c578063ca15c8731461087c5761033c565b8063a3912ec81161014e578063affed0e011610128578063affed0e0146107cc578063b1a2567e146107e2578063b1d08a03146108025761033c565b8063a3912ec81461033a578063ab7965661461077f578063ac78dfe8146107ac5761033c565b8063994390891161017f57806399439089146107155780639dcc4da314610735578063a217fddf1461076a5761033c565b80639157921c1461068f57806391d14854146106af57806393c5678f146106f55761033c565b806336568abe116102745780635c975abb1161021d5780637de5dedd116101f75780637de5dedd146106115780638456cb59146106265780638f34e3471461063b5780639010d07c1461066f5761033c565b80635c975abb146105ac5780636932be98146105c45780636c1ce670146105f15761033c565b80634d0d66731161024e5780634d0d66731461052f5780634d493f4e1461054f57806359122f6b1461057f5761033c565b806336568abe146104e75780633f4ba83a146105075780634b14557e1461051c5761033c565b80631d4a7210116102d65780632f2ff15d116102b05780632f2ff15d1461049b578063302d12db146104bb5780633644e515146104d25761033c565b80631d4a721014610428578063248a9ca3146104555780632dfdf0b5146104855761033c565b8063180ff1e911610307578063180ff1e9146103d55780631a8e55b0146103e85780631b6e7594146104085761033c565b806301ffc9a71461034457806317ce2dd41461037957806317fcb39b1461039d5761033c565b3661033c5761033a6109e6565b005b61033a6109e6565b34801561035057600080fd5b5061036461035f366004614843565b610a69565b60405190151581526020015b60405180910390f35b34801561038557600080fd5b5061038f60755481565b604051908152602001610370565b3480156103a957600080fd5b506074546103bd906001600160a01b031681565b6040516001600160a01b039091168152602001610370565b61033a6103e33660046148f4565b610aad565b3480156103f457600080fd5b5061033a6104033660046149e6565b610dbd565b34801561041457600080fd5b5061033a610423366004614a52565b610e8f565b34801561043457600080fd5b5061038f610443366004614aec565b603e6020526000908152604090205481565b34801561046157600080fd5b5061038f610470366004614b09565b60009081526072602052604090206001015490565b34801561049157600080fd5b5061038f60765481565b3480156104a757600080fd5b5061033a6104b6366004614b22565b610f64565b3480156104c757600080fd5b5061038f620f424081565b3480156104de57600080fd5b5060775461038f565b3480156104f357600080fd5b5061033a610502366004614b22565b610f8f565b34801561051357600080fd5b5061033a61101b565b61033a61052a366004614b52565b611083565b34801561053b57600080fd5b5061036461054a366004614b7d565b6110e1565b34801561055b57600080fd5b5061036461056a366004614b09565b607a6020526000908152604090205460ff1681565b34801561058b57600080fd5b5061038f61059a366004614aec565b603a6020526000908152604090205481565b3480156105b857600080fd5b5060005460ff16610364565b3480156105d057600080fd5b5061038f6105df366004614b09565b60796020526000908152604090205481565b3480156105fd57600080fd5b5061036461060c366004614c06565b61118c565b34801561061d57600080fd5b5061038f61119f565b34801561063257600080fd5b5061033a611234565b34801561064757600080fd5b5061038f7f5e5712e902fff5e704bc4d506ad976718319e019e9d2a872528a01a85db433e481565b34801561067b57600080fd5b506103bd61068a366004614c32565b61129c565b34801561069b57600080fd5b5061033a6106aa366004614c54565b6112b4565b3480156106bb57600080fd5b506103646106ca366004614b22565b60009182526072602090815260408084206001600160a01b0393909316845291905290205460ff1690565b34801561070157600080fd5b5061033a6107103660046149e6565b6115ca565b34801561072157600080fd5b506003546103bd906001600160a01b031681565b34801561074157600080fd5b50610755610750366004614c32565b611696565b60408051928352602083019190915201610370565b34801561077657600080fd5b5061038f600081565b34801561078b57600080fd5b5061038f61079a366004614aec565b603c6020526000908152604090205481565b3480156107b857600080fd5b506103646107c7366004614b09565b61172f565b3480156107d857600080fd5b5061038f60045481565b3480156107ee57600080fd5b5061033a6107fd3660046149e6565b6117ce565b34801561080e57600080fd5b5061038f61081d366004614aec565b60396020526000908152604090205481565b34801561083b57600080fd5b5061084f61084a366004614aec565b61189a565b6040516103709190614ca5565b34801561086857600080fd5b50610755610877366004614c32565b611992565b34801561088857600080fd5b5061038f610897366004614b09565b611a17565b3480156108a857600080fd5b50603754603854610755565b3480156108c057600080fd5b5061033a6108cf366004614aec565b611a2e565b3480156108e057600080fd5b5061038f6108ef366004614aec565b603b6020526000908152604090205481565b34801561090d57600080fd5b5061033a61091c366004614b22565b611a97565b34801561092d57600080fd5b5061038f61093c366004614aec565b603d6020526000908152604090205481565b34801561095a57600080fd5b5061033a610969366004614aec565b611abd565b34801561097a57600080fd5b50610364610989366004614b09565b611b26565b34801561099a57600080fd5b5061033a6109a9366004614cd2565b611bbd565b3480156109ba57600080fd5b5061033a6109c93660046149e6565b611cc7565b3480156109da57600080fd5b50600154600254610755565b60005460ff1615610a315760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064015b60405180910390fd5b6074546001600160a01b03163314610a6757610a4b614802565b338152604080820151349101528051610a65908290611d93565b505b565b60006001600160e01b031982167f5a05180f000000000000000000000000000000000000000000000000000000001480610aa75750610aa78261210a565b92915050565b607154610100900460ff16610ac85760715460ff1615610acc565b303b155b610b3e5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610a28565b607154610100900460ff16158015610b60576071805461ffff19166101011790555b610b6b60008d612171565b6075899055610b798b61217b565b610b828a6121dd565b610c29604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f159f52c1e3a2b6a6aad3950adf713516211484e0516dad685ea662a094b7c43b918101919091527fad7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a560608201524660808201523060a082015260c00160408051601f198184030181529190528051602090910120607755565b610c338887612238565b5050610c3f87876122f8565b5050610c496123d3565b6000610c558680614da6565b90501115610d1657610c7e610c6a8680614da6565b610c776020890189614da6565b8787612467565b610ca4610c8b8680614da6565b8660005b602002810190610c9f9190614da6565b612666565b610cca610cb18680614da6565b8660015b602002810190610cc59190614da6565b612779565b610cf0610cd78680614da6565b8660025b602002810190610ceb9190614da6565b61288c565b610d16610cfd8680614da6565b8660035b602002810190610d119190614da6565b612a30565b60005b610d266040870187614da6565b9050811015610d9c57610d8a7f5e5712e902fff5e704bc4d506ad976718319e019e9d2a872528a01a85db433e4610d606040890189614da6565b84818110610d7057610d70614d90565b9050602002016020810190610d859190614aec565b612b43565b80610d9481614e06565b915050610d19565b508015610daf576071805461ff00191690555b505050505050505050505050565b6000805160206157b9833981519152546001600160a01b03163314610e1d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b82610e7d5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e8984848484612779565b50505050565b6000805160206157b9833981519152546001600160a01b03163314610eef5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b84610f4e5760405162461bcd60e51b815260206004820152602960248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220656d60448201526870747920617272617960b81b6064820152608401610a28565b610f5c868686868686612467565b505050505050565b600082815260726020526040902060010154610f808133612b65565b610f8a8383612b43565b505050565b6001600160a01b038116331461100d5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c6600000000000000000000000000000000006064820152608401610a28565b6110178282612be5565b5050565b6000805160206157b9833981519152546001600160a01b0316331461107b5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a67612c07565b60005460ff16156110c95760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610a28565b610a656110db36839003830183614ec0565b33611d93565b6000805460ff16156111285760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610a28565b611184848484808060200260200160405190810160405280939291908181526020016000905b8282101561117a5761116b60608302860136819003810190614f13565b8152602001906001019061114e565b5050505050612ca3565b949350505050565b600061119883836133bc565b9392505050565b600061122f600360009054906101000a90046001600160a01b03166001600160a01b031663926323d56040518163ffffffff1660e01b815260040160206040518083038186803b1580156111f257600080fd5b505afa158015611206573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061122a9190614f89565b613480565b905090565b6000805160206157b9833981519152546001600160a01b031633146112945760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a676134b6565b60008281526073602052604081206111989083613531565b7f5e5712e902fff5e704bc4d506ad976718319e019e9d2a872528a01a85db433e46112df8133612b65565b60006112f86112f336859003850185614ff0565b61353d565b905061130c6112f336859003850185614ff0565b8335600090815260796020526040902054146113765760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c696420726563656044820152621a5c1d60ea1b6064820152608401610a28565b82356000908152607a602052604090205460ff166113fc5760405162461bcd60e51b815260206004820152603160248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220617060448201527f70726f766564207769746864726177616c0000000000000000000000000000006064820152608401610a28565b82356000908152607a602052604090819020805460ff19169055517fd639511b37b3b002cca6cfe6bca0d833945a5af5a045578a0627fc43b79b26309061144690839086906150c4565b60405180910390a160006114606080850160608601614aec565b9050600061147661012086016101008701615151565b600181111561148757611487614c71565b141561154f5760006114a2368690038601610100870161516e565b6001600160a01b0383166000908152603b60205260409020549091506114ce90610140870135906135c6565b604082015260006114e8368790038701610100880161516e565b60408301519091506114ff9061014088013561518a565b604082015260745461151f908390339086906001600160a01b03166135e0565b6115486115326060880160408901614aec565b60745483919086906001600160a01b03166135e0565b505061158b565b61158b6115626060860160408701614aec565b60745483906001600160a01b03166115833689900389016101008a0161516e565b9291906135e0565b7f21e88e956aa3e086f6388e899965cef814688f99ad8bb29b08d396571016372d82856040516115bc9291906150c4565b60405180910390a150505050565b6000805160206157b9833981519152546001600160a01b0316331461162a5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b8261168a5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e8984848484612666565b6000806116b86000805160206157b9833981519152546001600160a01b031690565b6001600160a01b0316336001600160a01b0316146117115760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b61171b84846122f8565b90925090506117286123d3565b9250929050565b6003546040805163926323d560e01b815290516000926001600160a01b03169163926323d5916004808301926020929190829003018186803b15801561177457600080fd5b505afa158015611788573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ac9190614f89565b6037546117b991906151a1565b6038546117c690846151a1565b101592915050565b6000805160206157b9833981519152546001600160a01b0316331461182e5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b8261188e5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e898484848461288c565b60408051808201909152600080825260208201526001600160a01b0382166000908152607860205260409081902081518083019092528054829060ff1660018111156118e8576118e8614c71565b60018111156118f9576118f9614c71565b815290546001600160a01b036101009091048116602092830152908201519192501661198d5760405162461bcd60e51b815260206004820152602560248201527f4d61696e636861696e4761746577617956323a20756e737570706f727465642060448201527f746f6b656e0000000000000000000000000000000000000000000000000000006064820152608401610a28565b919050565b6000806119b46000805160206157b9833981519152546001600160a01b031690565b6001600160a01b0316336001600160a01b031614611a0d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b61171b8484612238565b6000818152607360205260408120610aa790613a13565b6000805160206157b9833981519152546001600160a01b03163314611a8e5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a65816121dd565b600082815260726020526040902060010154611ab38133612b65565b610f8a8383612be5565b6000805160206157b9833981519152546001600160a01b03163314611b1d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a658161217b565b6003546040805163926323d560e01b815290516000926001600160a01b03169163926323d5916004808301926020929190829003018186803b158015611b6b57600080fd5b505afa158015611b7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba39190614f89565b600154611bb091906151a1565b6002546117c690846151a1565b6000805160206157b9833981519152546001600160a01b03163314611c1d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b85611c7c5760405162461bcd60e51b815260206004820152602960248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220656d60448201526870747920617272617960b81b6064820152608401610a28565b611c8a878787878787612467565b611c978787836000610c8f565b611ca48787836001610cb5565b611cb18787836002610cdb565b611cbe8787836003610d01565b50505050505050565b6000805160206157b9833981519152546001600160a01b03163314611d275760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b82611d875760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e8984848484612a30565b604080518082018252600080825260208201526074549184015190916001600160a01b031690611dc290613a1d565b60208401516001600160a01b0316611ee1573484604001516040015114611e375760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c69642072657175604482015262195cdd60ea1b6064820152608401610a28565b611e408161189a565b6040850151519092506001811115611e5a57611e5a614c71565b82516001811115611e6d57611e6d614c71565b14611ecd5760405162461bcd60e51b815260206004820152602a60248201527f4d61696e636861696e4761746577617956323a20696e76616c696420746f6b656044820152691b881cdd185b99185c9960b21b6064820152608401610a28565b6001600160a01b0381166020850152612087565b3415611f3b5760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c69642072657175604482015262195cdd60ea1b6064820152608401610a28565b611f48846020015161189a565b6040850151519092506001811115611f6257611f62614c71565b82516001811115611f7557611f75614c71565b14611fd55760405162461bcd60e51b815260206004820152602a60248201527f4d61696e636861696e4761746577617956323a20696e76616c696420746f6b656044820152691b881cdd185b99185c9960b21b6064820152608401610a28565b60208401516040850151611fec9185903090613ac7565b83602001516001600160a01b0316816001600160a01b031614156120875760408481015181015190517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101919091526001600160a01b03821690632e1a7d4d90602401600060405180830381600087803b15801561206e57600080fd5b505af1158015612082573d6000803e3d6000fd5b505050505b607680546000918261209883614e06565b91905055905060006120bf858386602001516075548a613ce190949392919063ffffffff16565b90507fd7b25068d9dc8d00765254cfb7f5070f98d263c8d68931d937c7362fa738048b6120eb8261353d565b826040516120fa9291906151c0565b60405180910390a1505050505050565b60006001600160e01b031982167f7965db0b000000000000000000000000000000000000000000000000000000001480610aa757507f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b0319831614610aa7565b6110178282612b43565b6074805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527f9d2334c23be647e994f27a72c5eee42a43d5bdcfe15bb88e939103c2b114cbaf906020015b60405180910390a150565b6003805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527fef40dc07567635f84f5edbd2f8dbc16b40d9d282dd8e7e6f4ff58236b6836169906020016121d2565b6000808284111561228b5760405162461bcd60e51b815260206004820152601c60248201527f4761746577617956323a20696e76616c6964207468726573686f6c64000000006044820152606401610a28565b505060018054600280549285905583905560048054919291849186919060006122b383614e06565b9091555060408051868152602081018690527f976f8a9c5bdf8248dec172376d6e2b80a8e3df2f0328e381c6db8e1cf138c0f891015b60405180910390a49250929050565b600080828411156123715760405162461bcd60e51b815260206004820152602760248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c696420746860448201527f726573686f6c64000000000000000000000000000000000000000000000000006064820152608401610a28565b5050603780546038805492859055839055600480549192918491869190600061239983614e06565b9091555060408051868152602081018690527f31312c97b89cc751b832d98fd459b967a2c3eef3b49757d1cf5ebaa12bb6eee191016122e9565b6002546037546123e391906151a1565b6038546001546123f391906151a1565b1115610a675760405162461bcd60e51b815260206004820152602860248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c696420746860448201527f726573686f6c64730000000000000000000000000000000000000000000000006064820152608401610a28565b848314801561247557508481145b6124e75760405162461bcd60e51b815260206004820152602860248201527f4d61696e636861696e4761746577617956323a20696e76616c6964206172726160448201527f79206c656e6774680000000000000000000000000000000000000000000000006064820152608401610a28565b60005b8581101561262c5784848281811061250457612504614d90565b90506020020160208101906125199190614aec565b6078600089898581811061252f5761252f614d90565b90506020020160208101906125449190614aec565b6001600160a01b039081168252602082019290925260400160002080547fffffffffffffffffffffff0000000000000000000000000000000000000000ff1661010093909216929092021790558282828181106125a3576125a3614d90565b90506020020160208101906125b89190615151565b607860008989858181106125ce576125ce614d90565b90506020020160208101906125e39190614aec565b6001600160a01b031681526020810191909152604001600020805460ff19166001838181111561261557612615614c71565b02179055508061262481614e06565b9150506124ea565b507fa4f03cc9c0e0aeb5b71b4ec800702753f65748c2cf3064695ba8e8b46be704448686868686866040516120fa969594939291906152c1565b8281146126c85760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b83811015612743578282828181106126e5576126e5614d90565b905060200201356039600087878581811061270257612702614d90565b90506020020160208101906127179190614aec565b6001600160a01b031681526020810191909152604001600020558061273b81614e06565b9150506126cb565b507f80bc635c452ae67f12f9b6f12ad4daa6dbbc04eeb9ebb87d354ce10c0e210dc0848484846040516115bc9493929190615339565b8281146127db5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b83811015612856578282828181106127f8576127f8614d90565b90506020020135603a600087878581811061281557612815614d90565b905060200201602081019061282a9190614aec565b6001600160a01b031681526020810191909152604001600020558061284e81614e06565b9150506127de565b507f64557254143204d91ba2d95acb9fda1e5fea55f77efd028685765bc1e94dd4b5848484846040516115bc9493929190615339565b8281146128ee5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b838110156129fa57620f424083838381811061290f5761290f614d90565b90506020020135111561298a5760405162461bcd60e51b815260206004820152602860248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c696420706560448201527f7263656e746167650000000000000000000000000000000000000000000000006064820152608401610a28565b82828281811061299c5761299c614d90565b90506020020135603b60008787858181106129b9576129b9614d90565b90506020020160208101906129ce9190614aec565b6001600160a01b03168152602081019190915260400160002055806129f281614e06565b9150506128f1565b507fb05f5de88ae0294ebb6f67c5af2fcbbd593cc6bdfe543e2869794a4c8ce3ea50848484846040516115bc9493929190615339565b828114612a925760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b83811015612b0d57828282818110612aaf57612aaf614d90565b90506020020135603c6000878785818110612acc57612acc614d90565b9050602002016020810190612ae19190614aec565b6001600160a01b0316815260208101919091526040016000205580612b0581614e06565b915050612a95565b507fb5d2963614d72181b4df1f993d45b83edf42fa19710f0204217ba1b3e183bb73848484846040516115bc9493929190615339565b612b4d8282613db6565b6000828152607360205260409020610f8a9082613e58565b60008281526072602090815260408083206001600160a01b038516845290915290205460ff1661101757612ba3816001600160a01b03166014613e6d565b612bae836020613e6d565b604051602001612bbf9291906153d0565b60408051601f198184030181529082905262461bcd60e51b8252610a2891600401615451565b612bef828261404e565b6000828152607360205260409020610f8a90826140d1565b60005460ff16612c595760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610a28565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6000823561014084013582612cbe6080870160608801614aec565b9050612cdb612cd6368890038801610100890161516e565b613a1d565b6001612ced6040880160208901615151565b6001811115612cfe57612cfe614c71565b14612d715760405162461bcd60e51b815260206004820152602860248201527f4d61696e636861696e4761746577617956323a20696e76616c6964207265636560448201527f697074206b696e640000000000000000000000000000000000000000000000006064820152608401610a28565b60808601354614612de95760405162461bcd60e51b8152602060048201526024808201527f4d61696e636861696e4761746577617956323a20696e76616c6964206368616960448201527f6e206964000000000000000000000000000000000000000000000000000000006064820152608401610a28565b6000612dfe61084a6080890160608a01614aec565b9050612e1261012088016101008901615151565b6001811115612e2357612e23614c71565b81516001811115612e3657612e36614c71565b148015612e675750612e4e60e0880160c08901614aec565b6001600160a01b031681602001516001600160a01b0316145b612ebf5760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c696420726563656044820152621a5c1d60ea1b6064820152608401610a28565b60008481526079602052604090205415612f415760405162461bcd60e51b815260206004820152603260248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220707260448201527f6f636573736564207769746864726177616c00000000000000000000000000006064820152608401610a28565b6001612f5561012089016101008a01615151565b6001811115612f6657612f66614c71565b1480612f795750612f7782846133bc565b155b612feb5760405162461bcd60e51b815260206004820152603260248201527f4d61696e636861696e4761746577617956323a2072656163686564206461696c60448201527f79207769746864726177616c206c696d697400000000000000000000000000006064820152608401610a28565b6000612fff6112f3368a90038a018a614ff0565b9050600061300f607754836140e6565b6003549091506001600160a01b0316600061303d6130356101208d016101008e01615151565b878985614142565b60408051606081018252600080825260208201819052918101829052919b50919250819081906000805b8f5181101561323c578f818151811061308257613082614d90565b6020908102919091018101518051818301516040808401518151600081529586018083528f905260ff9093169085015260608401526080830152935060019060a0016020604051602081039080840390855afa1580156130e6573d6000803e3d6000fd5b505050602060405103519450846001600160a01b0316846001600160a01b0316106131795760405162461bcd60e51b815260206004820152602160248201527f4d61696e636861696e4761746577617956323a20696e76616c6964206f72646560448201527f72000000000000000000000000000000000000000000000000000000000000006064820152608401610a28565b6040517f953865650000000000000000000000000000000000000000000000000000000081526001600160a01b03808716600483015286955089169063953865659060240160206040518083038186803b1580156131d657600080fd5b505afa1580156131ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061320e9190614f89565b6132189083615484565b915086821061322a576001955061323c565b8061323481614e06565b915050613067565b50846132b05760405162461bcd60e51b815260206004820152603660248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220696e60448201527f73756666696369656e7420766f746520776569676874000000000000000000006064820152608401610a28565b50505060008a81526079602052604090208690555050881561332c576000888152607a602052604090819020805460ff19166001179055517f89e52969465b1f1866fc5d46fd62de953962e9cb33552443cd999eba05bd20dc906133179086908e906150c4565b60405180910390a15050505050505050610aa7565b6133368688614233565b61337561334960608d0160408e01614aec565b87607460009054906101000a90046001600160a01b03168e61010001803603810190611583919061516e565b7f21e88e956aa3e086f6388e899965cef814688f99ad8bb29b08d396571016372d848c6040516133a69291906150c4565b60405180910390a1505050505050505092915050565b6001600160a01b0382166000908152603a602052604081205482106133e357506000610aa7565b60006133f2620151804261549c565b6001600160a01b0385166000908152603e60205260409020549091508111156134385750506001600160a01b0382166000908152603c6020526040902054811015610aa7565b6001600160a01b0384166000908152603d602052604090205461345c908490615484565b6001600160a01b0385166000908152603c602052604090205411159150610aa79050565b600060025460016002548460015461349891906151a1565b6134a29190615484565b6134ac919061518a565b610aa7919061549c565b60005460ff16156134fc5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610a28565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612c863390565b600061119883836142c3565b60007fb9d1fe7c9deeec5dc90a2f47ff1684239519f2545b2228d3d91fb27df3189eea60001b8260000151836020015161357a85604001516142ed565b61358786606001516142ed565b6135948760800151614350565b6040516020016135a9969594939291906154be565b604051602081830303815290604052805190602001209050919050565b6000620f42406135d683856151a1565b611198919061549c565b6000816001600160a01b0316836001600160a01b031614156136905760408086015190516001600160a01b0386169180156108fc02916000818181858888f1935050505061368b57816001600160a01b031663d0e30db086604001516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561366757600080fd5b505af115801561367b573d6000803e3d6000fd5b505050505061368b858585614393565b613a0c565b6000855160018111156136a5576136a5614c71565b1415613866576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038516906370a082319060240160206040518083038186803b15801561370657600080fd5b505afa15801561371a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061373e9190614f89565b9050856040015181101561385557836001600160a01b03166340c10f193083896040015161376c919061518a565b6040516001600160a01b03909216602483015260448201526064016040516020818303038152906040529060e01b6020820180516001600160e01b0383818316178352505050506040516137c091906154f8565b6000604051808303816000865af19150503d80600081146137fd576040519150601f19603f3d011682016040523d82523d6000602084013e613802565b606091505b505080925050816138555760405162461bcd60e51b815260206004820152601b60248201527f546f6b656e3a204552433230206d696e74696e67206661696c656400000000006044820152606401610a28565b613860868686614393565b50613a0c565b60018551600181111561387b5761387b614c71565b141561399e5761389083858760200151614437565b61368b57602085810151604080516001600160a01b038881166024830152604480830194909452825180830390940184526064909101825292820180516001600160e01b03167f40c10f1900000000000000000000000000000000000000000000000000000000179052519185169161390991906154f8565b6000604051808303816000865af19150503d8060008114613946576040519150601f19603f3d011682016040523d82523d6000602084013e61394b565b606091505b5050809150508061368b5760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e3a20455243373231206d696e74696e67206661696c6564000000006044820152606401610a28565b60405162461bcd60e51b815260206004820152602160248201527f546f6b656e3a20756e737570706f7274656420746f6b656e207374616e64617260448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610a28565b5050505050565b6000610aa7825490565b600081516001811115613a3257613a32614c71565b148015613a43575060008160400151115b8015613a5157506020810151155b80613a7b5750600181516001811115613a6c57613a6c614c71565b148015613a7b57506040810151155b610a655760405162461bcd60e51b815260206004820152601360248201527f546f6b656e3a20696e76616c696420696e666f000000000000000000000000006044820152606401610a28565b600060608186516001811115613adf57613adf614c71565b1415613bbd5760408681015181516001600160a01b038881166024830152878116604483015260648083019390935283518083039093018352608490910183526020820180516001600160e01b03166323b872dd60e01b179052915191851691613b4991906154f8565b6000604051808303816000865af19150503d8060008114613b86576040519150601f19603f3d011682016040523d82523d6000602084013e613b8b565b606091505b509092509050818015613bb6575080511580613bb6575080806020019051810190613bb69190615514565b9150613c84565b600186516001811115613bd257613bd2614c71565b141561399e57602086810151604080516001600160a01b0389811660248301528881166044830152606480830194909452825180830390940184526084909101825292820180516001600160e01b03166323b872dd60e01b1790525191851691613c3c91906154f8565b6000604051808303816000865af19150503d8060008114613c79576040519150601f19603f3d011682016040523d82523d6000602084013e613c7e565b606091505b50909250505b81610f5c57613c92866144e2565b613ca6866001600160a01b03166014613e6d565b613cba866001600160a01b03166014613e6d565b613cce866001600160a01b03166014613e6d565b604051602001612bbf9493929190615536565b613d516040805160a08101825260008082526020808301829052835160608082018652838252818301849052818601849052848601919091528451808201865283815280830184905280860184905281850152845190810185528281529081018290529283015290608082015290565b83815260006020820181905250604080820180516001600160a01b039788169052602080890151825190891690820152905146908301528751606084018051918916909152805195909716940193909352935182015292909201516080820152919050565b60008281526072602090815260408083206001600160a01b038516845290915290205460ff166110175760008281526072602090815260408083206001600160a01b03851684529091529020805460ff19166001179055613e143390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000611198836001600160a01b03841661454f565b60606000613e7c8360026151a1565b613e87906002615484565b67ffffffffffffffff811115613e9f57613e9f614e21565b6040519080825280601f01601f191660200182016040528015613ec9576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110613f0057613f00614d90565b60200101906001600160f81b031916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110613f4b57613f4b614d90565b60200101906001600160f81b031916908160001a9053506000613f6f8460026151a1565b613f7a906001615484565b90505b6001811115613fff577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110613fbb57613fbb614d90565b1a60f81b828281518110613fd157613fd1614d90565b60200101906001600160f81b031916908160001a90535060049490941c93613ff881615606565b9050613f7d565b5083156111985760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610a28565b60008281526072602090815260408083206001600160a01b038516845290915290205460ff16156110175760008281526072602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000611198836001600160a01b03841661459e565b604080517f19010000000000000000000000000000000000000000000000000000000000006020808301919091526022820185905260428083018590528351808403909101815260629092019092528051910120600090611198565b6000806000836001600160a01b031663926323d56040518163ffffffff1660e01b815260040160206040518083038186803b15801561418057600080fd5b505afa158015614194573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141b89190614f89565b90506141c381613480565b925060008760018111156141d9576141d9614c71565b1415614229576001600160a01b038616600090815260396020526040902054851061420a5761420781614691565b92505b6001600160a01b0386166000908152603a602052604090205485101591505b5094509492505050565b6000614242620151804261549c565b6001600160a01b0384166000908152603e6020526040902054909150811115614291576001600160a01b03929092166000908152603e6020908152604080832094909455603d90529190912055565b6001600160a01b0383166000908152603d6020526040812080548492906142b9908490615484565b9091555050505050565b60008260000182815481106142da576142da614d90565b9060005260206000200154905092915050565b805160208083015160408085015190516000946135a9947f353bdd8d69b9e3185b3972e08b03845c0c14a21a390215302776a7a34b0e87649491939192019384526001600160a01b03928316602085015291166040830152606082015260800190565b805160208083015160408085015190516000946135a9947f1e2b74b2a792d5c0f0b6e59b037fa9d43d84fbb759337f0112fcc15ca414fc8d94919391920161561d565b600080845160018111156143a9576143a9614c71565b14156143c5576143be828486604001516146a9565b90506143ef565b6001845160018111156143da576143da614c71565b141561399e576143be82848660200151614437565b80610e89576143fd846144e2565b614411846001600160a01b03166014613e6d565b614425846001600160a01b03166014613e6d565b604051602001612bbf93929190615648565b604080513060248201526001600160a01b038481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092861691614495916154f8565b6000604051808303816000865af19150503d80600081146144d2576040519150601f19603f3d011682016040523d82523d6000602084013e6144d7565b606091505b509095945050505050565b606061450d826000015160018111156144fd576144fd614c71565b6001600160a01b03166001613e6d565b61451a8360200151614795565b6145278460400151614795565b604051602001614539939291906156d9565b6040516020818303038152906040529050919050565b600081815260018301602052604081205461459657508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610aa7565b506000610aa7565b600081815260018301602052604081205480156146875760006145c260018361518a565b85549091506000906145d69060019061518a565b905081811461463b5760008660000182815481106145f6576145f6614d90565b906000526020600020015490508087600001848154811061461957614619614d90565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061464c5761464c6157a2565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610aa7565b6000915050610aa7565b600060385460016038548460375461349891906151a1565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b03167fa9059cbb0000000000000000000000000000000000000000000000000000000017905291516000926060929087169161471f91906154f8565b6000604051808303816000865af19150503d806000811461475c576040519150601f19603f3d011682016040523d82523d6000602084013e614761565b606091505b50909250905081801561478c57508051158061478c57508080602001905181019061478c9190615514565b95945050505050565b6060816147d557505060408051808201909152600481527f3078303000000000000000000000000000000000000000000000000000000000602082015290565b8160005b81156147f857806147e981614e06565b915050600882901c91506147d9565b6111848482613e6d565b604080516060810182526000808252602082015290810161483e6040805160608101909152806000815260200160008152602001600081525090565b905290565b60006020828403121561485557600080fd5b81356001600160e01b03198116811461119857600080fd5b6001600160a01b0381168114610a6557600080fd5b803561198d8161486d565b8060608101831015610aa757600080fd5b8060808101831015610aa757600080fd5b60008083601f8401126148c157600080fd5b50813567ffffffffffffffff8111156148d957600080fd5b6020830191508360208260051b850101111561172857600080fd5b60008060008060008060008060008060006101408c8e03121561491657600080fd5b61491f8c614882565b9a5061492d60208d01614882565b995061493b60408d01614882565b985060608c0135975060808c0135965060a08c0135955060c08c0135945067ffffffffffffffff8060e08e0135111561497357600080fd5b6149838e60e08f01358f0161488d565b9450806101008e0135111561499757600080fd5b6149a88e6101008f01358f0161489e565b9350806101208e013511156149bc57600080fd5b506149ce8d6101208e01358e016148af565b81935080925050509295989b509295989b9093969950565b600080600080604085870312156149fc57600080fd5b843567ffffffffffffffff80821115614a1457600080fd5b614a20888389016148af565b90965094506020870135915080821115614a3957600080fd5b50614a46878288016148af565b95989497509550505050565b60008060008060008060608789031215614a6b57600080fd5b863567ffffffffffffffff80821115614a8357600080fd5b614a8f8a838b016148af565b90985096506020890135915080821115614aa857600080fd5b614ab48a838b016148af565b90965094506040890135915080821115614acd57600080fd5b50614ada89828a016148af565b979a9699509497509295939492505050565b600060208284031215614afe57600080fd5b81356111988161486d565b600060208284031215614b1b57600080fd5b5035919050565b60008060408385031215614b3557600080fd5b823591506020830135614b478161486d565b809150509250929050565b600060a08284031215614b6457600080fd5b50919050565b60006101608284031215614b6457600080fd5b60008060006101808486031215614b9357600080fd5b614b9d8585614b6a565b925061016084013567ffffffffffffffff80821115614bbb57600080fd5b818601915086601f830112614bcf57600080fd5b813581811115614bde57600080fd5b876020606083028501011115614bf357600080fd5b6020830194508093505050509250925092565b60008060408385031215614c1957600080fd5b8235614c248161486d565b946020939093013593505050565b60008060408385031215614c4557600080fd5b50508035926020909101359150565b60006101608284031215614c6757600080fd5b6111988383614b6a565b634e487b7160e01b600052602160045260246000fd5b60028110610a6557634e487b7160e01b600052602160045260246000fd5b81516040820190614cb581614c87565b808352506001600160a01b03602084015116602083015292915050565b60008060008060008060006080888a031215614ced57600080fd5b873567ffffffffffffffff80821115614d0557600080fd5b614d118b838c016148af565b909950975060208a0135915080821115614d2a57600080fd5b614d368b838c016148af565b909750955060408a0135915080821115614d4f57600080fd5b614d5b8b838c016148af565b909550935060608a0135915080821115614d7457600080fd5b50614d818a828b0161489e565b91505092959891949750929550565b634e487b7160e01b600052603260045260246000fd5b6000808335601e19843603018112614dbd57600080fd5b83018035915067ffffffffffffffff821115614dd857600080fd5b6020019150600581901b360382131561172857600080fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415614e1a57614e1a614df0565b5060010190565b634e487b7160e01b600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715614e6857634e487b7160e01b600052604160045260246000fd5b60405290565b60028110610a6557600080fd5b600060608284031215614e8d57600080fd5b614e95614e37565b90508135614ea281614e6e565b80825250602082013560208201526040820135604082015292915050565b600060a08284031215614ed257600080fd5b614eda614e37565b8235614ee58161486d565b81526020830135614ef58161486d565b6020820152614f078460408501614e7b565b60408201529392505050565b600060608284031215614f2557600080fd5b6040516060810181811067ffffffffffffffff82111715614f5657634e487b7160e01b600052604160045260246000fd5b604052823560ff81168114614f6a57600080fd5b8152602083810135908201526040928301359281019290925250919050565b600060208284031215614f9b57600080fd5b5051919050565b600060608284031215614fb457600080fd5b614fbc614e37565b90508135614fc98161486d565b81526020820135614fd98161486d565b806020830152506040820135604082015292915050565b6000610160828403121561500357600080fd5b60405160a0810181811067ffffffffffffffff8211171561503457634e487b7160e01b600052604160045260246000fd5b60405282358152602083013561504981614e6e565b602082015261505b8460408501614fa2565b604082015261506d8460a08501614fa2565b6060820152615080846101008501614e7b565b60808201529392505050565b80356150978161486d565b6001600160a01b0390811683526020820135906150b38261486d565b166020830152604090810135910152565b6000610180820190508382528235602083015260208301356150e581614e6e565b6150ee81614c87565b80604084015250615105606083016040850161508c565b61511560c0830160a0850161508c565b61012061010084013561512781614e6e565b61513081614c87565b81840152830135610140808401919091529092013561016090910152919050565b60006020828403121561516357600080fd5b813561119881614e6e565b60006060828403121561518057600080fd5b6111988383614e7b565b60008282101561519c5761519c614df0565b500390565b60008160001904831182151516156151bb576151bb614df0565b500290565b6000610180820190508382528251602083015260208301516151e181614c87565b6040838101919091528381015180516001600160a01b03908116606086015260208201511660808501529081015160a084015250606083015180516001600160a01b0390811660c085015260208201511660e08401526040810151610100840152506080830151805161525381614c87565b6101208401526020810151610140840152604001516101609092019190915292915050565b8183526000602080850194508260005b858110156152b657813561529b8161486d565b6001600160a01b031687529582019590820190600101615288565b509495945050505050565b6060815260006152d560608301888a615278565b6020838203818501526152e982888a615278565b8481036040860152858152869250810160005b8681101561532a57833561530f81614e6e565b61531881614c87565b825292820192908201906001016152fc565b509a9950505050505050505050565b60408152600061534d604083018688615278565b82810360208401528381527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84111561538557600080fd5b8360051b80866020840137600091016020019081529695505050505050565b60005b838110156153bf5781810151838201526020016153a7565b83811115610e895750506000910152565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516154088160178501602088016153a4565b7f206973206d697373696e6720726f6c652000000000000000000000000000000060179184019182015283516154458160288401602088016153a4565b01602801949350505050565b60208152600082518060208401526154708160408501602087016153a4565b601f01601f19169190910160400192915050565b6000821982111561549757615497614df0565b500190565b6000826154b957634e487b7160e01b600052601260045260246000fd5b500490565b8681526020810186905260c081016154d586614c87565b8560408301528460608301528360808301528260a0830152979650505050505050565b6000825161550a8184602087016153a4565b9190910192915050565b60006020828403121561552657600080fd5b8151801515811461119857600080fd5b7f546f6b656e3a20636f756c64206e6f74207472616e7366657220000000000000815260008551602061556f82601a8601838b016153a4565b7f2066726f6d200000000000000000000000000000000000000000000000000000601a9285019283015286516155aa81838501848b016153a4565b630103a37960e51b92018181019290925285516155cd81602485018985016153a4565b660103a37b5b2b7160cd1b6024939091019283015284516155f481602b85018489016153a4565b91909101602b01979650505050505050565b60008161561557615615614df0565b506000190190565b8481526080810161562d85614c87565b84602083015283604083015282606083015295945050505050565b7f546f6b656e3a20636f756c64206e6f74207472616e736665722000000000000081526000845161568081601a8501602089016153a4565b630103a37960e51b601a9184019182015284516156a481601e8401602089016153a4565b660103a37b5b2b7160cd1b601e929091019182015283516156cc8160258401602088016153a4565b0160250195945050505050565b7f546f6b656e496e666f280000000000000000000000000000000000000000000081526000845161571181600a8501602089016153a4565b80830190507f2c0000000000000000000000000000000000000000000000000000000000000080600a830152855161575081600b850160208a016153a4565b600b920191820152835161576b81600c8401602088016153a4565b7f2900000000000000000000000000000000000000000000000000000000000000600c9290910191820152600d0195945050505050565b634e487b7160e01b600052603160045260246000fdfeb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610348617350726f787941646d696e3a20756e617574686f72697a65642073656e64a2646970667358221220badebb50cc96ecc4d7c364bf224a8d7c089229c1d01abb009e0241a26ff0da2564736f6c63430008090033";
+}
diff --git a/crates/tweak/src/compatibility.rs b/crates/tweak/src/compatibility.rs
new file mode 100644
index 000000000000..a93135e2e26a
--- /dev/null
+++ b/crates/tweak/src/compatibility.rs
@@ -0,0 +1,355 @@
+//! Compatibility check of tweaking an on-chain contract with a local cloned project.
+//! The local project usually should be created by `forge clone` command.
+//! Users may modify the source code of the cloned project, but the storage layout should remain the
+//! same as the original contract.
+
+use std::collections::BTreeMap;
+
+use foundry_compilers::artifacts::StorageLayout;
+
+use crate::metadata::ClonedProject;
+
+/// Check the tweak compatibility of the project with the given root.
+/// The project is compatible if:
+/// - the project's storage layout is the same as the original contract.
+///
+/// If the project is not compatible, an error is returned.
+pub fn check_storage_compatibility(cloned_project: &ClonedProject) -> eyre::Result<()> {
+ // to check the storage layout compatibility, we need to download the original contract's code
+ // from etherscan and compile.
+ let original_layout = cloned_project.metadata.storage_layout.to_owned();
+ let current_layout = cloned_project
+ .main_artifact()?
+ .storage_layout
+ .to_owned()
+ .expect("storage layout is missing");
+ check_storage_layout_compatibility(&original_layout, ¤t_layout)
+}
+
+/// Check that the current storage layout is compatible with the original storage layout.
+/// Each state variable in the original storage layout should have the same slot in the current
+/// storage layout.
+pub fn check_storage_layout_compatibility(
+ original: &StorageLayout,
+ current: &StorageLayout,
+) -> eyre::Result<()> {
+ // TODO: need a more sophisticated comparison algorithm.
+ // The current implementation is a naive one that only checks the slot and offset of each
+ // storage variables. check storage variables
+ let current_storage_var_map = BTreeMap::from_iter(
+ current.storage.iter().map(|v| (format!("{}:{}", v.contract, v.label), v)),
+ );
+ for original_var in original.storage.iter() {
+ let current_var = current_storage_var_map
+ .get(&format!("{}:{}", original_var.contract, original_var.label));
+ if current_var.is_none() {
+ return Err(eyre::eyre!(
+ "the storage variable {} is missing in the current contract",
+ original_var.label
+ ));
+ }
+ let current_var = current_var.unwrap();
+ // offset, slot, type should be the same
+ if original_var.offset != current_var.offset || original_var.slot != current_var.slot {
+ return Err(eyre::eyre!(
+ "the storage variable {} has different layout in the current contract",
+ original_var.label
+ ));
+ }
+ }
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use foundry_compilers::artifacts::StorageLayout;
+
+ fn load_json_layout(json_str: &str) -> StorageLayout {
+ serde_json::from_str(json_str).unwrap()
+ }
+
+ #[test]
+ pub fn test_same_storage_layout() {
+ let storage = load_json_layout(
+ r#"{
+ "storage": [
+ {
+ "astId": 3,
+ "contract": "contract.sol:C",
+ "label": "data",
+ "offset": 0,
+ "slot": "0",
+ "type": "t_uint256"
+ },
+ {
+ "astId": 5,
+ "contract": "contract.sol:C",
+ "label": "owner",
+ "offset": 0,
+ "slot": "1",
+ "type": "t_address"
+ }
+ ],
+ "types": {
+ "t_address": {
+ "encoding": "inplace",
+ "label": "address",
+ "numberOfBytes": "20"
+ },
+ "t_uint256": {
+ "encoding": "inplace",
+ "label": "uint256",
+ "numberOfBytes": "32"
+ }
+ }
+ }"#,
+ );
+
+ assert!(super::check_storage_layout_compatibility(&storage, &storage).is_ok());
+ }
+
+ #[test]
+ pub fn test_storage_layout_shift() {
+ let original = load_json_layout(
+ r#"{
+ "storage": [
+ {
+ "astId": 3,
+ "contract": "contract.sol:C",
+ "label": "data",
+ "offset": 0,
+ "slot": "0",
+ "type": "t_uint256"
+ }
+ ],
+ "types": {
+ "t_uint256": {
+ "encoding": "inplace",
+ "label": "uint256",
+ "numberOfBytes": "32"
+ }
+ }
+ }"#,
+ );
+ let current = load_json_layout(
+ r#"{
+ "storage": [
+ {
+ "astId": 3,
+ "contract": "contract.sol:C",
+ "label": "data",
+ "offset": 0,
+ "slot": "1",
+ "type": "t_uint256"
+ }
+ ],
+ "types": {
+ "t_uint256": {
+ "encoding": "inplace",
+ "label": "uint256",
+ "numberOfBytes": "32"
+ }
+ }
+ }"#,
+ );
+ assert!(super::check_storage_layout_compatibility(&original, ¤t).is_err());
+ }
+
+ #[test]
+ pub fn test_storage_layout_different_type() {
+ let original = load_json_layout(
+ r#"{
+ "storage": [
+ {
+ "astId": 3,
+ "contract": "contract.sol:C",
+ "label": "data",
+ "offset": 0,
+ "slot": "0",
+ "type": "t_uint256"
+ }
+ ],
+ "types": {
+ "t_uint256": {
+ "encoding": "inplace",
+ "label": "uint256",
+ "numberOfBytes": "32"
+ }
+ }
+ }"#,
+ );
+ let current = load_json_layout(
+ r#"{
+ "storage": [
+ {
+ "astId": 3,
+ "contract": "contract.sol:C",
+ "label": "data",
+ "offset": 0,
+ "slot": "1",
+ "type": "t_address"
+ }
+ ],
+ "types": {
+ "t_address": {
+ "encoding": "inplace",
+ "label": "address",
+ "numberOfBytes": "20"
+ }
+ }
+ }"#,
+ );
+ assert!(super::check_storage_layout_compatibility(&original, ¤t).is_err());
+ }
+
+ #[test]
+ pub fn test_storage_layout_additional() {
+ let original = load_json_layout(
+ r#"{
+ "storage": [
+ {
+ "astId": 3,
+ "contract": "contract.sol:C",
+ "label": "data",
+ "offset": 0,
+ "slot": "0",
+ "type": "t_uint256"
+ }
+ ],
+ "types": {
+ "t_uint256": {
+ "encoding": "inplace",
+ "label": "uint256",
+ "numberOfBytes": "32"
+ }
+ }
+ }"#,
+ );
+ let current = load_json_layout(
+ r#"{
+ "storage": [
+ {
+ "astId": 3,
+ "contract": "contract.sol:C",
+ "label": "data",
+ "offset": 0,
+ "slot": "0",
+ "type": "t_uint256"
+ },
+ {
+ "astId": 5,
+ "contract": "contract.sol:C",
+ "label": "owner",
+ "offset": 0,
+ "slot": "1",
+ "type": "t_address"
+ }
+ ],
+ "types": {
+ "t_address": {
+ "encoding": "inplace",
+ "label": "address",
+ "numberOfBytes": "20"
+ },
+ "t_uint256": {
+ "encoding": "inplace",
+ "label": "uint256",
+ "numberOfBytes": "32"
+ }
+ }
+ }"#,
+ );
+ assert!(super::check_storage_layout_compatibility(&original, ¤t).is_ok());
+ }
+ #[test]
+ pub fn test_storage_layout_struct() {
+ let original = load_json_layout(
+ r#"{
+ "storage": [
+ {
+ "astId": 1039,
+ "contract": "src/BatchSwap.sol:BatchSwap",
+ "label": "payment",
+ "offset": 0,
+ "slot": "14",
+ "type": "t_struct(paymentStruct)1028_storage"
+ }
+ ],
+ "types": {
+ "t_struct(paymentStruct)1028_storage": {
+ "encoding": "inplace",
+ "key": null,
+ "label": "struct BatchSwap.paymentStruct",
+ "numberOfBytes": "64",
+ "value": null,
+ "other": {
+ "members": [
+ {
+ "astId": 1025,
+ "contract": "src/BatchSwap.sol:BatchSwap",
+ "label": "status",
+ "offset": "0",
+ "slot": "0",
+ "type": "t_bool"
+ },
+ {
+ "astId": 1027,
+ "contract": "src/BatchSwap.sol:BatchSwap",
+ "label": "value",
+ "offset": "0",
+ "slot": "1",
+ "type": "t_uint256"
+ }
+ ]
+ }
+ }
+ }
+ }"#,
+ );
+ let current = load_json_layout(
+ r#"{
+ "storage": [
+ {
+ "astId": 1039,
+ "contract": "src/BatchSwap.sol:BatchSwap",
+ "label": "payment",
+ "offset": 0,
+ "slot": "14",
+ "type": "t_struct(paymentStruct)666_storage"
+ }
+ ],
+ "types": {
+ "t_struct(paymentStruct)666_storage": {
+ "encoding": "inplace",
+ "key": null,
+ "label": "struct BatchSwap.paymentStruct",
+ "numberOfBytes": "64",
+ "value": null,
+ "other": {
+ "members": [
+ {
+ "astId": 663,
+ "contract": "src/BatchSwap.sol:BatchSwap",
+ "label": "status",
+ "offset": "0",
+ "slot": "0",
+ "type": "t_bool"
+ },
+ {
+ "astId": 664,
+ "contract": "src/BatchSwap.sol:BatchSwap",
+ "label": "value",
+ "offset": "0",
+ "slot": "1",
+ "type": "t_uint256"
+ }
+ ]
+ }
+ }
+ }
+ }"#,
+ );
+ assert!(super::check_storage_layout_compatibility(&original, ¤t).is_ok());
+ }
+}
diff --git a/crates/tweak/src/constants.rs b/crates/tweak/src/constants.rs
new file mode 100644
index 000000000000..792d5d015d18
--- /dev/null
+++ b/crates/tweak/src/constants.rs
@@ -0,0 +1,45 @@
+//! Constant values used during tweaking the code.
+
+use alloy_primitives::Address;
+use foundry_config::NamedChain;
+
+/// Non-standard precompiled contracts
+pub enum NonStandardPrecompiled {
+ BinanceSmartChain([&'static str; 11]),
+}
+
+impl NonStandardPrecompiled {
+ /// Binance Smart Chain non-standard precompiled contracts
+ /// Collected from
+ pub const BSC_NON_STANDARD_PRECOMPILED: NonStandardPrecompiled =
+ NonStandardPrecompiled::BinanceSmartChain([
+ "0x0000000000000000000000000000000000001000",
+ "0x0000000000000000000000000000000000001001",
+ "0x0000000000000000000000000000000000001002",
+ "0x0000000000000000000000000000000000001003",
+ "0x0000000000000000000000000000000000001004",
+ "0x0000000000000000000000000000000000001005",
+ "0x0000000000000000000000000000000000001006",
+ "0x0000000000000000000000000000000000001007",
+ "0x0000000000000000000000000000000000001008",
+ "0x0000000000000000000000000000000000002000",
+ "0x0000000000000000000000000000000000002001",
+ ]);
+
+ pub fn get_addresses(&self) -> Vec {
+ match self {
+ NonStandardPrecompiled::BinanceSmartChain(addresses) => {
+ addresses.iter().map(|a| a.parse().expect("incorrect address")).collect()
+ }
+ }
+ }
+
+ pub fn get_precomiled_address(chain_id: NamedChain) -> Option> {
+ match chain_id {
+ NamedChain::BinanceSmartChain | NamedChain::BinanceSmartChainTestnet => {
+ Some(Self::BSC_NON_STANDARD_PRECOMPILED.get_addresses())
+ }
+ _ => None,
+ }
+ }
+}
diff --git a/crates/tweak/src/lib.rs b/crates/tweak/src/lib.rs
new file mode 100644
index 000000000000..de27570fada4
--- /dev/null
+++ b/crates/tweak/src/lib.rs
@@ -0,0 +1,76 @@
+pub mod code;
+pub mod compatibility;
+mod constants;
+mod metadata;
+
+use std::collections::BTreeMap;
+
+use alloy_primitives::{keccak256, Address, Bytes, B256};
+use eyre::Result;
+
+use foundry_cli::opts::RpcOpts;
+use foundry_evm::{backend::Backend, fork::CreateFork};
+pub use metadata::ClonedProject;
+use revm::{
+ primitives::{Bytecode, KECCAK_EMPTY},
+ Database,
+};
+
+pub type TweakData = BTreeMap;
+
+pub async fn build_tweak_data(
+ projects: &Vec,
+ rpc: &RpcOpts,
+ quick: bool,
+) -> Result {
+ let mut tweak_data = BTreeMap::new();
+ for project in projects {
+ let metadata = &project.metadata;
+ let address = metadata.address;
+ let code = project.tweaked_code(rpc, quick).await?;
+ tweak_data.insert(address, code);
+ }
+ Ok(tweak_data)
+}
+
+pub fn build_tweaked_backend(fork: Option, tweak_data: &TweakData) -> Result {
+ let mut backend = Backend::spawn(fork);
+ for (address, code) in tweak_data {
+ tweak_backend_once(&mut backend, *address, code.clone())?;
+ }
+ Ok(backend)
+}
+
+/// Tweak the code of a contract in the blockchain backend.
+pub fn tweak_backend_once(
+ backend: &mut Backend,
+ tweak_address: Address,
+ tweaked_code: Bytes,
+) -> Result<()> {
+ let mut info = backend.basic(tweak_address)?.unwrap_or_default();
+ let code_hash = if tweaked_code.as_ref().is_empty() {
+ KECCAK_EMPTY
+ } else {
+ B256::from_slice(&keccak256(tweaked_code.as_ref())[..])
+ };
+ info.code_hash = code_hash;
+ info.code = Some(Bytecode::new_raw(alloy_primitives::Bytes(tweaked_code.0)));
+ backend.insert_account_info(tweak_address, info);
+ Ok(())
+}
+
+pub fn tweak_backend(backend: &mut Backend, tweak_data: &TweakData) -> Result<()> {
+ for (tweak_address, tweaked_code) in tweak_data {
+ let mut info = backend.basic(*tweak_address)?.unwrap_or_default();
+ let code_hash = if tweaked_code.as_ref().is_empty() {
+ revm::primitives::KECCAK_EMPTY
+ } else {
+ B256::from_slice(&alloy_primitives::keccak256(tweaked_code.as_ref())[..])
+ };
+ info.code_hash = code_hash;
+ info.code = Some(Bytecode::new_raw(alloy_primitives::Bytes(tweaked_code.clone().0)));
+ backend.insert_account_info(*tweak_address, info);
+ }
+
+ Ok(())
+}
diff --git a/crates/tweak/src/metadata.rs b/crates/tweak/src/metadata.rs
new file mode 100644
index 000000000000..b6498a68e76f
--- /dev/null
+++ b/crates/tweak/src/metadata.rs
@@ -0,0 +1,187 @@
+use std::{
+ path::PathBuf,
+ sync::{Arc, Mutex},
+};
+
+use alloy_primitives::{Address, Bytes, ChainId, TxHash};
+use eyre::{eyre, Result};
+use foundry_cli::opts::RpcOpts;
+use foundry_common::compile::ProjectCompiler;
+use foundry_compilers::{
+ artifacts::{
+ output_selection::ContractOutputSelection, ConfigurableContractArtifact, StorageLayout,
+ },
+ ProjectCompileOutput,
+};
+use foundry_config::Config;
+
+/// ClonedProject represents a foundry project that is cloned by the `forge clone` command.
+/// It couples with an on-chain contract instance.
+/// Users may modify the source code of the cloned project, but the storage layout should remain the
+/// same as the original contract. The cloned project will be used to tweak the on-chain contract.
+#[derive(Debug, Clone, Default)]
+pub struct ClonedProject {
+ pub root: PathBuf,
+ pub config: Config,
+ pub metadata: CloneMetadata,
+
+ // cache
+ pub(crate) _compile_output: Arc>>,
+ pub(crate) _main_artifact: Arc>>,
+}
+
+impl PartialEq for ClonedProject {
+ fn eq(&self, other: &Self) -> bool {
+ self.root == other.root
+ }
+}
+
+impl Eq for ClonedProject {}
+
+impl PartialOrd for ClonedProject {
+ fn partial_cmp(&self, other: &Self) -> Option {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for ClonedProject {
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ self.root.cmp(&other.root)
+ }
+}
+
+impl ClonedProject {
+ fn set_cache(cache: Arc>>, compile_output: T) {
+ *cache.clone().lock().unwrap() = Some(compile_output);
+ }
+ fn get_cache(cache: Arc>>) -> T {
+ // cache.clone().lock().unwrap().unwrap()
+ let lock_result = cache.lock().expect("Failed to lock the cache");
+ (*lock_result).clone().expect("Value not present")
+ }
+ fn is_cached(cache: Arc>>) -> bool {
+ cache.clone().lock().unwrap().is_some()
+ }
+
+ /// Load the cloned project from the root directory of the project.
+ /// If the clone metadata file does not exist, an error is returned.
+ /// The root should be an absolute path.
+ pub fn load_with_root(root: impl Into) -> Result {
+ let root = root.into();
+ assert!(root.is_absolute());
+ let cwd = std::env::current_dir()?;
+ std::env::set_current_dir(&root)?;
+ let config = Config::load_with_root(&root);
+ std::env::set_current_dir(cwd)?;
+ let metadata = CloneMetadata::load_with_root(&root)?;
+ Ok(ClonedProject {
+ root,
+ config,
+ metadata,
+ _compile_output: Default::default(),
+ _main_artifact: Default::default(),
+ })
+ }
+
+ /// Compile the project and return the artifacts.
+ /// The compile output is cached.
+ /// A workaround for the insufficient implementation of Config::load_with_root.
+ pub fn compile_safe(&self) -> Result {
+ // check the cache
+ if Self::is_cached(self._compile_output.clone()) {
+ return Ok(Self::get_cache(self._compile_output.clone()));
+ }
+
+ // load the foundry config
+ // XXX (ZZ): some insufficient implementation of Config::project_paths(). It depends on the
+ // current working directory, preventiong us from invoking this function directly
+ let cwd = std::env::current_dir()?;
+ std::env::set_current_dir(&self.root)?;
+
+ // compile the project to get the current artifacts
+ let mut config = self.config.clone();
+ config.extra_output.push(ContractOutputSelection::StorageLayout);
+ let project = config.project()?;
+ let output = ProjectCompiler::new().compile(&project)?;
+
+ std::env::set_current_dir(cwd)?;
+
+ // cache the output
+ Self::set_cache(self._compile_output.clone(), output);
+ Ok(Self::get_cache(self._compile_output.clone()))
+ }
+
+ /// Get the artifact of the main contract of the project.
+ pub fn main_artifact(&self) -> Result {
+ // check the cache
+ if Self::is_cached(self._main_artifact.clone()) {
+ return Ok(Self::get_cache(self._main_artifact.clone()));
+ }
+
+ let output = self.compile_safe()?;
+ let (_, _, artifact) = output
+ .artifacts_with_files()
+ .find(|(_, contract_name, _)| **contract_name == self.metadata.target_contract)
+ .ok_or_else(|| {
+ eyre!("the contract {} is not found in the project", self.metadata.target_contract)
+ })?;
+
+ // cache the artifact
+ Self::set_cache(self._main_artifact.clone(), artifact.clone());
+ Ok(Self::get_cache(self._main_artifact.clone()))
+ }
+
+ /// Get the tweaked code of the main contract of the project.
+ pub async fn tweaked_code(&self, rpc: &RpcOpts, quick: bool) -> Result {
+ // check chain id
+ if self.config.chain.unwrap_or_default().id() != self.metadata.chain_id {
+ return Err(eyre!(
+ "the chain id of the project ({}) is different from the chain id of the on-chain contract ({})",
+ self.config.chain.unwrap_or_default().id(),
+ self.metadata.chain_id
+ ));
+ }
+ // check the storage compatibility
+ super::compatibility::check_storage_compatibility(self)?;
+
+ // get tweaked code
+ let code = super::code::generate_tweaked_code(rpc, self, quick).await?;
+ Ok(code)
+ }
+}
+
+/// CloneMetadata stores the metadata that are not included by `foundry.toml` but necessary for a
+/// cloned contract. This struct is the twin of the `CloneMetadata` struct in the `clone` command of
+/// `forge` crate.
+#[derive(Debug, Clone, Default, serde::Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CloneMetadata {
+ /// The path to the source file that contains the contract declaration.
+ /// The path is relative to the root directory of the project.
+ pub path: PathBuf,
+ /// The name of the contract in the file.
+ pub target_contract: String,
+ /// The address of the contract on the blockchian.
+ pub address: Address,
+ /// The chain id.
+ pub chain_id: ChainId,
+ /// The transaction hash of the creation transaction.
+ pub creation_transaction: TxHash,
+ /// The address of the deployer (caller of the CREATE/CREATE2).
+ pub deployer: Address,
+ /// The constructor arguments of the contract.
+ pub constructor_arguments: Bytes,
+ /// The storage layout of the contract.
+ pub storage_layout: StorageLayout,
+}
+
+impl CloneMetadata {
+ /// Load the metadata from the `clone.toml` file in the root directory of the project.
+ /// If the file does not exist, an error is returned.
+ pub fn load_with_root(root: impl Into) -> Result {
+ let path = root.into().join(".clone.meta");
+ let metadata = std::fs::read_to_string(path)?;
+ let metadata = serde_json::from_str(&metadata)?;
+ Ok(metadata)
+ }
+}