@@ -54,7 +54,12 @@ class UnisatApi(BlockchainApi, INftParser, INftProvider):
5454 'get_collection_stats' : 'v3/market/collection/auction/collection_statistic' ,
5555 }
5656
57- def __init__ (self , api_key : str , sleep_provider : Optional [ISleepProvider ] = None ):
57+ def __init__ (
58+ self ,
59+ api_key : str ,
60+ sleep_provider : Optional [ISleepProvider ] = None ,
61+ limit : Optional [int ] = 10000 ,
62+ ):
5863 """
5964 Initialize the Unisat API client
6065
@@ -70,17 +75,14 @@ def __init__(self, api_key: str, sleep_provider: Optional[ISleepProvider] = None
7075 'Authorization' : f'Bearer { api_key } ' ,
7176 'Content-Type' : 'application/json' ,
7277 }
78+ self .limit = limit
7379
74- def fetch_nfts (
75- self , address : str , cursor : Optional [int ] = None , size : int = 16
76- ) -> FetchResult :
80+ def fetch_nfts (self , address : str ) -> FetchResult :
7781 """
7882 Fetch NFTs (inscriptions) owned by the address
7983
8084 Args:
8185 address: BTC address to fetch NFTs for
82- cursor: Pagination cursor (offset)
83- size: Number of items to return per page (default: 16)
8486
8587 Returns:
8688 FetchResult containing the NFT data
@@ -91,9 +93,7 @@ def fetch_nfts(
9193 if not address :
9294 raise ValueError ("Address is required" )
9395
94- params = {'size' : size }
95- if cursor is not None :
96- params ['cursor' ] = cursor
96+ params = {'size' : self .limit , 'cursor' : 0 }
9797
9898 try :
9999 return self .get_data (
@@ -116,76 +116,74 @@ def parse_nfts(self, fetch_result: FetchResult) -> ParseResult:
116116 """Parse NFT data from API response"""
117117 errors = []
118118 data = []
119- cursor = None
120119
121120 if not fetch_result .data :
122121 errors .append ("No data in fetch result" )
123122 return ParseResult (data = [], errors = errors )
124123
124+ if isinstance (fetch_result .data , dict ) and "code" in fetch_result .data :
125+ api_code = fetch_result .data ["code" ]
126+ if api_code != 0 :
127+ api_msg = fetch_result .data .get ("msg" , "Unknown error" )
128+ errors .append (f"Unisat error { api_code } : { api_msg } " )
129+ return ParseResult (data = [], errors = errors )
130+
125131 inner_data = fetch_result .data .get ("data" , {})
126132 if not inner_data :
127133 errors .append ("No data in API response" )
128134 return ParseResult (data = [], errors = errors )
129135
130- cursor = (
131- str (inner_data .get ("cursor" ))
132- if inner_data .get ("cursor" ) is not None
133- else None
134- )
135-
136136 for nft in self ._yield_parsed_nfts (inner_data ):
137137 data .append (nft )
138138
139- return ParseResult (data = data , errors = errors , cursor = cursor )
139+ return ParseResult (data = data , errors = errors )
140140
141141 def _yield_parsed_nfts (self , data : Dict ) -> Generator [NftToken , None , None ]:
142- """Yield parsed NFT tokens from API response data """
142+ """Yield parsed NFT tokens from Unisat API response"""
143143 if not data or "inscription" not in data :
144+ logger .warning ("No NFT data found in response" )
144145 return
145146
146147 for item in data ["inscription" ]:
147148 try :
148149 if not all (
149150 k in item
150- for k in [
151+ for k in (
151152 "inscriptionId" ,
152153 "inscriptionNumber" ,
153154 "timestamp" ,
154155 "utxo" ,
155- ]
156+ )
156157 ):
157158 logger .warning (f"Missing required fields in NFT data: { item } " )
158159 continue
159160
160161 utxo = item ["utxo" ]
161- if not all (k in utxo for k in [ "txid" , "address" ] ):
162+ if not all (k in utxo for k in ( "txid" , "address" ) ):
162163 logger .warning (f"Missing required fields in UTXO data: { utxo } " )
163164 continue
164165
165- inscription_number = str (item ["inscriptionNumber" ])
166- timestamp = str (item ["timestamp" ])
167-
168- yield NftToken (
166+ yield NftToken .from_api (
169167 ident = item ["inscriptionId" ],
170168 collection = "ordinals" ,
171169 collection_name = "Bitcoin Ordinals" ,
172170 contract = utxo ["txid" ],
173171 standard = "ordinals" ,
174- name = f"Ordinal #{ inscription_number } " ,
172+ name = f"Ordinal #{ item [ 'inscriptionNumber' ] } " ,
175173 description = "" ,
176174 amount = 1 ,
177175 image_url = "" ,
178176 metadata_url = None ,
179- metadata = {},
180- updated_time = int (timestamp ),
177+ updated_time = str (item ["timestamp" ]),
181178 is_disabled = False ,
182179 is_nsfw = False ,
183180 blockchain = Blockchain .BITCOIN ,
184181 asset_type = AssetType .AVAILABLE ,
185182 market_url = None ,
186183 )
184+
187185 except Exception as e :
188- logger .warning (f"Error parsing NFT item { item } : { e } " )
186+ logger .warning (f"Error parsing NFT item { item } : { str ( e ) } " )
189187 continue
190188
191189 def fetch_collection (self , collection : str ) -> FetchResult :
@@ -269,8 +267,7 @@ def fetch_listings(
269267 self ,
270268 nft_type : BtcNftType = BtcNftType .COLLECTION ,
271269 collection : Optional [str ] = None ,
272- cursor : Optional [str ] = None ,
273- limit : int = 100 ,
270+ limit : int = 499 ,
274271 address : Optional [str ] = None ,
275272 tick : Optional [str ] = None ,
276273 min_price : Optional [int ] = None ,
@@ -293,7 +290,6 @@ def fetch_listings(
293290 Args:
294291 nft_type: Type of NFT (brc20, domain, collection, arc20, runes)
295292 collection: Collection ID (slug), optional
296- cursor: Pagination cursor (offset, 'start' parameter)
297293 limit: Number of items per page
298294 address: Filter by address
299295 tick: Filter by tick (for BRC20)
@@ -319,8 +315,6 @@ def fetch_listings(
319315 nft_type .value if isinstance (nft_type , BtcNftType ) else str (nft_type )
320316 )
321317
322- start = int (cursor ) if cursor else 0
323-
324318 filter_dict = {"nftType" : nft_type_str }
325319
326320 if collection :
@@ -355,10 +349,16 @@ def fetch_listings(
355349 sort_dict = {}
356350 sort_dict [sort_by ] = sort_order
357351
352+ if limit >= 500 :
353+ logger .warning (
354+ f"Unisat API limit is 500. You tried to fetch { limit } items. Truncating to 499."
355+ )
356+ limit = 499
357+
358358 request_body = {
359359 "filter" : filter_dict ,
360360 "sort" : sort_dict ,
361- "start" : start ,
361+ "start" : 0 ,
362362 "limit" : limit ,
363363 }
364364
@@ -394,12 +394,9 @@ def parse_listings(self, fetch_result: FetchResult) -> ParseResult:
394394 return ParseResult (errors = fetch_result .errors )
395395
396396 items = inner_data .get ("list" , [])
397- timestamp = inner_data .get ("timestamp" )
398- cursor = str (timestamp ) if timestamp else None
399397
400398 return ParseResult (
401399 data = list (self ._yield_parsed_listings (items )),
402- cursor = cursor ,
403400 errors = fetch_result .errors ,
404401 )
405402
@@ -457,8 +454,7 @@ def fetch_offers(
457454 tick : Optional [str ] = None ,
458455 domain_type : Optional [str ] = None ,
459456 collection : Optional [str ] = None ,
460- cursor : Optional [str ] = None ,
461- limit : int = 100 ,
457+ limit : int = 499 ,
462458 ) -> FetchResult :
463459 """
464460 Fetch listing events (historical or recent) in a collection.
@@ -471,8 +467,7 @@ def fetch_offers(
471467 tick: Filter by tick (for BRC20)
472468 domain_type: Filter by domain type
473469 collection: Collection ID to filter by
474- cursor: Pagination cursor (offset, 'start' parameter)
475- limit: Number of items per page
470+ limit: Number of items
476471
477472 Returns:
478473 FetchResult containing the listing action data
@@ -482,8 +477,6 @@ def fetch_offers(
482477 nft_type .value if isinstance (nft_type , BtcNftType ) else str (nft_type )
483478 )
484479
485- start = int (cursor ) if cursor else 0
486-
487480 filter_dict = {}
488481 if nft_type_str :
489482 filter_dict ["nftType" ] = nft_type_str
@@ -500,9 +493,15 @@ def fetch_offers(
500493 if collection :
501494 filter_dict ["collectionId" ] = collection
502495
496+ if limit >= 500 :
497+ logger .warning (
498+ f"Unisat API limit is 500. You tried to fetch { limit } items. Truncating to 499."
499+ )
500+ limit = 499
501+
503502 request_body = {
504503 "filter" : filter_dict ,
505- "start" : start ,
504+ "start" : 0 ,
506505 "limit" : limit ,
507506 }
508507
0 commit comments