Skip to main content

forge_script/
multi_sequence.rs

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