Skip to content
Closed
127 changes: 57 additions & 70 deletions crates/cast/src/cmd/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,76 +42,69 @@ use super::run::fetch_contracts_bytecode_from_trace;
static OVERRIDE_PATTERN: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^([^:]+):([^:]+):([^:]+)$").unwrap());

/// CLI arguments for `cast call`.
/// Call a contract function locally (eth_call) and print the result.
///
/// ## State Override Flags
/// Example:
///
/// The following flags can be used to override the state for the call:
///
/// * `--override-balance <address>:<balance>` - Override the balance of an account
/// * `--override-nonce <address>:<nonce>` - Override the nonce of an account
/// * `--override-code <address>:<code>` - Override the code of an account
/// * `--override-state <address>:<slot>:<value>` - Override a storage slot of an account
///
/// Multiple overrides can be specified for the same account. For example:
///
/// ```bash
/// cast call 0x... "transfer(address,uint256)" 0x... 100 \
/// --override-balance 0x123:0x1234 \
/// --override-nonce 0x123:1 \
/// --override-code 0x123:0x1234 \
/// --override-state 0x123:0x1:0x1234
/// --override-state-diff 0x123:0x1:0x1234
/// ```
/// cast call 0xAbC... "balanceOf(address)" 0x123... --rpc-url <URL>
#[derive(Debug, Parser)]
#[command(
about = "Call a contract function locally (eth_call) and print the result.",
long_about = "Call a contract function locally (eth_call) and print the result.\n\
EXAMPLES:\n\
cast call 0xAbC... 'balanceOf(address)' 0x123... --rpc-url <URL>\n\
cast call 0xAbC... --data 0xabcdef... --rpc-url <URL>\n\
cast call 0xAbC... 'transfer(address,uint256)' 0x123... 100 --override-balance 0x123:1000\n\
See more: https://book.getfoundry.sh/reference/cast/cast-call.html"
)]
pub struct CallArgs {
/// The destination of the transaction.
#[arg(value_parser = NameOrAddress::from_str)]
/// Destination address of the contract to call.
#[arg(help = "Destination address of the contract to call.", value_name = "TO", value_parser = NameOrAddress::from_str)]
to: Option<NameOrAddress>,

/// The signature of the function to call.
/// Function signature to call, e.g. `balanceOf(address)`.
#[arg(help = "Function signature to call.", value_name = "SIG")]
sig: Option<String>,

/// The arguments of the function to call.
/// Arguments for the function call.
#[arg(help = "Arguments for the function call.", value_name = "ARGS")]
args: Vec<String>,

/// Raw hex-encoded data for the transaction. Used instead of \[SIG\] and \[ARGS\].
/// Raw hex-encoded data for the transaction. Used instead of [SIG] and [ARGS].
#[arg(
long,
conflicts_with_all = &["sig", "args"]
conflicts_with_all = &["sig", "args"],
value_name = "DATA",
help = "Raw hex-encoded data for the transaction. Used instead of [SIG] and [ARGS]."
)]
data: Option<String>,

/// Forks the remote rpc, executes the transaction locally and prints a trace
#[arg(long, default_value_t = false)]
/// Forks the remote rpc, executes the transaction locally and prints a trace.
#[arg(long, default_value_t = false, help = "Forks the remote rpc, executes the transaction locally and prints a trace.")]
trace: bool,

/// Opens an interactive debugger.
/// Can only be used with `--trace`.
#[arg(long, requires = "trace")]
/// Opens an interactive debugger (requires --trace).
#[arg(long, requires = "trace", help = "Opens an interactive debugger (requires --trace).")]
debug: bool,

#[arg(long, requires = "trace")]
/// Decode internal calls in traces (requires --trace).
#[arg(long, requires = "trace", help = "Decode internal calls in traces (requires --trace).")]
decode_internal: bool,

/// Labels to apply to the traces; format: `address:label`.
/// Can only be used with `--trace`.
#[arg(long, requires = "trace")]
/// Labels to apply to the traces; format: `address:label` (requires --trace).
#[arg(long, requires = "trace", help = "Labels to apply to the traces; format: address:label (requires --trace).")]
labels: Vec<String>,

