@@ -7,8 +7,10 @@ use alloy::{
77 signers:: SignerSync ,
88} ;
99use alloy_network:: { TransactionBuilder , TransactionBuilder7702 } ;
10- use alloy_rpc_types:: TransactionRequest ;
10+ use alloy_rpc_types:: { Block , BlockNumberOrTag , EIP1186AccountProofResponse , TransactionRequest } ;
1111use alloy_signer_local:: PrivateKeySigner ;
12+ use reth_primitives_traits:: Account ;
13+ use reth_trie_common:: { AccountProof , StorageProof } ;
1214use url:: Url ;
1315
1416static REPLICA_RPC : LazyLock < Url > = LazyLock :: new ( || {
@@ -127,3 +129,61 @@ async fn test_new_wallet_api() -> Result<(), Box<dyn std::error::Error>> {
127129
128130 Ok ( ( ) )
129131}
132+
133+ #[ tokio:: test]
134+ async fn test_withdrawal_proof_with_fallback ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
135+ if !ci_info:: is_ci ( ) {
136+ return Ok ( ( ) ) ;
137+ }
138+
139+ let provider = ProviderBuilder :: new ( ) . on_http ( REPLICA_RPC . clone ( ) ) ;
140+ let block: Block = provider
141+ . client ( )
142+ . request ( "eth_getBlockByNumber" , ( BlockNumberOrTag :: Latest , false ) )
143+ . await ?;
144+ let block_number = BlockNumberOrTag :: Number ( block. header . number ) ;
145+
146+ // Withdrawal contract will return an empty account proof, since it only handles storage proofs
147+ let withdrawal_contract_response: EIP1186AccountProofResponse = provider
148+ . client ( )
149+ . request (
150+ "eth_getProof" ,
151+ ( odyssey_common:: WITHDRAWAL_CONTRACT , vec ! [ B256 :: ZERO ] , block_number) ,
152+ )
153+ . await ?;
154+
155+ assert ! ( withdrawal_contract_response. account_proof. is_empty( ) ) ;
156+ assert ! ( !withdrawal_contract_response. storage_proof. is_empty( ) ) ;
157+
158+ let storage_root = withdrawal_contract_response. storage_hash ;
159+ for proof in withdrawal_contract_response. storage_proof {
160+ StorageProof :: new ( proof. key . as_b256 ( ) ) . with_proof ( proof. proof ) . verify ( storage_root) ?
161+ }
162+
163+ // If not targeting the withdrawal contract, it defaults back to the standard getProof
164+ // implementation
165+ let signer = PrivateKeySigner :: from_bytes ( & b256 ! (
166+ "59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
167+ ) ) ?;
168+
169+ let eoa_response: EIP1186AccountProofResponse = provider
170+ . client ( )
171+ . request ( "eth_getProof" , ( signer. address ( ) , [ 0 ; 0 ] , block_number) )
172+ . await
173+ . unwrap ( ) ;
174+
175+ assert ! ( !eoa_response. account_proof. is_empty( ) ) ;
176+ AccountProof {
177+ address : signer. address ( ) ,
178+ info : Some ( Account {
179+ nonce : eoa_response. nonce ,
180+ balance : eoa_response. balance ,
181+ bytecode_hash : Some ( eoa_response. code_hash ) ,
182+ } ) ,
183+ proof : eoa_response. account_proof ,
184+ ..Default :: default ( )
185+ }
186+ . verify ( block. header . state_root ) ?;
187+
188+ Ok ( ( ) )
189+ }
0 commit comments