@@ -4,8 +4,8 @@ use crate::{
44 abi:: { Multicall , SimpleStorage } ,
55 utils:: { connect_pubsub_with_wallet, http_provider, http_provider_with_signer} ,
66} ;
7- use alloy_consensus:: { SignableTransaction , Transaction , TxEip1559 } ;
8- use alloy_network:: { EthereumWallet , TransactionBuilder , TxSignerSync } ;
7+ use alloy_consensus:: { SidecarBuilder , SignableTransaction , SimpleCoder , Transaction , TxEip1559 } ;
8+ use alloy_network:: { EthereumWallet , TransactionBuilder , TransactionBuilder4844 , TxSignerSync } ;
99use alloy_primitives:: {
1010 Address , B256 , ChainId , U256 , b256, bytes,
1111 map:: { AddressHashMap , B256HashMap , HashMap } ,
@@ -466,3 +466,168 @@ async fn can_get_code_by_hash() {
466466 let code = api. debug_code_by_hash ( code_hash, None ) . await . unwrap ( ) ;
467467 assert_eq ! ( & code. unwrap( ) , foundry_evm:: constants:: DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE ) ;
468468}
469+
470+ #[ tokio:: test( flavor = "multi_thread" ) ]
471+ async fn test_fill_transaction_fills_chain_id ( ) {
472+ let ( api, handle) = spawn ( NodeConfig :: test ( ) ) . await ;
473+ let wallet = handle. dev_wallets ( ) . next ( ) . unwrap ( ) ;
474+ let from = wallet. address ( ) ;
475+
476+ let tx_req = TransactionRequest :: default ( )
477+ . with_from ( from)
478+ . with_to ( Address :: random ( ) )
479+ . with_gas_limit ( 21_000 ) ;
480+
481+ let filled = api. fill_transaction ( WithOtherFields :: new ( tx_req) ) . await . unwrap ( ) ;
482+
483+ // Should fill with the chain id from provider
484+ assert ! ( filled. tx. chain_id( ) . is_some( ) ) ;
485+ assert_eq ! ( filled. tx. chain_id( ) . unwrap( ) , CHAIN_ID ) ;
486+ }
487+
488+ #[ tokio:: test( flavor = "multi_thread" ) ]
489+ async fn test_fill_transaction_fills_nonce ( ) {
490+ let ( api, handle) = spawn ( NodeConfig :: test ( ) ) . await ;
491+
492+ let accounts: Vec < _ > = handle. dev_wallets ( ) . collect ( ) ;
493+ let signer: EthereumWallet = accounts[ 0 ] . clone ( ) . into ( ) ;
494+ let from = accounts[ 0 ] . address ( ) ;
495+ let to = accounts[ 1 ] . address ( ) ;
496+
497+ let provider = http_provider_with_signer ( & handle. http_endpoint ( ) , signer) ;
498+
499+ // Send a transaction to increment nonce
500+ let tx = TransactionRequest :: default ( ) . with_from ( from) . with_to ( to) . with_value ( U256 :: from ( 100 ) ) ;
501+ let tx = WithOtherFields :: new ( tx) ;
502+ provider. send_transaction ( tx) . await . unwrap ( ) . get_receipt ( ) . await . unwrap ( ) ;
503+
504+ // Now the account should have nonce 1
505+ let tx_req = TransactionRequest :: default ( )
506+ . with_from ( from)
507+ . with_to ( to)
508+ . with_value ( U256 :: from ( 1000 ) )
509+ . with_gas_limit ( 21_000 ) ;
510+
511+ let filled = api. fill_transaction ( WithOtherFields :: new ( tx_req) ) . await . unwrap ( ) ;
512+
513+ assert_eq ! ( filled. tx. nonce( ) , 1 ) ;
514+ }
515+
516+ #[ tokio:: test( flavor = "multi_thread" ) ]
517+ async fn test_fill_transaction_preserves_provided_fields ( ) {
518+ let ( api, handle) = spawn ( NodeConfig :: test ( ) ) . await ;
519+ let wallet = handle. dev_wallets ( ) . next ( ) . unwrap ( ) ;
520+ let from = wallet. address ( ) ;
521+
522+ let provided_nonce = 100u64 ;
523+ let provided_gas_limit = 50_000u64 ;
524+
525+ let tx_req = TransactionRequest :: default ( )
526+ . with_from ( from)
527+ . with_to ( Address :: random ( ) )
528+ . with_value ( U256 :: from ( 1000 ) )
529+ . with_nonce ( provided_nonce)
530+ . with_gas_limit ( provided_gas_limit) ;
531+
532+ let filled = api. fill_transaction ( WithOtherFields :: new ( tx_req) ) . await . unwrap ( ) ;
533+
534+ // Should preserve the provided nonce and gas limit
535+ assert_eq ! ( filled. tx. nonce( ) , provided_nonce) ;
536+ assert_eq ! ( filled. tx. gas_limit( ) , provided_gas_limit) ;
537+ }
538+
539+ #[ tokio:: test( flavor = "multi_thread" ) ]
540+ async fn test_fill_transaction_fills_all_missing_fields ( ) {
541+ let ( api, handle) = spawn ( NodeConfig :: test ( ) ) . await ;
542+ let wallet = handle. dev_wallets ( ) . next ( ) . unwrap ( ) ;
543+ let from = wallet. address ( ) ;
544+
545+ // Create a simple transfer transaction with minimal fields
546+ let tx_req = TransactionRequest :: default ( ) . with_from ( from) . with_to ( Address :: random ( ) ) ;
547+
548+ let filled = api. fill_transaction ( WithOtherFields :: new ( tx_req) ) . await . unwrap ( ) ;
549+
550+ // Should fill all required fields and be EIP-1559
551+ assert ! ( filled. tx. is_eip1559( ) ) ;
552+ assert ! ( filled. tx. gas_limit( ) > 0 ) ;
553+ let essentials = filled. tx . essentials ( ) ;
554+ assert ! ( essentials. max_fee_per_gas. is_some( ) ) ;
555+ assert ! ( essentials. max_priority_fee_per_gas. is_some( ) ) ;
556+ }
557+
558+ #[ tokio:: test( flavor = "multi_thread" ) ]
559+ async fn test_fill_transaction_eip4844_blob_fee ( ) {
560+ let node_config = NodeConfig :: test ( ) . with_hardfork ( Some ( EthereumHardfork :: Cancun . into ( ) ) ) ;
561+ let ( api, handle) = spawn ( node_config) . await ;
562+ let wallet = handle. dev_wallets ( ) . next ( ) . unwrap ( ) ;
563+ let from = wallet. address ( ) ;
564+
565+ let mut builder = SidecarBuilder :: < SimpleCoder > :: new ( ) ;
566+ builder. ingest ( b"dummy blob" ) ;
567+ let sidecar = builder. build ( ) . unwrap ( ) ;
568+
569+ // EIP-4844 blob transaction with sidecar but no blob fee
570+ let mut tx_req = TransactionRequest :: default ( ) . with_from ( from) . with_to ( Address :: random ( ) ) ;
571+ tx_req. sidecar = Some ( sidecar) ;
572+ tx_req. transaction_type = Some ( 3 ) ; // EIP-4844
573+
574+ let filled = api. fill_transaction ( WithOtherFields :: new ( tx_req) ) . await . unwrap ( ) ;
575+
576+ // Blob transaction should have max_fee_per_blob_gas filled
577+ assert ! (
578+ filled. tx. max_fee_per_blob_gas( ) . is_some( ) ,
579+ "max_fee_per_blob_gas should be filled for blob tx"
580+ ) ;
581+ let essentials = filled. tx . essentials ( ) ;
582+ assert ! ( essentials. blob_versioned_hashes. is_some( ) , "blob_versioned_hashes should be present" ) ;
583+ }
584+
585+ #[ tokio:: test( flavor = "multi_thread" ) ]
586+ async fn test_fill_transaction_eip4844_preserves_blob_fee ( ) {
587+ let node_config = NodeConfig :: test ( ) . with_hardfork ( Some ( EthereumHardfork :: Cancun . into ( ) ) ) ;
588+ let ( api, handle) = spawn ( node_config) . await ;
589+ let wallet = handle. dev_wallets ( ) . next ( ) . unwrap ( ) ;
590+ let from = wallet. address ( ) ;
591+
592+ let provided_blob_fee = 5_000_000u128 ;
593+
594+ let mut builder = SidecarBuilder :: < SimpleCoder > :: new ( ) ;
595+ builder. ingest ( b"dummy blob" ) ;
596+ let sidecar = builder. build ( ) . unwrap ( ) ;
597+
598+ // EIP-4844 blob transaction with blob fee already set
599+ let mut tx_req = TransactionRequest :: default ( )
600+ . with_from ( from)
601+ . with_to ( Address :: random ( ) )
602+ . with_max_fee_per_blob_gas ( provided_blob_fee) ;
603+ tx_req. sidecar = Some ( sidecar) ;
604+ tx_req. transaction_type = Some ( 3 ) ; // EIP-4844
605+
606+ let filled = api. fill_transaction ( WithOtherFields :: new ( tx_req) ) . await . unwrap ( ) ;
607+
608+ // Should preserve the provided blob fee
609+ assert_eq ! (
610+ filled. tx. max_fee_per_blob_gas( ) ,
611+ Some ( provided_blob_fee) ,
612+ "should preserve provided max_fee_per_blob_gas"
613+ ) ;
614+ }
615+
616+ #[ tokio:: test( flavor = "multi_thread" ) ]
617+ async fn test_fill_transaction_non_blob_tx_no_blob_fee ( ) {
618+ let ( api, handle) = spawn ( NodeConfig :: test ( ) ) . await ;
619+ let wallet = handle. dev_wallets ( ) . next ( ) . unwrap ( ) ;
620+ let from = wallet. address ( ) ;
621+
622+ // EIP-1559 transaction without blob fields
623+ let mut tx_req = TransactionRequest :: default ( ) . with_from ( from) . with_to ( Address :: random ( ) ) ;
624+ tx_req. transaction_type = Some ( 2 ) ; // EIP-1559
625+
626+ let filled = api. fill_transaction ( WithOtherFields :: new ( tx_req) ) . await . unwrap ( ) ;
627+
628+ // Non-blob transaction should NOT have blob fee filled
629+ assert ! (
630+ filled. tx. max_fee_per_blob_gas( ) . is_none( ) ,
631+ "max_fee_per_blob_gas should not be set for non-blob tx"
632+ ) ;
633+ }
0 commit comments