@@ -730,7 +730,7 @@ async def _query_active_contracts_via_updates(
730730 party : str ,
731731 extract : Callable [[str , dict , str ], Optional [_T ]],
732732 stop_when : Optional [Callable [[_T ], bool ]] = None ,
733- page_size : int = 1000 ,
733+ page_size : int = 100 ,
734734 end_offset : Optional [int ] = None ,
735735) -> list [_T ]:
736736 """
@@ -780,7 +780,59 @@ async def _query_active_contracts_via_updates(
780780 break
781781
782782 max_offset_this_page = begin
783+
784+ def _item_offset (item : dict ) -> Optional [int ]:
785+ """Return the offset for any update variant (Transaction,
786+ OffsetCheckpoint, Reassignment, TopologyTransaction, ...) so the
787+ cursor advances even on pages that contain no Transactions. If we
788+ only looked at Transactions, a page of pure OffsetCheckpoints
789+ would leave `max_offset_this_page == begin` and the outer loop
790+ would break prematurely — missing all later transactions.
791+ """
792+ upd = item .get ("update" , {}) if isinstance (item , dict ) else {}
793+ if not isinstance (upd , dict ):
794+ return None
795+ # Try every known variant wrapper, both capitalized and flat.
796+ for variant in (
797+ "Transaction" ,
798+ "OffsetCheckpoint" ,
799+ "Reassignment" ,
800+ "TopologyTransaction" ,
801+ ):
802+ wrapper = upd .get (variant )
803+ if isinstance (wrapper , dict ):
804+ value = wrapper .get ("value" )
805+ if isinstance (value , dict ) and value .get ("offset" ) is not None :
806+ return _coerce_offset (value .get ("offset" ))
807+ for variant in (
808+ "transaction" ,
809+ "offsetCheckpoint" ,
810+ "reassignment" ,
811+ "topologyTransaction" ,
812+ ):
813+ value = upd .get (variant )
814+ if isinstance (value , dict ) and value .get ("offset" ) is not None :
815+ return _coerce_offset (value .get ("offset" ))
816+ return None
817+
818+ def _coerce_offset (raw ) -> Optional [int ]:
819+ if isinstance (raw , (int , float )):
820+ return int (raw )
821+ if isinstance (raw , str ):
822+ try :
823+ return int (raw )
824+ except ValueError :
825+ return None
826+ return None
827+
783828 for item in items :
829+ # Always try to advance the page cursor — regardless of whether
830+ # the item is a Transaction. This is the fix for checkpoint-only
831+ # pages that would otherwise terminate the walk prematurely.
832+ item_off = _item_offset (item )
833+ if item_off is not None and item_off > max_offset_this_page :
834+ max_offset_this_page = item_off
835+
784836 update = item .get ("update" , {}) if isinstance (item , dict ) else {}
785837 tx_wrapper = update .get ("Transaction" ) or {}
786838 tx = tx_wrapper .get ("value" ) if isinstance (tx_wrapper , dict ) else None
@@ -789,19 +841,6 @@ async def _query_active_contracts_via_updates(
789841 if not tx :
790842 continue
791843
792- raw_offset = tx .get ("offset" )
793- if isinstance (raw_offset , str ):
794- try :
795- tx_offset = int (raw_offset )
796- except ValueError :
797- tx_offset = max_offset_this_page
798- elif isinstance (raw_offset , (int , float )):
799- tx_offset = int (raw_offset )
800- else :
801- tx_offset = max_offset_this_page
802- if tx_offset > max_offset_this_page :
803- max_offset_this_page = tx_offset
804-
805844 for ev in tx .get ("events" , []) or []:
806845 created = ev .get ("CreatedEvent" ) or ev .get ("createdEvent" )
807846 if created :
0 commit comments