forge_script/
multi_sequence.rs
1use eyre::{ContextCompat, Result, WrapErr};
2use forge_script_sequence::{
3 now, sig_to_file_name, ScriptSequence, SensitiveScriptSequence, DRY_RUN_DIR,
4};
5use foundry_common::{fs, shell};
6use foundry_compilers::ArtifactId;
7use foundry_config::Config;
8use serde::{Deserialize, Serialize};
9use std::{
10 io::{BufWriter, Write},
11 path::PathBuf,
12};
13
14#[derive(Clone, Default, Serialize, Deserialize)]
16pub struct MultiChainSequence {
17 pub deployments: Vec<ScriptSequence>,
18 #[serde(skip)]
19 pub path: PathBuf,
20 #[serde(skip)]
21 pub sensitive_path: PathBuf,
22 pub timestamp: u64,
23}
24
25#[derive(Clone, Default, Serialize, Deserialize)]
27pub struct SensitiveMultiChainSequence {
28 pub deployments: Vec<SensitiveScriptSequence>,
29}
30
31impl SensitiveMultiChainSequence {
32 fn from_multi_sequence(sequence: MultiChainSequence) -> Self {
33 Self {
34 deployments: sequence.deployments.into_iter().map(|sequence| sequence.into()).collect(),
35 }
36 }
37}
38
39impl MultiChainSequence {
40 pub fn new(
41 deployments: Vec<ScriptSequence>,
42 sig: &str,
43 target: &ArtifactId,
44 config: &Config,
45 dry_run: bool,
46 ) -> Result<Self> {
47 let (path, sensitive_path) = Self::get_paths(config, sig, target, dry_run)?;
48
49 Ok(Self { deployments, path, sensitive_path, timestamp: now().as_secs() })
50 }
51
52 pub fn get_paths(
56 config: &Config,
57 sig: &str,
58 target: &ArtifactId,
59 dry_run: bool,
60 ) -> Result<(PathBuf, PathBuf)> {
61 let mut broadcast = config.broadcast.to_path_buf();
62 let mut cache = config.cache_path.to_path_buf();
63 let mut common = PathBuf::new();
64
65 common.push("multi");
66
67 if dry_run {
68 common.push(DRY_RUN_DIR);
69 }
70
71 let target_fname = target
72 .source
73 .file_name()
74 .wrap_err_with(|| format!("No filename for {:?}", target.source))?
75 .to_string_lossy();
76
77 common.push(format!("{target_fname}-latest"));
78
79 broadcast.push(common.clone());
80 cache.push(common);
81
82 fs::create_dir_all(&broadcast)?;
83 fs::create_dir_all(&cache)?;
84
85 let filename = format!("{}.json", sig_to_file_name(sig));
86
87 broadcast.push(filename.clone());
88 cache.push(filename);
89
90 Ok((broadcast, cache))
91 }
92
93 pub fn load(config: &Config, sig: &str, target: &ArtifactId, dry_run: bool) -> Result<Self> {
95 let (path, sensitive_path) = Self::get_paths(config, sig, target, dry_run)?;
96 let mut sequence: Self = foundry_compilers::utils::read_json_file(&path)
97 .wrap_err("Multi-chain deployment not found.")?;
98 let sensitive_sequence: SensitiveMultiChainSequence =
99 foundry_compilers::utils::read_json_file(&sensitive_path)
100 .wrap_err("Multi-chain deployment sensitive details not found.")?;
101
102 sequence.deployments.iter_mut().enumerate().for_each(|(i, sequence)| {
103 sequence.fill_sensitive(&sensitive_sequence.deployments[i]);
104 });
105
106 sequence.path = path;
107 sequence.sensitive_path = sensitive_path;
108
109 Ok(sequence)
110 }
111
112 pub fn save(&mut self, silent: bool, save_ts: bool) -> Result<()> {
114 self.deployments.iter_mut().for_each(|sequence| sequence.sort_receipts());
115
116 self.timestamp = now().as_secs();
117
118 let sensitive_sequence = SensitiveMultiChainSequence::from_multi_sequence(self.clone());
119
120 let mut writer = BufWriter::new(fs::create_file(&self.path)?);
123 serde_json::to_writer_pretty(&mut writer, &self)?;
124 writer.flush()?;
125
126 if save_ts {
127 let path = self.path.to_string_lossy();
129 let file = PathBuf::from(&path.replace("-latest", &format!("-{}", self.timestamp)));
130 fs::create_dir_all(file.parent().unwrap())?;
131 fs::copy(&self.path, &file)?;
132 }
133
134 let mut writer = BufWriter::new(fs::create_file(&self.sensitive_path)?);
137 serde_json::to_writer_pretty(&mut writer, &sensitive_sequence)?;
138 writer.flush()?;
139
140 if save_ts {
141 let path = self.sensitive_path.to_string_lossy();
143 let file = PathBuf::from(&path.replace("-latest", &format!("-{}", self.timestamp)));
144 fs::create_dir_all(file.parent().unwrap())?;
145 fs::copy(&self.sensitive_path, &file)?;
146 }
147
148 if !silent {
149 if shell::is_json() {
150 sh_println!(
151 "{}",
152 serde_json::json!({
153 "status": "success",
154 "transactions": self.path.display().to_string(),
155 "sensitive": self.sensitive_path.display().to_string(),
156 })
157 )?;
158 } else {
159 sh_println!("\nTransactions saved to: {}\n", self.path.display())?;
160 sh_println!("Sensitive details saved to: {}\n", self.sensitive_path.display())?;
161 }
162 }
163
164 Ok(())
165 }
166}