/// The EVM Version to use.
/// Can only be used with `--trace`.
#[arg(long, requires = "trace")]
/// EVM Version to use (requires --trace).
#[arg(long, requires = "trace", value_name = "EVM_VERSION", help = "EVM Version to use (requires --trace).")]
evm_version: Option<EvmVersion>,

/// The block height to query at.
///
/// Can also be the tags earliest, finalized, safe, latest, or pending.
#[arg(long, short)]
/// Block height to query at (number or tag: earliest, latest, pending, etc).
#[arg(long, short, value_name = "BLOCK", help = "Block height to query at (number or tag: earliest, latest, pending, etc).")]
block: Option<BlockId>,

/// Enable Odyssey features.
#[arg(long, alias = "alphanet")]
#[arg(long, alias = "alphanet", help = "Enable Odyssey features.")]
pub odyssey: bool,

#[command(subcommand)]
Expand All @@ -124,63 +117,57 @@ pub struct CallArgs {
eth: EthereumOpts,

/// Use current project artifacts for trace decoding.
#[arg(long, visible_alias = "la")]
#[arg(long, visible_alias = "la", help = "Use current project artifacts for trace decoding.")]
pub with_local_artifacts: bool,

/// Override the balance of an account.
/// Format: address:balance
#[arg(long = "override-balance", value_name = "ADDRESS:BALANCE")]
/// Override the balance of an account. Format: address:balance
#[arg(long = "override-balance", value_name = "ADDRESS:BALANCE", help = "Override the balance of an account. Format: address:balance")]
pub balance_overrides: Option<Vec<String>>,

/// Override the nonce of an account.
/// Format: address:nonce
#[arg(long = "override-nonce", value_name = "ADDRESS:NONCE")]
/// Override the nonce of an account. Format: address:nonce
#[arg(long = "override-nonce", value_name = "ADDRESS:NONCE", help = "Override the nonce of an account. Format: address:nonce")]
pub nonce_overrides: Option<Vec<String>>,

/// Override the code of an account.
/// Format: address:code
#[arg(long = "override-code", value_name = "ADDRESS:CODE")]
/// Override the code of an account. Format: address:code
#[arg(long = "override-code", value_name = "ADDRESS:CODE", help = "Override the code of an account. Format: address:code")]
pub code_overrides: Option<Vec<String>>,

/// Override the state of an account.
/// Format: address:slot:value
#[arg(long = "override-state", value_name = "ADDRESS:SLOT:VALUE")]
/// Override the state of an account. Format: address:slot:value
#[arg(long = "override-state", value_name = "ADDRESS:SLOT:VALUE", help = "Override the state of an account. Format: address:slot:value")]
pub state_overrides: Option<Vec<String>>,

/// Override the state diff of an account.
/// Format: address:slot:value
#[arg(long = "override-state-diff", value_name = "ADDRESS:SLOT:VALUE")]
/// Override the state diff of an account. Format: address:slot:value
#[arg(long = "override-state-diff", value_name = "ADDRESS:SLOT:VALUE", help = "Override the state diff of an account. Format: address:slot:value")]
pub state_diff_overrides: Option<Vec<String>>,

/// Override the block timestamp.
#[arg(long = "block.time", value_name = "TIME")]
#[arg(long = "block.time", value_name = "TIME", help = "Override the block timestamp.")]
pub block_time: Option<u64>,

/// Override the block number.
#[arg(long = "block.number", value_name = "NUMBER")]
#[arg(long = "block.number", value_name = "NUMBER", help = "Override the block number.")]
pub block_number: Option<u64>,
}

#[derive(Debug, Parser)]
pub enum CallSubcommands {
/// ignores the address field and simulates creating a contract
/// Simulate contract creation (ignores the address field).
#[command(name = "--create")]
Create {
/// Bytecode of contract.
/// Bytecode of contract to deploy.
#[arg(help = "Bytecode of contract to deploy.", value_name = "BYTECODE")]
code: String,

/// The signature of the constructor.
/// Constructor signature, e.g. `constructor(uint256)`.
#[arg(help = "Constructor signature.", value_name = "SIG")]
sig: Option<String>,

/// The arguments of the constructor.
/// Arguments for the constructor.
#[arg(help = "Arguments for the constructor.", value_name = "ARGS")]
args: Vec<String>,

/// Ether to send in the transaction.
///
/// Either specified in wei, or as a string with a unit type.
///
/// Examples: 1ether, 10gwei, 0.01ether
#[arg(long, value_parser = parse_ether_value)]
/// Ether to send in the transaction (e.g. 1ether, 10gwei, 0.01ether).
#[arg(long, value_parser = parse_ether_value, value_name = "VALUE", help = "Ether to send in the transaction (e.g. 1ether, 10gwei, 0.01ether).")]
value: Option<U256>,
},
}
Expand Down
57 changes: 38 additions & 19 deletions crates/cast/src/cmd/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,53 @@ use foundry_cli::{
};
use std::{path::PathBuf, str::FromStr};

