Skip to content

Commit

Permalink
feat(script): show the broadcasted transactions when verbose>=4 (#9655)
Browse files Browse the repository at this point in the history
* feat(script): add --dry-run

Signed-off-by: jsvisa <delweng@gmail.com>

* feat(script): implement the tx print

Signed-off-by: jsvisa <delweng@gmail.com>

* no newline if no args

Signed-off-by: jsvisa <delweng@gmail.com>

* clippy

Signed-off-by: jsvisa <delweng@gmail.com>

* add --dry-run --broadcast testcase

Signed-off-by: jsvisa <delweng@gmail.com>

* lossy stdout test

Signed-off-by: jsvisa <delweng@gmail.com>

* feat(script): print txs if --dry-run

Signed-off-by: jsvisa <delweng@gmail.com>

* feat(script): make dry-run as the default behavior

Signed-off-by: jsvisa <delweng@gmail.com>

* fix

Signed-off-by: jsvisa <delweng@gmail.com>

* use writeln instead of push_str

Signed-off-by: jsvisa <delweng@gmail.com>

* implment UIfmt for TransactionMaybeSigned

Signed-off-by: jsvisa <delweng@gmail.com>

* dryrun: use UIfmt instead

Signed-off-by: jsvisa <delweng@gmail.com>

* dryrun: print contract only if call

Signed-off-by: jsvisa <delweng@gmail.com>

* use [..] to test

Signed-off-by: jsvisa <delweng@gmail.com>

* update testcase

Signed-off-by: jsvisa <delweng@gmail.com>

* feat(script): --dry-run --resume

Signed-off-by: jsvisa <delweng@gmail.com>

* no long input

Signed-off-by: jsvisa <delweng@gmail.com>

* no double newline

Signed-off-by: jsvisa <delweng@gmail.com>

Revert "no double newline"

This reverts commit 6337995e4735b7cb2965962d6a7cd29addf367f7.

Signed-off-by: jsvisa <delweng@gmail.com>

wip

Signed-off-by: jsvisa <delweng@gmail.com>

* print transaction if -vvvv

Signed-off-by: jsvisa <delweng@gmail.com>

* Revert "update testcase"

This reverts commit ed5201c.

Signed-off-by: jsvisa <delweng@gmail.com>

* update test for -vvvv broadcast

Signed-off-by: jsvisa <delweng@gmail.com>

* no dryrun module

Signed-off-by: jsvisa <delweng@gmail.com>

* test

Signed-off-by: jsvisa <delweng@gmail.com>

---------

Signed-off-by: jsvisa <delweng@gmail.com>
Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com>
  • Loading branch information
jsvisa and zerosnacks authored Jan 22, 2025
1 parent 36393fb commit 9ac89d4
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 2 deletions.
39 changes: 39 additions & 0 deletions crates/common/src/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,45 @@ revertReason {}",
}
}

impl UIfmt for TransactionMaybeSigned {
fn pretty(&self) -> String {
match self {
Self::Signed { tx, .. } => tx.pretty(),
Self::Unsigned(tx) => format!(
"
accessList {}
chainId {}
gasLimit {}
gasPrice {}
input {}
maxFeePerBlobGas {}
maxFeePerGas {}
maxPriorityFeePerGas {}
nonce {}
to {}
type {}
value {}",
tx.access_list
.as_ref()
.map(|a| a.iter().collect::<Vec<_>>())
.unwrap_or_default()
.pretty(),
tx.chain_id.pretty(),
tx.gas_limit().unwrap_or_default(),
tx.gas_price.pretty(),
tx.input.input.pretty(),
tx.max_fee_per_blob_gas.pretty(),
tx.max_fee_per_gas.pretty(),
tx.max_priority_fee_per_gas.pretty(),
tx.nonce.pretty(),
tx.to.as_ref().map(|a| a.to()).unwrap_or_default().pretty(),
tx.transaction_type.unwrap_or_default(),
tx.value.pretty(),
),
}
}
}

