@@ -83,6 +83,20 @@ func GetElTokenTransfersByTxHash(ctx context.Context, txHash []byte) ([]*dbtypes
8383 return transfers , nil
8484}
8585
86+ // GetElTokenTransferCountByBlockUidAndTxHash returns the number of token
87+ // transfers for a given block UID and transaction hash.
88+ func GetElTokenTransferCountByBlockUidAndTxHash (ctx context.Context , blockUid uint64 , txHash []byte ) (uint64 , error ) {
89+ var count uint64
90+ err := ReaderDb .GetContext (ctx , & count ,
91+ "SELECT COUNT(*) FROM el_token_transfers WHERE block_uid = $1 AND tx_hash = $2" ,
92+ blockUid , txHash ,
93+ )
94+ if err != nil {
95+ return 0 , err
96+ }
97+ return count , nil
98+ }
99+
86100func GetElTokenTransfersByBlockUid (ctx context.Context , blockUid uint64 ) ([]* dbtypes.ElTokenTransfer , error ) {
87101 transfers := []* dbtypes.ElTokenTransfer {}
88102 err := ReaderDb .SelectContext (ctx , & transfers , "SELECT block_uid, tx_hash, tx_pos, tx_idx, token_id, token_type, token_index, from_id, to_id, amount, amount_raw FROM el_token_transfers WHERE block_uid = $1 ORDER BY tx_pos ASC, tx_idx ASC" , blockUid )
@@ -106,14 +120,15 @@ func GetElTokenTransfersByTokenID(ctx context.Context, tokenID uint64, offset ui
106120 args := []any {tokenID }
107121
108122 // Use window function for count - avoids double scan
123+ // NULLS LAST matches the composite index definition to enable index scans.
109124 fmt .Fprint (& sql , `
110125 SELECT
111126 block_uid, tx_hash, tx_pos, tx_idx, token_id, token_type, token_index,
112127 from_id, to_id, amount, amount_raw,
113128 COUNT(*) OVER() AS total_count
114129 FROM el_token_transfers
115130 WHERE token_id = $1
116- ORDER BY block_uid DESC, tx_pos DESC, tx_idx DESC
131+ ORDER BY block_uid DESC NULLS LAST , tx_pos DESC, tx_idx DESC
117132 LIMIT $2` )
118133 args = append (args , limit )
119134
@@ -160,14 +175,15 @@ func GetElTokenTransfersByAccountID(ctx context.Context, accountID uint64, isFro
160175 }
161176
162177 // Use window function for count - avoids double scan
178+ // NULLS LAST matches the composite index definition to enable index scans.
163179 fmt .Fprintf (& sql , `
164180 SELECT
165181 block_uid, tx_hash, tx_pos, tx_idx, token_id, token_type, token_index,
166182 from_id, to_id, amount, amount_raw,
167183 COUNT(*) OVER() AS total_count
168184 FROM el_token_transfers
169185 WHERE %s = $1
170- ORDER BY block_uid DESC, tx_pos DESC, tx_idx DESC
186+ ORDER BY block_uid DESC NULLS LAST , tx_pos DESC, tx_idx DESC
171187 LIMIT $2` , column )
172188 args = append (args , limit )
173189
@@ -260,7 +276,7 @@ func GetElTokenTransfersFiltered(ctx context.Context, offset uint64, limit uint3
260276 FROM cte
261277 UNION ALL SELECT * FROM (
262278 SELECT * FROM cte
263- ORDER BY block_uid DESC, tx_pos DESC, tx_idx DESC
279+ ORDER BY block_uid DESC NULLS LAST , tx_pos DESC, tx_idx DESC
264280 LIMIT $%v` , len (args ))
265281
266282 if offset > 0 {
@@ -295,7 +311,10 @@ const MaxAccountTokenTransferCount = 100000
295311func GetElTokenTransfersByAccountIDCombined (ctx context.Context , accountID uint64 , tokenTypes []uint8 , offset uint64 , limit uint32 ) ([]* dbtypes.ElTokenTransfer , uint64 , bool , error ) {
296312 // Use UNION ALL instead of OR for better index usage.
297313 // The second query excludes rows where from_id = accountID to avoid duplicates.
314+ // Push LIMIT into each UNION branch so PG can use composite indexes efficiently.
315+ // NULLS LAST is required to match the index definition and enable index scans.
298316 var sql strings.Builder
317+ innerLimit := offset + uint64 (limit )
299318 args := []any {accountID , accountID , accountID }
300319
301320 // Build token type filter clause
@@ -312,20 +331,27 @@ func GetElTokenTransfersByAccountIDCombined(ctx context.Context, accountID uint6
312331 tokenTypeFilter = fmt .Sprintf (" AND token_type IN (%s)" , tokenTypeArgs .String ())
313332 }
314333
334+ args = append (args , innerLimit )
335+ innerLimitIdx := len (args )
336+
315337 fmt .Fprintf (& sql , `
316338 SELECT block_uid, tx_hash, tx_pos, tx_idx, token_id, token_type, token_index,
317339 from_id, to_id, amount, amount_raw
318340 FROM (
319- SELECT block_uid, tx_hash, tx_pos, tx_idx, token_id, token_type, token_index,
341+ ( SELECT block_uid, tx_hash, tx_pos, tx_idx, token_id, token_type, token_index,
320342 from_id, to_id, amount, amount_raw
321343 FROM el_token_transfers WHERE from_id = $1%s
344+ ORDER BY block_uid DESC NULLS LAST
345+ LIMIT $%d)
322346 UNION ALL
323- SELECT block_uid, tx_hash, tx_pos, tx_idx, token_id, token_type, token_index,
347+ ( SELECT block_uid, tx_hash, tx_pos, tx_idx, token_id, token_type, token_index,
324348 from_id, to_id, amount, amount_raw
325349 FROM el_token_transfers WHERE to_id = $2 AND from_id != $3%s
350+ ORDER BY block_uid DESC NULLS LAST
351+ LIMIT $%d)
326352 ) combined
327353 ORDER BY block_uid DESC, tx_pos DESC, tx_idx DESC
328- LIMIT $%d` , tokenTypeFilter , tokenTypeFilter , len (args )+ 1 )
354+ LIMIT $%d` , tokenTypeFilter , innerLimitIdx , tokenTypeFilter , innerLimitIdx , len (args )+ 1 )
329355 args = append (args , limit )
330356
331357 if offset > 0 {
0 commit comments