@@ -9,7 +9,9 @@ use katana_pool_api::{
99 PendingTransactions , PendingTx , PoolError , PoolOrd , PoolResult , PoolTransaction , Subscription ,
1010 TransactionPool , TxId ,
1111} ;
12+ use katana_primitives:: contract:: Nonce ;
1213use katana_primitives:: transaction:: TxHash ;
14+ use katana_primitives:: ContractAddress ;
1315use parking_lot:: RwLock ;
1416use tokio:: sync:: mpsc;
1517use tracing:: { error, trace, warn, Instrument } ;
@@ -227,6 +229,17 @@ where
227229 fn validator ( & self ) -> & Self :: Validator {
228230 & self . inner . validator
229231 }
232+
233+ fn get_nonce ( & self , address : ContractAddress ) -> Option < Nonce > {
234+ self . inner
235+ . transactions
236+ . read ( )
237+ . iter ( )
238+ . filter ( |tx| tx. tx . sender ( ) == address)
239+ . map ( |tx| tx. tx . nonce ( ) )
240+ . max ( )
241+ . map ( |max_nonce| max_nonce + 1 )
242+ }
230243}
231244
232245impl < T , V , O > Clone for Pool < T , V , O >
@@ -494,4 +507,97 @@ mod tests {
494507 #[ test]
495508 #[ ignore = "Txs dependency management not fully implemented yet" ]
496509 fn dependent_txs_random_insertion ( ) { }
510+
511+ #[ tokio:: test]
512+ async fn get_nonce_returns_none_for_unknown_address ( ) {
513+ let pool = TestPool :: test ( ) ;
514+ let unknown_address = ContractAddress :: from ( Felt :: from_hex ( "0xdead" ) . unwrap ( ) ) ;
515+ assert_eq ! ( pool. get_nonce( unknown_address) , None ) ;
516+ }
517+
518+ #[ tokio:: test]
519+ async fn get_nonce_returns_next_nonce_for_single_pending_tx ( ) {
520+ let pool = TestPool :: test ( ) ;
521+ let sender = ContractAddress :: from ( Felt :: from_hex ( "0x1337" ) . unwrap ( ) ) ;
522+ let nonce = Nonce :: from ( 5u128 ) ;
523+
524+ let tx = PoolTx :: new ( ) . with_sender ( sender) . with_nonce ( nonce) ;
525+ pool. add_transaction ( tx) . await . unwrap ( ) ;
526+
527+ // Should return nonce + 1
528+ assert_eq ! ( pool. get_nonce( sender) , Some ( Nonce :: from( 6u128 ) ) ) ;
529+ }
530+
531+ #[ tokio:: test]
532+ async fn get_nonce_returns_highest_nonce_plus_one_for_multiple_txs ( ) {
533+ let pool = TestPool :: test ( ) ;
534+ let sender = ContractAddress :: from ( Felt :: from_hex ( "0x1337" ) . unwrap ( ) ) ;
535+
536+ // Add transactions with nonces 1, 5, 3 (out of order)
537+ let tx1 = PoolTx :: new ( ) . with_sender ( sender) . with_nonce ( Nonce :: from ( 1u128 ) ) ;
538+ let tx2 = PoolTx :: new ( ) . with_sender ( sender) . with_nonce ( Nonce :: from ( 3u128 ) ) ;
539+ let tx3 = PoolTx :: new ( ) . with_sender ( sender) . with_nonce ( Nonce :: from ( 2u128 ) ) ;
540+
541+ pool. add_transaction ( tx1) . await . unwrap ( ) ;
542+ pool. add_transaction ( tx2) . await . unwrap ( ) ;
543+ pool. add_transaction ( tx3) . await . unwrap ( ) ;
544+
545+ // Should return max(1, 3, 2) + 1 = 4
546+ assert_eq ! ( pool. get_nonce( sender) , Some ( Nonce :: from( 4u128 ) ) ) ;
547+ }
548+
549+ #[ tokio:: test]
550+ async fn get_nonce_isolated_per_address ( ) {
551+ let pool = TestPool :: test ( ) ;
552+
553+ let sender1 = ContractAddress :: from ( Felt :: from_hex ( "0x1" ) . unwrap ( ) ) ;
554+ let sender2 = ContractAddress :: from ( Felt :: from_hex ( "0x2" ) . unwrap ( ) ) ;
555+ let sender3 = ContractAddress :: from ( Felt :: from_hex ( "0x3" ) . unwrap ( ) ) ;
556+
557+ // Add transactions from different senders
558+ let tx1 = PoolTx :: new ( ) . with_sender ( sender1) . with_nonce ( Nonce :: from ( 10u128 ) ) ;
559+ let tx2 = PoolTx :: new ( ) . with_sender ( sender2) . with_nonce ( Nonce :: from ( 20u128 ) ) ;
560+ let tx3 = PoolTx :: new ( ) . with_sender ( sender1) . with_nonce ( Nonce :: from ( 15u128 ) ) ;
561+
562+ pool. add_transaction ( tx1) . await . unwrap ( ) ;
563+ pool. add_transaction ( tx2) . await . unwrap ( ) ;
564+ pool. add_transaction ( tx3) . await . unwrap ( ) ;
565+
566+ // Each sender should have their own max nonce
567+ assert_eq ! ( pool. get_nonce( sender1) , Some ( Nonce :: from( 16u128 ) ) ) ; // max(10, 15) + 1
568+ assert_eq ! ( pool. get_nonce( sender2) , Some ( Nonce :: from( 21u128 ) ) ) ; // 20 + 1
569+ assert_eq ! ( pool. get_nonce( sender3) , None ) ; // No txs from sender3
570+ }
571+
572+ #[ tokio:: test]
573+ async fn get_nonce_updates_after_transaction_removal ( ) {
574+ let pool = TestPool :: test ( ) ;
575+ let sender = ContractAddress :: from ( Felt :: from_hex ( "0x1337" ) . unwrap ( ) ) ;
576+
577+ let tx1 = PoolTx :: new ( ) . with_sender ( sender) . with_nonce ( Nonce :: from ( 1u128 ) ) ;
578+ let tx2 = PoolTx :: new ( ) . with_sender ( sender) . with_nonce ( Nonce :: from ( 2u128 ) ) ;
579+ let tx3 = PoolTx :: new ( ) . with_sender ( sender) . with_nonce ( Nonce :: from ( 3u128 ) ) ;
580+
581+ let hash1 = tx1. hash ( ) ;
582+ let hash3 = tx3. hash ( ) ;
583+
584+ pool. add_transaction ( tx1) . await . unwrap ( ) ;
585+ pool. add_transaction ( tx2. clone ( ) ) . await . unwrap ( ) ;
586+ pool. add_transaction ( tx3) . await . unwrap ( ) ;
587+
588+ // Should be 4 (max nonce 3 + 1)
589+ assert_eq ! ( pool. get_nonce( sender) , Some ( Nonce :: from( 4u128 ) ) ) ;
590+
591+ // Remove transactions with nonce 1 and 3
592+ pool. remove_transactions ( & [ hash1, hash3] ) ;
593+
594+ // Should now be 3 (only tx with nonce 2 remains)
595+ assert_eq ! ( pool. get_nonce( sender) , Some ( Nonce :: from( 3u128 ) ) ) ;
596+
597+ // Remove last transaction
598+ pool. remove_transactions ( & [ tx2. hash ( ) ] ) ;
599+
600+ // Should be None (no transactions left)
601+ assert_eq ! ( pool. get_nonce( sender) , None ) ;
602+ }
497603}
0 commit comments