fn extract_revert_reason<S: AsRef<str>>(error_string: S) -> Option<String> {
let message_substr = "execution reverted: ";
error_string
Expand Down
137 changes: 137 additions & 0 deletions crates/forge/tests/cli/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2485,3 +2485,140 @@ forgetest_async!(should_set_correct_sender_nonce_via_cli, |prj, cmd| {
== Logs ==
sender nonce 1124703[..]"#]]);
});

forgetest_async!(dryrun_without_broadcast, |prj, cmd| {
let (_api, handle) = spawn(NodeConfig::test()).await;

foundry_test_utils::util::initialize(prj.root());
prj.add_source(
"Foo",
r#"
import "forge-std/Script.sol";
contract Called {
event log_string(string);
uint256 public x;
uint256 public y;
function run(uint256 _x, uint256 _y) external {
x = _x;
y = _y;
emit log_string("script ran");
}
}
contract DryRunTest is Script {
function run() external {
vm.startBroadcast();
Called called = new Called();
called.run(123, 456);
}
}
"#,
)
.unwrap();

cmd.arg("script")
.args([
"DryRunTest",
"--private-key",
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
"--rpc-url",
&handle.http_endpoint(),
"-vvvv",
])
.assert_success()
.stdout_eq(str![[r#"
[COMPILING_FILES] with [SOLC_VERSION]
[SOLC_VERSION] [ELAPSED]
Compiler run successful!
Traces:
[..] DryRunTest::run()
├─ [0] VM::startBroadcast()
│ └─ ← [Return]
├─ [..] → new Called@0x5FbDB2315678afecb367f032d93F642f64180aa3
│ └─ ← [Return] 567 bytes of code
├─ [..] Called::run(123, 456)
│ ├─ emit log_string(val: "script ran")
│ └─ ← [Stop]
└─ ← [Stop]
Script ran successfully.
== Logs ==
script ran
## Setting up 1 EVM.
==========================
Simulated On-chain Traces:
[113557] → new Called@0x5FbDB2315678afecb367f032d93F642f64180aa3
└─ ← [Return] 567 bytes of code
[46595] Called::run(123, 456)
├─ emit log_string(val: "script ran")
└─ ← [Stop]
==========================
Chain 31337
[ESTIMATED_GAS_PRICE]
[ESTIMATED_TOTAL_GAS_USED]
[ESTIMATED_AMOUNT_REQUIRED]
==========================
=== Transactions that will be broadcast ===
Chain 31337
### Transaction 1 ###
accessList []
chainId 31337
gasLimit 228247
gasPrice
input [..]
maxFeePerBlobGas
maxFeePerGas
maxPriorityFeePerGas
nonce 0
to
type 0
value 0
### Transaction 2 ###
accessList []
chainId 31337
gasLimit 93856
gasPrice
input 0x7357f5d2000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000001c8
maxFeePerBlobGas
maxFeePerGas
maxPriorityFeePerGas
nonce 1
to 0x5FbDB2315678afecb367f032d93F642f64180aa3
type 0
value 0
contract: Called(0x5FbDB2315678afecb367f032d93F642f64180aa3)
data (decoded): run(uint256,uint256)(
123,
456
)
SIMULATION COMPLETE. To broadcast these transactions, add --broadcast and wallet configuration(s) to the previous command. See forge script --help for more.
[SAVED_TRANSACTIONS]
[SAVED_SENSITIVE_VALUES]
"#]]);
});
5 changes: 5 additions & 0 deletions crates/script/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,11 @@ impl ScriptArgs {
// Exit early in case user didn't provide any broadcast/verify related flags.
if !bundled.args.should_broadcast() {
if !shell::is_json() {
if shell::verbosity() >= 4 {
sh_println!("\n=== Transactions that will be broadcast ===\n")?;
bundled.sequence.show_transactions()?;
}

sh_println!("\nSIMULATION COMPLETE. To broadcast these transactions, add --broadcast and wallet configuration(s) to the previous command. See forge script --help for more.")?;
}
return Ok(());
Expand Down
52 changes: 50 additions & 2 deletions crates/script/src/sequence.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,44 @@
use crate::multi_sequence::MultiChainSequence;
use eyre::Result;
use forge_script_sequence::ScriptSequence;
use forge_script_sequence::{ScriptSequence, TransactionWithMetadata};
use foundry_cli::utils::Git;
use foundry_common::fmt::UIfmt;
use foundry_compilers::ArtifactId;
use foundry_config::Config;
use std::path::Path;
use std::{
fmt::{Error, Write},
path::Path,
};

/// Format transaction details for display
fn format_transaction(index: usize, tx: &TransactionWithMetadata) -> Result<String, Error> {
let mut output = String::new();
writeln!(output, "### Transaction {index} ###")?;
writeln!(output, "{}", tx.tx().pretty())?;

// Show contract name and address if available
if !tx.opcode.is_any_create() {
if let (Some(name), Some(addr)) = (&tx.contract_name, &tx.contract_address) {
writeln!(output, "contract: {name}({addr})")?;
}
}

// Show decoded function if available
if let (Some(func), Some(args)) = (&tx.function, &tx.arguments) {
if args.is_empty() {
writeln!(output, "data (decoded): {func}()")?;
} else {
writeln!(output, "data (decoded): {func}(")?;
for (i, arg) in args.iter().enumerate() {
writeln!(&mut output, " {}{}", arg, if i + 1 < args.len() { "," } else { "" })?;
}
writeln!(output, ")")?;
}
}

writeln!(output)?;
Ok(output)
}

/// Returns the commit hash of the project if it exists
pub fn get_commit_hash(root: &Path) -> Option<String> {
Expand Down Expand Up @@ -57,6 +91,20 @@ impl ScriptSequenceKind {

Ok(())
}

pub fn show_transactions(&self) -> Result<()> {
for sequence in self.sequences() {
if !sequence.transactions.is_empty() {
sh_println!("\nChain {}\n", sequence.chain)?;

for (i, tx) in sequence.transactions.iter().enumerate() {
sh_print!("{}", format_transaction(i + 1, tx)?)?;
}
}
}

Ok(())
}
}

impl Drop for ScriptSequenceKind {
Expand Down

0 comments on commit 9ac89d4

Please sign in to comment.