foundry_cli/opts/
transaction.rs1use std::str::FromStr;
2
3use super::TempoOpts;
4use crate::utils::{parse_ether_value, parse_json};
5use alloy_eips::{eip2930::AccessList, eip7702::SignedAuthorization};
6use alloy_network::{Network, TransactionBuilder};
7use alloy_primitives::{Address, U64, U256, hex};
8use alloy_rlp::Decodable;
9use clap::Parser;
10use foundry_common::FoundryTransactionBuilder;
11
12#[derive(Clone, Debug)]
15pub enum CliAuthorizationList {
16 Address(Address),
18 Signed(SignedAuthorization),
20}
21
22impl FromStr for CliAuthorizationList {
23 type Err = eyre::Error;
24
25 fn from_str(s: &str) -> Result<Self, Self::Err> {
26 if let Ok(addr) = Address::from_str(s) {
27 Ok(Self::Address(addr))
28 } else if let Ok(auth) = SignedAuthorization::decode(&mut hex::decode(s)?.as_ref()) {
29 Ok(Self::Signed(auth))
30 } else {
31 eyre::bail!("Failed to decode authorization")
32 }
33 }
34}
35
36#[derive(Clone, Debug, Parser)]
37#[command(next_help_heading = "Transaction options")]
38pub struct TransactionOpts {
39 #[arg(long, env = "ETH_GAS_LIMIT")]
41 pub gas_limit: Option<U256>,
42
43 #[arg(
48 long,
49 env = "ETH_GAS_PRICE",
50 value_parser = parse_ether_value,
51 value_name = "PRICE"
52 )]
53 pub gas_price: Option<U256>,
54
55 #[arg(
57 long,
58 env = "ETH_PRIORITY_GAS_PRICE",
59 value_parser = parse_ether_value,
60 value_name = "PRICE"
61 )]
62 pub priority_gas_price: Option<U256>,
63
64 #[arg(long, value_parser = parse_ether_value)]
70 pub value: Option<U256>,
71
72 #[arg(long)]
74 pub nonce: Option<U64>,
75
76 #[arg(long)]
80 pub legacy: bool,
81
82 #[arg(long, conflicts_with = "legacy")]
86 pub blob: bool,
87
88 #[arg(long, conflicts_with = "legacy", requires = "blob")]
91 pub eip4844: bool,
92
93 #[arg(long, conflicts_with = "legacy", value_parser = parse_ether_value, env = "ETH_BLOB_GAS_PRICE", value_name = "BLOB_PRICE")]
95 pub blob_gas_price: Option<U256>,
96
97 #[arg(long, conflicts_with_all = &["legacy", "blob"])]
101 pub auth: Vec<CliAuthorizationList>,
102
103 #[arg(long, value_parser = parse_json::<AccessList>)]
109 pub access_list: Option<Option<AccessList>>,
110
111 #[command(flatten)]
112 pub tempo: TempoOpts,
113}
114
115impl TransactionOpts {
116 pub fn apply<N: Network>(&self, tx: &mut N::TransactionRequest, legacy: bool)
118 where
119 N::TransactionRequest: FoundryTransactionBuilder<N>,
120 {
121 if let Some(gas_limit) = self.gas_limit {
122 tx.set_gas_limit(gas_limit.to());
123 }
124
125 if let Some(value) = self.value {
126 tx.set_value(value);
127 }
128
129 if let Some(gas_price) = self.gas_price {
130 if legacy {
131 tx.set_gas_price(gas_price.to());
132 } else {
133 tx.set_max_fee_per_gas(gas_price.to());
134 }
135 }
136
137 if !legacy && let Some(priority_fee) = self.priority_gas_price {
138 tx.set_max_priority_fee_per_gas(priority_fee.to());
139 }
140
141 if let Some(max_blob_fee) = self.blob_gas_price {
142 tx.set_max_fee_per_blob_gas(max_blob_fee.to())
143 }
144
145 self.tempo.apply::<N>(tx, self.nonce.map(|n| n.to()));
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn parse_priority_gas_tx_opts() {
156 let args: TransactionOpts =
157 TransactionOpts::parse_from(["foundry-cli", "--priority-gas-price", "100"]);
158 assert!(args.priority_gas_price.is_some());
159 }
160}