33from decimal import Decimal
44from typing import Callable , Iterable , Optional , Tuple
55
6- from blockapi .utils .num import raw_to_decimals
76from blockapi .v2 .base import (
87 ApiException ,
98 BlockchainApi ,
@@ -98,6 +97,8 @@ def __init__(
9897 blockchain : Blockchain ,
9998 sleep_provider : ISleepProvider = None ,
10099 limit : Optional [int ] = None ,
100+ max_listings = 500 ,
101+ max_offers = 500 ,
101102 ):
102103 super ().__init__ (api_key )
103104
@@ -110,6 +111,9 @@ def __init__(
110111 self ._sleep_provider = sleep_provider or SleepProvider ()
111112 self ._limit = limit
112113
114+ self .max_listings = max_listings
115+ self .max_offers = max_offers
116+
113117 def fetch_nfts (self , address : str , cursor : Optional [str ] = None ) -> FetchResult :
114118 logger .info (f'Fetch nfts from { address } , cursor={ cursor } ' )
115119 return self ._coalesce (self ._yield_nfts (address , cursor ))
@@ -256,17 +260,32 @@ def _yield_fetch_data(
256260 self , fetch_method : Callable , key : str , cursor : Optional [str ] = None
257261 ) -> Iterable [Tuple [FetchResult , Optional [str ]]]:
258262 cursors = set ()
263+ page_count = 0
264+ item_count = 0
259265
260- count = 0
261266 while True :
262267 self ._sleep_provider .sleep (self .base_url , self .api_options .rate_limit )
263- count += 1
264- logger .debug (f'Fetching page { count } of { key } from { cursor } ' )
268+ page_count += 1
269+ logger .debug (f'Fetching page { page_count } of { key } from { cursor } ' )
265270 fetched , next_cursor = fetch_method (key , cursor )
266271 if self ._should_retry (fetched ):
267- count -= 1
272+ page_count -= 1
268273 continue
269274
275+ # Count items for dynamic limiting
276+ item_limit = None
277+ if fetched .data :
278+ current_items = 0
279+ if 'offers' in fetched .data :
280+ current_items = len (fetched .data ['offers' ])
281+ item_limit = self .max_offers
282+ elif 'listings' in fetched .data :
283+ current_items = len (fetched .data ['listings' ])
284+ item_limit = self .max_listings
285+ # skip `'nfts' in fetched.data`, because it doesn't have a limit
286+
287+ item_count += current_items
288+
270289 yield fetched , next_cursor
271290
272291 if not next_cursor :
@@ -278,13 +297,19 @@ def _yield_fetch_data(
278297
279298 cursors .add (cursor )
280299
281- if self ._limit and count >= self ._limit :
300+ # Check both page and item limits
301+ if self ._limit and page_count >= self ._limit :
302+ break
303+
304+ if item_limit and item_count >= item_limit :
282305 break
283306
284307 def _fetch_offers_page (
285308 self , method : str , collection : str , cursor : Optional [str ] = None
286309 ) -> tuple [FetchResult , Optional [str ]]:
287- params = dict (next = cursor ) if cursor else dict ()
310+ params = {'limit' : 100 } # the max limit allowed is 100 items per page
311+ if cursor :
312+ params ['next' ] = cursor
288313
289314 fetched = self .get_data (
290315 method ,
@@ -567,15 +592,17 @@ def _get_type(item_type) -> Optional[OfferItemType]:
567592
568593 return OFFER_ITEM_TYPES .get (item_type )
569594
570- @ staticmethod
571- def _coalesce ( fetch_results : Iterable [ Tuple [ FetchResult , Optional [ str ]]]):
595+ def _coalesce ( self , fetch_results : Iterable [ Tuple [ FetchResult , Optional [ str ]]]):
596+ """Simple aggregator - no limiting logic needed here."""
572597 data = []
573598 errors = []
574599 last = None
575600 last_cursor = None
601+
576602 for item , cursor in fetch_results :
577603 last_cursor = cursor
578604 last = item
605+
579606 if item .data :
580607 data .append (item .data )
581608
@@ -636,3 +663,14 @@ def _should_retry(self, data):
636663 return True
637664
638665 return False
666+
667+
668+ if __name__ == '__main__' :
669+ col = 'ever-fragments-of-civitas'
670+ o = OpenSeaApi (
671+ '6c0e52527b124aeeb0bebbcfbaf2e7b6' , Blockchain .ETHEREUM , max_listings = 100
672+ )
673+ # offers = o.fetch_offers(col)
674+ listings = o .fetch_listings (col )
675+
676+ assert True
0 commit comments