Skip to content

Commit db71cec

Browse files
authored
feat: copyFile cheatcode (foundry-rs#5613)
1 parent a0a31c3 commit db71cec

File tree

14 files changed

+226
-112
lines changed

14 files changed

+226
-112
lines changed

crates/abi/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,11 @@ Additional bindings can be generated by doing the following:
88
2. update the [build script](./build.rs)'s `MultiAbigen::new` call;
99
3. build the crate once with `cargo build -p foundry-abi`, generating the bindings for the first time;
1010
4. export the newly-generated bindings at the root of the crate, in [`lib.rs`](./src/lib.rs).
11+
12+
New cheatcodes can be added by doing the following:
13+
14+
1. add its Solidity definition(s) in [`HEVM.sol`](./abi/HEVM.sol), bindings should regenerate automatically;
15+
2. implement it in [`foundry-evm`](../evm/src/executor/inspector/cheatcodes/);
16+
3. update the [`Vm.sol`](../../testdata/cheats/Vm.sol) test interface;
17+
4. add tests in [`testdata`](../../testdata/cheats/);
18+
5. open a PR to [`forge-std`](https://github.com/foundry-rs/forge-std) to add it to the `Vm` interface.

crates/abi/abi/HEVM.sol

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,14 @@ startBroadcast(uint256)
119119
stopBroadcast()
120120

121121
projectRoot()(string)
122+
openFile(string)
122123
readFile(string)(string)
123124
readFileBinary(string)(bytes)
125+
readLine(string)(string)
124126
writeFile(string,string)
125127
writeFileBinary(string,bytes)
126-
openFile(string)
127-
readLine(string)(string)
128128
writeLine(string,string)
129+
copyFile(string,string)
129130
closeFile(string)
130131
removeFile(string)
131132
createDir(string, bool)

crates/abi/src/bindings/hevm.rs

Lines changed: 58 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cli/src/forge/cmd/script/multi.rs

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ use ethers::{
99
signers::LocalWallet,
1010
};
1111
use eyre::{ContextCompat, WrapErr};
12+
use foundry_cli::utils::now;
1213
use foundry_common::{fs, get_http_provider};
1314
use foundry_config::Config;
1415
use futures::future::join_all;
1516
use serde::{Deserialize, Serialize};
1617
use std::{
17-
io::BufWriter,
18+
io::{BufWriter, Write},
1819
path::{Path, PathBuf},
1920
sync::Arc,
20-
time::{SystemTime, UNIX_EPOCH},
2121
};
2222
use tracing::log::trace;
2323

@@ -31,11 +31,8 @@ pub struct MultiChainSequence {
3131

3232
impl Drop for MultiChainSequence {
3333
fn drop(&mut self) {
34-
self.deployments.iter_mut().for_each(|sequence| {
35-
sequence.sort_receipts();
36-
});
37-
38-
self.save().expect("not able to save multi deployment sequence");
34+
self.deployments.iter_mut().for_each(|sequence| sequence.sort_receipts());
35+
self.save().expect("could not save multi deployment sequence");
3936
}
4037
}
4138

@@ -50,14 +47,7 @@ impl MultiChainSequence {
5047
let path =
5148
MultiChainSequence::get_path(&log_folder.join("multi"), sig, target, broadcasted)?;
5249

53-
Ok(MultiChainSequence {
54-
deployments,
55-
path,
56-
timestamp: SystemTime::now()
57-
.duration_since(UNIX_EPOCH)
58-
.expect("Wrong system time.")
59-
.as_secs(),
60-
})
50+
Ok(MultiChainSequence { deployments, path, timestamp: now().as_secs() })
6151
}
6252

6353
/// Saves to ./broadcast/multi/contract_filename[-timestamp]/sig.json
@@ -85,8 +75,7 @@ impl MultiChainSequence {
8575
let filename = sig
8676
.split_once('(')
8777
.wrap_err_with(|| format!("Failed to compute file name: Signature {sig} is invalid."))?
88-
.0
89-
.to_string();
78+
.0;
9079
out.push(format!("{filename}.json"));
9180

9281
Ok(out)
@@ -100,20 +89,20 @@ impl MultiChainSequence {
10089

10190
/// Saves the transactions as file if it's a standalone deployment.
10291
pub fn save(&mut self) -> eyre::Result<()> {
103-
self.timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
104-
let path = self.path.to_string_lossy();
92+
self.timestamp = now().as_secs();
10593

10694
//../Contract-latest/run.json
107-
serde_json::to_writer_pretty(BufWriter::new(fs::create_file(&self.path)?), &self)?;
95+
let mut writer = BufWriter::new(fs::create_file(&self.path)?);
96+
serde_json::to_writer_pretty(&mut writer, &self)?;
97+
writer.flush()?;
10898

10999
//../Contract-[timestamp]/run.json
100+
let path = self.path.to_string_lossy();
110101
let file = PathBuf::from(&path.replace("-latest", &format!("-{}", self.timestamp)));
102+
fs::create_dir_all(file.parent().unwrap())?;
103+
fs::copy(&self.path, &file)?;
111104

112-
fs::create_dir_all(file.parent().expect("to have a file."))?;
113-
114-
serde_json::to_writer_pretty(BufWriter::new(fs::create_file(file)?), &self)?;
115-
116-
println!("\nTransactions saved to: {path}\n");
105+
println!("\nTransactions saved to: {}\n", self.path.display());
117106

118107
Ok(())
119108
}

crates/cli/src/forge/cmd/script/sequence.rs

Lines changed: 29 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ use ethers::{
1313
types::transaction::eip2718::TypedTransaction,
1414
};
1515
use eyre::{ContextCompat, WrapErr};
16+
use foundry_cli::utils::now;
1617
use foundry_common::{fs, shell, SELECTOR_LEN};
1718
use foundry_config::Config;
1819
use serde::{Deserialize, Serialize};
1920
use std::{
2021
collections::{HashMap, VecDeque},
21-
io::BufWriter,
22+
io::{BufWriter, Write},
2223
path::{Path, PathBuf},
23-
time::{SystemTime, UNIX_EPOCH},
2424
};
2525
use tracing::trace;
2626
use yansi::Paint;
@@ -102,10 +102,7 @@ impl ScriptSequence {
102102
pending: vec![],
103103
path,
104104
sensitive_path,
105-
timestamp: SystemTime::now()
106-
.duration_since(UNIX_EPOCH)
107-
.expect("Wrong system time.")
108-
.as_secs(),
105+
timestamp: now().as_secs(),
109106
libraries: vec![],
110107
chain,
111108
multi: is_multi,
@@ -152,43 +149,34 @@ impl ScriptSequence {
152149

153150
/// Saves the transactions as file if it's a standalone deployment.
154151
pub fn save(&mut self) -> eyre::Result<()> {
155-
if !self.multi && !self.transactions.is_empty() {
156-
self.timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
157-
158-
let sensitive_script_sequence: SensitiveScriptSequence = self.into();
159-
160-
let path = self.path.to_string_lossy();
161-
let sensitive_path = self.sensitive_path.to_string_lossy();
162-
163-
// broadcast folder writes
164-
//../run-latest.json
165-
serde_json::to_writer_pretty(BufWriter::new(fs::create_file(&self.path)?), &self)?;
166-
//../run-[timestamp].json
167-
serde_json::to_writer_pretty(
168-
BufWriter::new(fs::create_file(
169-
path.replace("latest.json", &format!("{}.json", self.timestamp)),
170-
)?),
171-
&self,
172-
)?;
173-
174-
// cache folder writes
175-
//../run-latest.json
176-
serde_json::to_writer_pretty(
177-
BufWriter::new(fs::create_file(&self.sensitive_path)?),
178-
&sensitive_script_sequence,
179-
)?;
180-
//../run-[timestamp].json
181-
serde_json::to_writer_pretty(
182-
BufWriter::new(fs::create_file(
183-
sensitive_path.replace("latest.json", &format!("{}.json", self.timestamp)),
184-
)?),
185-
&sensitive_script_sequence,
186-
)?;
187-
188-
shell::println(format!("\nTransactions saved to: {path}\n"))?;
189-
shell::println(format!("Sensitive values saved to: {sensitive_path}\n"))?;
152+
if self.multi || self.transactions.is_empty() {
153+
return Ok(())
190154
}
191155

156+
self.timestamp = now().as_secs();
157+
let ts_name = format!("run-{}.json", self.timestamp);
158+
159+
let sensitive_script_sequence: SensitiveScriptSequence = self.into();
160+
161+
// broadcast folder writes
162+
//../run-latest.json
163+
let mut writer = BufWriter::new(fs::create_file(&self.path)?);
164+
serde_json::to_writer_pretty(&mut writer, &self)?;
165+
writer.flush()?;
166+
//../run-[timestamp].json
167+
fs::copy(&self.path, self.path.with_file_name(&ts_name))?;
168+
169+
// cache folder writes
170+
//../run-latest.json
171+
let mut writer = BufWriter::new(fs::create_file(&self.sensitive_path)?);
172+
serde_json::to_writer_pretty(&mut writer, &sensitive_script_sequence)?;
173+
writer.flush()?;
174+
//../run-[timestamp].json
175+
fs::copy(&self.sensitive_path, self.sensitive_path.with_file_name(&ts_name))?;
176+
177+
shell::println(format!("\nTransactions saved to: {}\n", self.path.display()))?;
178+
shell::println(format!("Sensitive values saved to: {}\n", self.sensitive_path.display()))?;
179+
192180
Ok(())
193181
}
194182

crates/cli/src/utils/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::{
1414
path::{Path, PathBuf},
1515
process::{Command, Output, Stdio},
1616
str::FromStr,
17-
time::Duration,
17+
time::{Duration, SystemTime, UNIX_EPOCH},
1818
};
1919
use tracing_error::ErrorLayer;
2020
use tracing_subscriber::prelude::*;
@@ -153,6 +153,11 @@ pub fn parse_delay(delay: &str) -> Result<Duration> {
153153
Ok(delay)
154154
}
155155

156+
/// Returns the current time as a [`Duration`] since the Unix epoch.
157+
pub fn now() -> Duration {
158+
SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards")
159+
}
160+
156161
/// Runs the `future` in a new [`tokio::runtime::Runtime`]
157162
#[allow(unused)]
158163
pub fn block_on<F: Future>(future: F) -> F::Output {

0 commit comments

Comments
 (0)