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