1use alloy_consensus::{EnvKzgSettings, SidecarBuilder, SimpleCoder, TxEip4844Variant, TxEnvelope};
2use alloy_eips::eip7702::SignedAuthorization;
3use alloy_network::{
4 eip2718::Encodable2718, Ethereum, EthereumWallet, TransactionBuilder, TransactionBuilder4844,
5};
6use alloy_primitives::{hex, Address, Bytes, TxKind, B256, U256};
7use alloy_rpc_types_eth::{Authorization, TransactionInput, TransactionRequest};
8use alloy_signer::SignerSync;
9use alloy_signer_local::PrivateKeySigner;
10use eyre::Ok;
11
12#[derive(Debug)]
14pub struct TransactionTestContext;
15
16impl TransactionTestContext {
17 pub async fn transfer_tx(chain_id: u64, wallet: PrivateKeySigner) -> TxEnvelope {
19 let tx = tx(chain_id, 21000, None, None, 0);
20 Self::sign_tx(wallet, tx).await
21 }
22
23 pub async fn transfer_tx_bytes(chain_id: u64, wallet: PrivateKeySigner) -> Bytes {
25 let signed = Self::transfer_tx(chain_id, wallet).await;
26 signed.encoded_2718().into()
27 }
28
29 pub async fn deploy_tx(
31 chain_id: u64,
32 gas: u64,
33 init_code: Bytes,
34 wallet: PrivateKeySigner,
35 ) -> TxEnvelope {
36 let tx = tx(chain_id, gas, Some(init_code), None, 0);
37 Self::sign_tx(wallet, tx).await
38 }
39
40 pub async fn deploy_tx_bytes(
42 chain_id: u64,
43 gas: u64,
44 init_code: Bytes,
45 wallet: PrivateKeySigner,
46 ) -> Bytes {
47 let signed = Self::deploy_tx(chain_id, gas, init_code, wallet).await;
48 signed.encoded_2718().into()
49 }
50
51 pub async fn set_code_tx(
55 chain_id: u64,
56 delegate_to: Address,
57 wallet: PrivateKeySigner,
58 ) -> TxEnvelope {
59 let authorization =
60 Authorization { chain_id: U256::from(chain_id), address: delegate_to, nonce: 0 };
61 let signature = wallet
62 .sign_hash_sync(&authorization.signature_hash())
63 .expect("could not sign authorization");
64 let tx = tx(chain_id, 48100, None, Some(authorization.into_signed(signature)), 0);
65 Self::sign_tx(wallet, tx).await
66 }
67
68 pub async fn set_code_tx_bytes(
72 chain_id: u64,
73 delegate_to: Address,
74 wallet: PrivateKeySigner,
75 ) -> Bytes {
76 let signed = Self::set_code_tx(chain_id, delegate_to, wallet).await;
77 signed.encoded_2718().into()
78 }
79
80 pub async fn tx_with_blobs(
82 chain_id: u64,
83 wallet: PrivateKeySigner,
84 ) -> eyre::Result<TxEnvelope> {
85 let mut tx = tx(chain_id, 210000, None, None, 0);
86
87 let mut builder = SidecarBuilder::<SimpleCoder>::new();
88 builder.ingest(b"dummy blob");
89 tx.set_blob_sidecar(builder.build()?);
90 tx.set_max_fee_per_blob_gas(15e9 as u128);
91
92 let signed = Self::sign_tx(wallet, tx).await;
93 Ok(signed)
94 }
95
96 pub async fn sign_tx(wallet: PrivateKeySigner, tx: TransactionRequest) -> TxEnvelope {
98 let signer = EthereumWallet::from(wallet);
99 <TransactionRequest as TransactionBuilder<Ethereum>>::build(tx, &signer).await.unwrap()
100 }
101
102 pub async fn tx_with_blobs_bytes(
104 chain_id: u64,
105 wallet: PrivateKeySigner,
106 ) -> eyre::Result<Bytes> {
107 let signed = Self::tx_with_blobs(chain_id, wallet).await?;
108
109 Ok(signed.encoded_2718().into())
110 }
111
112 pub async fn optimism_l1_block_info_tx(
114 chain_id: u64,
115 wallet: PrivateKeySigner,
116 nonce: u64,
117 ) -> Bytes {
118 let l1_block_info = Bytes::from_static(&hex!("7ef9015aa044bae9d41b8380d781187b426c6fe43df5fb2fb57bd4466ef6a701e1f01e015694deaddeaddeaddeaddeaddeaddeaddeaddead000194420000000000000000000000000000000000001580808408f0d18001b90104015d8eb900000000000000000000000000000000000000000000000000000000008057650000000000000000000000000000000000000000000000000000000063d96d10000000000000000000000000000000000000000000000000000000000009f35273d89754a1e0387b89520d989d3be9c37c1f32495a88faf1ea05c61121ab0d1900000000000000000000000000000000000000000000000000000000000000010000000000000000000000002d679b567db6187c0c8323fa982cfb88b74dbcc7000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240"));
119 let tx = tx(chain_id, 210000, Some(l1_block_info), None, nonce);
120 let signer = EthereumWallet::from(wallet);
121 <TransactionRequest as TransactionBuilder<Ethereum>>::build(tx, &signer)
122 .await
123 .unwrap()
124 .encoded_2718()
125 .into()
126 }
127
128 #[track_caller]
130 pub fn validate_sidecar(tx: TxEnvelope) -> Vec<B256> {
131 let proof_setting = EnvKzgSettings::Default;
132
133 match tx {
134 TxEnvelope::Eip4844(signed) => match signed.tx() {
135 TxEip4844Variant::TxEip4844WithSidecar(tx) => {
136 tx.validate_blob(proof_setting.get()).unwrap();
137 tx.sidecar.versioned_hashes().collect()
138 }
139 _ => panic!("Expected Eip4844 transaction with sidecar"),
140 },
141 _ => panic!("Expected Eip4844 transaction"),
142 }
143 }
144}
145
146fn tx(
148 chain_id: u64,
149 gas: u64,
150 data: Option<Bytes>,
151 delegate_to: Option<SignedAuthorization>,
152 nonce: u64,
153) -> TransactionRequest {
154 TransactionRequest {
155 nonce: Some(nonce),
156 value: Some(U256::from(100)),
157 to: Some(TxKind::Call(Address::random())),
158 gas: Some(gas),
159 max_fee_per_gas: Some(20e9 as u128),
160 max_priority_fee_per_gas: Some(20e9 as u128),
161 chain_id: Some(chain_id),
162 input: TransactionInput { input: None, data },
163 authorization_list: delegate_to.map(|addr| vec![addr]),
164 ..Default::default()
165 }
166}