Skip to main content

forge_script/
sequence.rs

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