@@ -838,6 +838,146 @@ async fn test_duplicate_flashblock_ignored() {
838838 assert_eq ! ( block, block_two) ;
839839}
840840
841+ /// Verifies that eth_call targeting pending block sees flashblock state changes.
842+ ///
843+ /// This test catches database layering bugs where pending state from flashblocks
844+ /// isn't visible to RPC callers. After a flashblock transfers ETH to Bob, an
845+ /// eth_call simulating a transfer FROM Bob should succeed because Bob now has
846+ /// more funds from the flashblock.
847+ #[ tokio:: test]
848+ async fn test_eth_call_sees_flashblock_state_changes ( ) {
849+ use alloy_eips:: BlockNumberOrTag ;
850+ use alloy_provider:: Provider ;
851+ use alloy_rpc_types_eth:: TransactionInput ;
852+ use op_alloy_rpc_types:: OpTransactionRequest ;
853+
854+ let test = TestHarness :: new ( ) . await ;
855+ let provider = test. node . provider ( ) ;
856+
857+ let bob_address = test. address ( User :: Bob ) ;
858+ let charlie_address = test. address ( User :: Charlie ) ;
859+
860+ // Get Bob's canonical balance to calculate a transfer amount that exceeds it
861+ let canonical_balance = provider. get_balance ( bob_address) . await . unwrap ( ) ;
862+
863+ // Send base flashblock
864+ test. send_flashblock ( FlashblockBuilder :: new_base ( & test) . build ( ) ) . await ;
865+
866+ // Flashblock 1: Alice sends a large amount to Bob
867+ let transfer_to_bob = 1_000_000_000_000_000_000u128 ; // 1 ETH
868+ let tx = test. build_transaction_to_send_eth_with_nonce (
869+ User :: Alice ,
870+ User :: Bob ,
871+ transfer_to_bob,
872+ 0 ,
873+ ) ;
874+ test. send_flashblock (
875+ FlashblockBuilder :: new ( & test, 1 )
876+ . with_transactions ( vec ! [ tx] )
877+ . build ( ) ,
878+ )
879+ . await ;
880+
881+ // Verify via state overrides that Bob received the funds
882+ let overrides = test
883+ . flashblocks
884+ . get_pending_blocks ( )
885+ . get_state_overrides ( )
886+ . expect ( "state overrides should exist after flashblock execution" ) ;
887+ let bob_override = overrides. get ( & bob_address) . expect ( "Bob should have a state override" ) ;
888+ let bob_pending_balance = bob_override. balance . expect ( "Bob's balance override should be set" ) ;
889+ assert_eq ! (
890+ bob_pending_balance,
891+ canonical_balance + U256 :: from( transfer_to_bob) ,
892+ "State override should show Bob's increased balance"
893+ ) ;
894+
895+ // Now the key test: eth_call from Bob should see this pending balance.
896+ // Try to transfer more than Bob's canonical balance (but less than pending).
897+ // This would fail if eth_call can't see the pending state.
898+ let transfer_amount = canonical_balance + U256 :: from ( 100_000u64 ) ;
899+ let call_request = OpTransactionRequest :: default ( )
900+ . from ( bob_address)
901+ . to ( charlie_address)
902+ . value ( transfer_amount)
903+ . gas_limit ( 21_000 )
904+ . input ( TransactionInput :: default ( ) ) ;
905+
906+ let result = provider. call ( call_request) . block ( BlockNumberOrTag :: Pending . into ( ) ) . await ;
907+ assert ! (
908+ result. is_ok( ) ,
909+ "eth_call from Bob should succeed because pending state shows increased balance. \
910+ If this fails, eth_call may not be seeing flashblock state changes. Error: {:?}",
911+ result. err( )
912+ ) ;
913+ }
914+
915+ /// Verifies that transactions in flashblock N+1 can see state changes from flashblock N.
916+ ///
917+ /// This test catches database layering bugs where writes from earlier flashblocks
918+ /// aren't visible to later flashblock execution. The key is that flashblock 2's
919+ /// transaction uses nonce=1, which only succeeds if the execution layer sees
920+ /// flashblock 1's transaction (which used nonce=0).
921+ #[ tokio:: test]
922+ async fn test_sequential_nonces_across_flashblocks ( ) {
923+ let test = TestHarness :: new ( ) . await ;
924+
925+ // Send base flashblock
926+ test. send_flashblock ( FlashblockBuilder :: new_base ( & test) . build ( ) ) . await ;
927+
928+ // Flashblock 1: Alice sends to Bob with nonce 0
929+ let tx_nonce_0 = test. build_transaction_to_send_eth_with_nonce ( User :: Alice , User :: Bob , 1000 , 0 ) ;
930+ test. send_flashblock (
931+ FlashblockBuilder :: new ( & test, 1 )
932+ . with_transactions ( vec ! [ tx_nonce_0] )
933+ . build ( ) ,
934+ )
935+ . await ;
936+
937+ // Verify flashblock 1 was processed - Alice's pending nonce should now be 1
938+ let alice_state = test. account_state ( User :: Alice ) ;
939+ assert_eq ! (
940+ alice_state. nonce, 1 ,
941+ "After flashblock 1, Alice's pending nonce should be 1"
942+ ) ;
943+
944+ // Flashblock 2: Alice sends to Charlie with nonce 1
945+ // This will FAIL if the execution layer can't see flashblock 1's state change
946+ let tx_nonce_1 =
947+ test. build_transaction_to_send_eth_with_nonce ( User :: Alice , User :: Charlie , 2000 , 1 ) ;
948+ test. send_flashblock (
949+ FlashblockBuilder :: new ( & test, 2 )
950+ . with_transactions ( vec ! [ tx_nonce_1] )
951+ . build ( ) ,
952+ )
953+ . await ;
954+
955+ // Verify flashblock 2 was processed - Alice's pending nonce should now be 2
956+ let alice_state_after = test. account_state ( User :: Alice ) ;
957+ assert_eq ! (
958+ alice_state_after. nonce, 2 ,
959+ "After flashblock 2, Alice's pending nonce should be 2. \
960+ If this fails, the database layering may be preventing flashblock 2 \
961+ from seeing flashblock 1's state changes."
962+ ) ;
963+
964+ // Also verify Bob and Charlie received their funds
965+ let overrides = test
966+ . flashblocks
967+ . get_pending_blocks ( )
968+ . get_state_overrides ( )
969+ . expect ( "state overrides should exist" ) ;
970+
971+ assert ! (
972+ overrides. get( & test. address( User :: Bob ) ) . is_some( ) ,
973+ "Bob should have received funds from flashblock 1"
974+ ) ;
975+ assert ! (
976+ overrides. get( & test. address( User :: Charlie ) ) . is_some( ) ,
977+ "Charlie should have received funds from flashblock 2"
978+ ) ;
979+ }
980+
841981#[ tokio:: test]
842982async fn test_progress_canonical_blocks_without_flashblocks ( ) {
843983 let mut test = TestHarness :: new ( ) . await ;
0 commit comments