/// CLI arguments for `cast send`.
/// Send a transaction to a contract or deploy a new contract.
///
/// Example:
///
/// cast send 0xAbC... "transfer(address,uint256)" 0x123... 100 --private-key <KEY> --rpc-url <URL>
/// cast send --create <BYTECODE> --private-key <KEY> --rpc-url <URL>
#[derive(Debug, Parser)]
#[command(
about = "Send a transaction to a contract or deploy a new contract.",
long_about = "Send a transaction to a contract or deploy a new contract.\n\
EXAMPLES:\n\
cast send 0xAbC... 'transfer(address,uint256)' 0x123... 100 --private-key <KEY> --rpc-url <URL>\n\
cast send --create <BYTECODE> --private-key <KEY> --rpc-url <URL>\n\
See more: https://book.getfoundry.sh/reference/cast/cast-send.html"
)]
pub struct SendTxArgs {
/// The destination of the transaction.
/// Destination address of the transaction (contract or EOA).
///
/// If not provided, you must use cast send --create.
#[arg(value_parser = NameOrAddress::from_str)]
/// If not provided, you must use `cast send --create`.
#[arg(help = "Destination address of the transaction.", value_name = "TO", value_parser = NameOrAddress::from_str)]
to: Option<NameOrAddress>,

/// The signature of the function to call.
/// Function signature to call, e.g. `transfer(address,uint256)`.
#[arg(help = "Function signature to call.", value_name = "SIG")]
sig: Option<String>,

/// The arguments of the function to call.
/// Arguments for the function call.
#[arg(help = "Arguments for the function call.", value_name = "ARGS")]
args: Vec<String>,

/// Only print the transaction hash and exit immediately.
#[arg(id = "async", long = "async", alias = "cast-async", env = "CAST_ASYNC")]
#[arg(id = "async", long = "async", alias = "cast-async", env = "CAST_ASYNC", help = "Only print the transaction hash and exit immediately.")]
cast_async: bool,

/// The number of confirmations until the receipt is fetched.
#[arg(long, default_value = "1")]
/// Number of confirmations to wait for the receipt.
#[arg(long, default_value = "1", value_name = "NUM", help = "Number of confirmations to wait for the receipt.")]
confirmations: u64,

#[command(subcommand)]
command: Option<SendTxSubcommands>,

/// Send via `eth_sendTransaction` using the `--from` argument or $ETH_FROM as sender
#[arg(long, requires = "from")]
/// Use `eth_sendTransaction` with an unlocked account (requires --from or $ETH_FROM).
#[arg(long, requires = "from", help = "Send via eth_sendTransaction using an unlocked account.")]
unlocked: bool,

/// Timeout for sending the transaction.
#[arg(long, env = "ETH_TIMEOUT")]
/// Timeout (in seconds) for sending the transaction.
#[arg(long, env = "ETH_TIMEOUT", value_name = "SECONDS", help = "Timeout in seconds for sending the transaction.")]
pub timeout: Option<u64>,

#[command(flatten)]
Expand All @@ -57,29 +72,33 @@ pub struct SendTxArgs {
#[command(flatten)]
eth: EthereumOpts,

/// The path of blob data to be sent.
/// Path to a file containing blob data to be sent.
#[arg(
long,
value_name = "BLOB_DATA_PATH",
conflicts_with = "legacy",
requires = "blob",
help_heading = "Transaction options"
help_heading = "Transaction options",
help = "Path to a file containing blob data to be sent."
)]
path: Option<PathBuf>,
}

