@@ -2,6 +2,7 @@ use axum::{
22 extract:: { Path , Query , State } ,
33 Json ,
44} ;
5+ use serde:: Deserialize ;
56use std:: sync:: Arc ;
67
78use super :: get_table_count;
@@ -11,28 +12,101 @@ use atlas_common::{
1112 AtlasError , Erc20Transfer , NftTransfer , PaginatedResponse , Pagination , Transaction ,
1213} ;
1314
15+ /// Query parameters for the transaction list endpoint.
16+ /// Supports cursor-based pagination via (block_number, block_index) for O(log N)
17+ /// seeks on large partitioned tables, replacing O(N) OFFSET scans.
18+ #[ derive( Debug , Deserialize ) ]
19+ pub struct TransactionListParams {
20+ #[ serde( default = "default_page" ) ]
21+ pub page : u32 ,
22+ #[ serde( default = "default_limit" ) ]
23+ pub limit : u32 ,
24+ /// Fetch transactions older than this (block_number, block_index).
25+ pub before_block : Option < i64 > ,
26+ pub before_index : Option < i32 > ,
27+ /// Fetch transactions newer than this (block_number, block_index).
28+ pub after_block : Option < i64 > ,
29+ pub after_index : Option < i32 > ,
30+ /// When true, fetch the oldest page of transactions.
31+ #[ serde( default ) ]
32+ pub last_page : bool ,
33+ }
34+
35+ fn default_page ( ) -> u32 {
36+ 1
37+ }
38+ fn default_limit ( ) -> u32 {
39+ 20
40+ }
41+
1442pub async fn list_transactions (
1543 State ( state) : State < Arc < AppState > > ,
16- Query ( pagination ) : Query < Pagination > ,
44+ Query ( params ) : Query < TransactionListParams > ,
1745) -> ApiResult < Json < PaginatedResponse < Transaction > > > {
18- // Use optimized count (approximate for large tables, exact for small)
1946 let total = get_table_count ( & state. pool , "transactions" ) . await ?;
20-
21- let transactions: Vec < Transaction > = sqlx:: query_as (
22- "SELECT hash, block_number, block_index, from_address, to_address, value, gas_price, gas_used, input_data, status, contract_created, timestamp
23- FROM transactions
24- ORDER BY block_number DESC, block_index DESC
25- LIMIT $1 OFFSET $2"
26- )
27- . bind ( pagination. limit ( ) )
28- . bind ( pagination. offset ( ) )
29- . fetch_all ( & state. pool )
30- . await ?;
47+ let limit = params. limit . min ( 100 ) as i64 ;
48+
49+ let transactions: Vec < Transaction > =
50+ if let ( Some ( bb) , Some ( bi) ) = ( params. before_block , params. before_index ) {
51+ // Next page: transactions older than cursor
52+ sqlx:: query_as (
53+ "SELECT hash, block_number, block_index, from_address, to_address, value, gas_price, gas_used, input_data, status, contract_created, timestamp
54+ FROM transactions
55+ WHERE (block_number, block_index) < ($1, $2)
56+ ORDER BY block_number DESC, block_index DESC
57+ LIMIT $3" ,
58+ )
59+ . bind ( bb)
60+ . bind ( bi)
61+ . bind ( limit)
62+ . fetch_all ( & state. pool )
63+ . await ?
64+ } else if let ( Some ( ab) , Some ( ai) ) = ( params. after_block , params. after_index ) {
65+ // Prev page: transactions newer than cursor (fetch ASC, reverse)
66+ let mut txs: Vec < Transaction > = sqlx:: query_as (
67+ "SELECT hash, block_number, block_index, from_address, to_address, value, gas_price, gas_used, input_data, status, contract_created, timestamp
68+ FROM transactions
69+ WHERE (block_number, block_index) > ($1, $2)
70+ ORDER BY block_number ASC, block_index ASC
71+ LIMIT $3" ,
72+ )
73+ . bind ( ab)
74+ . bind ( ai)
75+ . bind ( limit)
76+ . fetch_all ( & state. pool )
77+ . await ?;
78+ txs. reverse ( ) ;
79+ txs
80+ } else if params. last_page {
81+ // Last page: oldest transactions (fetch ASC, reverse)
82+ let mut txs: Vec < Transaction > = sqlx:: query_as (
83+ "SELECT hash, block_number, block_index, from_address, to_address, value, gas_price, gas_used, input_data, status, contract_created, timestamp
84+ FROM transactions
85+ ORDER BY block_number ASC, block_index ASC
86+ LIMIT $1" ,
87+ )
88+ . bind ( limit)
89+ . fetch_all ( & state. pool )
90+ . await ?;
91+ txs. reverse ( ) ;
92+ txs
93+ } else {
94+ // First page (default): newest transactions
95+ sqlx:: query_as (
96+ "SELECT hash, block_number, block_index, from_address, to_address, value, gas_price, gas_used, input_data, status, contract_created, timestamp
97+ FROM transactions
98+ ORDER BY block_number DESC, block_index DESC
99+ LIMIT $1" ,
100+ )
101+ . bind ( limit)
102+ . fetch_all ( & state. pool )
103+ . await ?
104+ } ;
31105
32106 Ok ( Json ( PaginatedResponse :: new (
33107 transactions,
34- pagination . page ,
35- pagination . limit ,
108+ params . page ,
109+ params . limit ,
36110 total,
37111 ) ) )
38112}
0 commit comments