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