#[derive(Debug, Parser)]
pub enum SendTxSubcommands {
/// Use to deploy raw contract bytecode.
/// Deploy raw contract bytecode as a new contract.
#[command(name = "--create")]
Create {
/// The bytecode of the contract to deploy.
/// Bytecode of the contract to deploy.
#[arg(help = "Bytecode of the contract to deploy.", value_name = "BYTECODE")]
code: String,

/// The signature of the function to call.
/// Constructor signature, e.g. `constructor(uint256)`.
#[arg(help = "Constructor signature.", value_name = "SIG")]
sig: Option<String>,

/// The arguments of the function to call.
/// Arguments for the constructor.
#[arg(help = "Arguments for the constructor.", value_name = "ARGS")]
args: Vec<String>,
},
}
Expand Down
49 changes: 32 additions & 17 deletions crates/forge/src/cmd/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,51 +37,66 @@ use std::{borrow::Borrow, marker::PhantomData, path::PathBuf, sync::Arc, time::D

merge_impl_figment_convert!(CreateArgs, build, eth);

/// CLI arguments for `forge create`.
/// Deploy a smart contract to the specified network.
///
/// This command compiles the contract, encodes constructor arguments, and sends a deployment transaction.
///
/// Example:
///
/// forge create src/Counter.sol:Counter --constructor-args 42 --rpc-url <URL> --private-key <KEY> --broadcast
#[derive(Clone, Debug, Parser)]
#[command(
about = "Deploy a smart contract to the specified network.",
long_about = "Deploy a smart contract to the specified network.\n\
Compiles the contract, encodes constructor arguments, and sends a deployment transaction.\n\
EXAMPLES:\n\
forge create src/Counter.sol:Counter --constructor-args 42 --rpc-url <URL> --private-key <KEY> --broadcast\n\
Use --verify to automatically verify the contract after deployment.\n\
See more: https://book.getfoundry.sh/reference/forge/forge-create.html"
)]
pub struct CreateArgs {
/// The contract identifier in the form `<path>:<contractname>`.
/// Contract identifier in the form `<path>:<contractname>`, e.g. `src/Counter.sol:Counter`.
#[arg(help = "Contract identifier in the form <path>:<contractname>.", value_name = "CONTRACT")]
contract: ContractInfo,

/// The constructor arguments.
/// Constructor arguments as a space-separated list, e.g. `42 "hello"`.
#[arg(
long,
num_args(1..),
conflicts_with = "constructor_args_path",
value_name = "ARGS",
allow_hyphen_values = true,
help = "Constructor arguments as a space-separated list."
)]
constructor_args: Vec<String>,

/// The path to a file containing the constructor arguments.
/// Path to a file containing constructor arguments (one per line or as JSON array).
#[arg(
long,
value_hint = ValueHint::FilePath,
value_name = "PATH",
help = "Path to a file containing constructor arguments (one per line or as JSON array)."
)]
constructor_args_path: Option<PathBuf>,

/// Broadcast the transaction.
#[arg(long)]
/// Broadcast the transaction to the network (otherwise, dry-run only).
#[arg(long, help = "Actually send the deployment transaction to the network.")]
pub broadcast: bool,

/// Verify contract after creation.
#[arg(long)]
/// Verify contract after creation (using the selected block explorer).
#[arg(long, help = "Automatically verify the contract after deployment.")]
verify: bool,

/// Send via `eth_sendTransaction` using the `--from` argument or `$ETH_FROM` as sender
#[arg(long, requires = "from")]
/// Use `eth_sendTransaction` with an unlocked account (requires --from or $ETH_FROM).
#[arg(long, requires = "from", help = "Send via eth_sendTransaction using an unlocked account.")]
unlocked: bool,

/// Prints the standard json compiler input if `--verify` is provided.
///
/// The standard json compiler input can be used to manually submit contract verification in
/// the browser.
#[arg(long, requires = "verify")]
/// Print the standard JSON compiler input (for manual verification in a browser).
#[arg(long, requires = "verify", help = "Print the standard JSON compiler input if --verify is provided.")]
show_standard_json_input: bool,

/// Timeout to use for broadcasting transactions.
#[arg(long, env = "ETH_TIMEOUT")]
/// Timeout (in seconds) for broadcasting transactions.
#[arg(long, env = "ETH_TIMEOUT", value_name = "SECONDS", help = "Timeout in seconds for broadcasting transactions.")]
pub timeout: Option<u64>,

#[command(flatten)]
Expand Down
Loading
Loading