forge_script/
sequence.rs

1use crate::multi_sequence::MultiChainSequence;
2use eyre::Result;
3use forge_script_sequence::{ScriptSequence, TransactionWithMetadata};
4use foundry_cli::utils::Git;
5use foundry_common::fmt::UIfmt;
6use foundry_compilers::ArtifactId;
7use foundry_config::Config;
8use std::{
9    fmt::{Error, Write},
10    path::Path,
11};
12
13/// Format transaction details for display
14fn format_transaction(index: usize, tx: &TransactionWithMetadata) -> Result<String, Error> {
15    let mut output = String::new();
16    writeln!(output, "### Transaction {index} ###")?;
17    writeln!(output, "{}", tx.tx().pretty())?;
18
19    // Show contract name and address if available
20    if !tx.opcode.is_any_create() {
21        if let (Some(name), Some(addr)) = (&tx.contract_name, &tx.contract_address) {
22            writeln!(output, "contract: {name}({addr})")?;
23        }
24    }
25
26    // Show decoded function if available
27    if let (Some(func), Some(args)) = (&tx.function, &tx.arguments) {
28        if args.is_empty() {
29            writeln!(output, "data (decoded): {func}()")?;
30        } else {
31            writeln!(output, "data (decoded): {func}(")?;
32            for (i, arg) in args.iter().enumerate() {
33                writeln!(&mut output, "  {}{}", arg, if i + 1 < args.len() { "," } else { "" })?;
34            }
35            writeln!(output, ")")?;
36        }
37    }
38
39    writeln!(output)?;
40    Ok(output)
41}
42
43/// Returns the commit hash of the project if it exists
44pub fn get_commit_hash(root: &Path) -> Option<String> {
45    Git::new(root).commit_hash(true, "HEAD").ok()
46}
47
48pub enum ScriptSequenceKind {
49    Single(ScriptSequence),
50    Multi(MultiChainSequence),
51}
52
53impl ScriptSequenceKind {
54    pub fn save(&mut self, silent: bool, save_ts: bool) -> Result<()> {
55        match self {
56            Self::Single(sequence) => sequence.save(silent, save_ts),
57            Self::Multi(sequence) => sequence.save(silent, save_ts),
58        }
59    }
60
61    pub fn sequences(&self) -> &[ScriptSequence] {
62        match self {
63            Self::Single(sequence) => std::slice::from_ref(sequence),
64            Self::Multi(sequence) => &sequence.deployments,
65        }
66    }
67
68    pub fn sequences_mut(&mut self) -> &mut [ScriptSequence] {
69        match self {
70            Self::Single(sequence) => std::slice::from_mut(sequence),
71            Self::Multi(sequence) => &mut sequence.deployments,
72        }
73    }
74    /// Updates underlying sequence paths to not be under /dry-run directory.
75    pub fn update_paths_to_broadcasted(
76        &mut self,
77        config: &Config,
78        sig: &str,
79        target: &ArtifactId,
80    ) -> Result<()> {
81        match self {
82            Self::Single(sequence) => {
83                sequence.paths =
84                    Some(ScriptSequence::get_paths(config, sig, target, sequence.chain, false)?);
85            }
86            Self::Multi(sequence) => {
87                (sequence.path, sequence.sensitive_path) =
88                    MultiChainSequence::get_paths(config, sig, target, false)?;
89            }
90        };
91
92        Ok(())
93    }
94
95    pub fn show_transactions(&self) -> Result<()> {
96        for sequence in self.sequences() {
97            if !sequence.transactions.is_empty() {
98                sh_println!("\nChain {}\n", sequence.chain)?;
99
100                for (i, tx) in sequence.transactions.iter().enumerate() {
101                    sh_print!("{}", format_transaction(i + 1, tx)?)?;
102                }
103            }
104        }
105
106        Ok(())
107    }
108}
109
110impl Drop for ScriptSequenceKind {
111    fn drop(&mut self) {
112        if let Err(err) = self.save(false, true) {
113            error!(?err, "could not save deployment sequence");
114        }
115    }
116}