Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add --broadcast flag to forge create, default to dry run mode #9420

Merged
merged 5 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion crates/forge/bin/cmd/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ pub struct CreateArgs {
)]
constructor_args_path: Option<PathBuf>,

/// Broadcast the transaction.
#[arg(long)]
pub broadcast: bool,

/// Verify contract after creation.
#[arg(long)]
verify: bool,
Expand Down Expand Up @@ -155,6 +159,10 @@ impl CreateArgs {
} else {
provider.get_chain_id().await?
};

// Whether to broadcast the transaction or not
let dry_run = !self.broadcast;

if self.unlocked {
// Deploy with unlocked account
let sender = self.eth.wallet.from.expect("required");
Expand All @@ -167,6 +175,7 @@ impl CreateArgs {
sender,
config.transaction_timeout,
id,
dry_run,
)
.await
} else {
Expand All @@ -185,6 +194,7 @@ impl CreateArgs {
deployer,
config.transaction_timeout,
id,
dry_run,
)
.await
}
Expand Down Expand Up @@ -260,6 +270,7 @@ impl CreateArgs {
deployer_address: Address,
timeout: u64,
id: ArtifactId,
dry_run: bool,
) -> Result<()> {
let bin = bin.into_bytes().unwrap_or_else(|| {
panic!("no bytecode found in bin object for {}", self.contract.name)
Expand Down Expand Up @@ -339,6 +350,30 @@ impl CreateArgs {
self.verify_preflight_check(constructor_args.clone(), chain, &id).await?;
}

if dry_run {
if !shell::is_json() {
sh_warn!("Dry run enabled, not broadcasting transaction\n")?;

sh_println!("Contract: {}", self.contract.name)?;
sh_println!(
"Transaction: {}",
serde_json::to_string_pretty(&deployer.tx.clone())?
)?;
sh_println!("ABI: {}\n", serde_json::to_string_pretty(&abi)?)?;

sh_warn!("To broadcast this transaction, add --broadcast to the previous command. See forge create --help for more.")?;
} else {
let output = json!({
"contract": self.contract.name,
"transaction": &deployer.tx,
"abi":&abi
});
sh_println!("{}", serde_json::to_string_pretty(&output)?)?;
}

return Ok(());
}

// Deploy the actual contract
let (deployed_contract, receipt) = deployer.send_with_receipt().await?;

Expand All @@ -349,7 +384,7 @@ impl CreateArgs {
"deployedTo": address.to_string(),
"transactionHash": receipt.transaction_hash
});
sh_println!("{output}")?;
sh_println!("{}", serde_json::to_string_pretty(&output)?)?;
} else {
sh_println!("Deployer: {deployer_address}")?;
sh_println!("Deployed to: {address}")?;
Expand Down
129 changes: 124 additions & 5 deletions crates/forge/tests/cli/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use anvil::{spawn, NodeConfig};
use foundry_compilers::artifacts::{remappings::Remapping, BytecodeHash};
use foundry_config::Config;
use foundry_test_utils::{
forgetest, forgetest_async, str,
forgetest, forgetest_async,
snapbox::IntoData,
str,
util::{OutputExt, TestCommand, TestProject},
};
use std::str::FromStr;
Expand Down Expand Up @@ -145,6 +147,7 @@ forgetest_async!(can_create_template_contract, |prj, cmd| {
let config = Config { bytecode_hash: BytecodeHash::None, ..Default::default() };
prj.write_config(config);

// Dry-run without the `--broadcast` flag
cmd.forge_fuse().args([
"create",
format!("./src/{TEMPLATE_CONTRACT}.sol:{TEMPLATE_CONTRACT}").as_str(),
Expand All @@ -154,20 +157,131 @@ forgetest_async!(can_create_template_contract, |prj, cmd| {
pk.as_str(),
]);

// Dry-run
cmd.assert().stdout_eq(str![[r#"
[COMPILING_FILES] with [SOLC_VERSION]
[SOLC_VERSION] [ELAPSED]
Compiler run successful!
Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
[TX_HASH]
Contract: Counter
Transaction: {
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"to": null,
"maxFeePerGas": "0x77359401",
"maxPriorityFeePerGas": "0x1",
"gas": "0x17575",
"input": "[..]",
"nonce": "0x0",
"chainId": "0x7a69"
}
ABI: [
{
"type": "function",
"name": "increment",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "number",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "setNumber",
"inputs": [
{
"name": "newNumber",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
}
]


"#]]);

// Dry-run with `--json` flag
cmd.arg("--json").assert().stdout_eq(
str![[r#"
{
"contract": "Counter",
"transaction": {
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"to": null,
"maxFeePerGas": "0x77359401",
"maxPriorityFeePerGas": "0x1",
"gas": "0x17575",
"input": "[..]",
"nonce": "0x0",
"chainId": "0x7a69"
},
"abi": [
{
"type": "function",
"name": "increment",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "number",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "setNumber",
"inputs": [
{
"name": "newNumber",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
}
]
}

"#]]
.is_json(),
);

cmd.forge_fuse().args([
"create",
format!("./src/{TEMPLATE_CONTRACT}.sol:{TEMPLATE_CONTRACT}").as_str(),
"--rpc-url",
rpc.as_str(),
"--private-key",
pk.as_str(),
"--broadcast",
]);

cmd.assert().stdout_eq(str![[r#"
No files changed, compilation skipped
Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
[TX_HASH]

"#]]);
Expand All @@ -193,6 +307,7 @@ forgetest_async!(can_create_using_unlocked, |prj, cmd| {
"--from",
format!("{dev:?}").as_str(),
"--unlocked",
"--broadcast",
]);

cmd.assert().stdout_eq(str![[r#"
Expand All @@ -204,6 +319,7 @@ Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
[TX_HASH]

"#]]);

cmd.assert().stdout_eq(str![[r#"
No files changed, compilation skipped
Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Expand Down Expand Up @@ -248,6 +364,7 @@ contract ConstructorContract {
rpc.as_str(),
"--private-key",
pk.as_str(),
"--broadcast",
"--constructor-args",
"My Constructor",
])
Expand Down Expand Up @@ -285,6 +402,7 @@ contract TupleArrayConstructorContract {
rpc.as_str(),
"--private-key",
pk.as_str(),
"--broadcast",
"--constructor-args",
"[(1,2), (2,3), (3,4)]",
])
Expand Down Expand Up @@ -335,6 +453,7 @@ contract UniswapV2Swap {
rpc.as_str(),
"--private-key",
pk.as_str(),
"--broadcast",
])
.assert_success()
.stdout_eq(str![[r#"
Expand Down