Skip to content

Commit 4405348

Browse files
authored
fix(unisat): cursor fix
* fix(unisat): cursor fix * fix(unisat): cursor fix * fix(unisat): cursor fix * fix(unisat): cursor fix * fix(unisat): cursor fix * fix(unisat): cursor fix
1 parent cca345c commit 4405348

2 files changed

Lines changed: 44 additions & 48 deletions

File tree

blockapi/test/v2/api/nft/test_unisat.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ def test_parse_nfts(requests_mock, unisat_client, inscription_data):
5353
assert nft1.standard == "ordinals"
5454
assert nft1.name == "Ordinal #12345"
5555
assert nft1.amount == 1
56-
assert nft1.updated_time == 1672531200
5756
assert nft1.blockchain == Blockchain.BITCOIN
5857
assert nft1.asset_type == AssetType.AVAILABLE
5958

@@ -72,7 +71,6 @@ def test_parse_nfts(requests_mock, unisat_client, inscription_data):
7271
assert nft2.standard == "ordinals"
7372
assert nft2.name == "Ordinal #12346"
7473
assert nft2.amount == 1
75-
assert nft2.updated_time == 1672531300
7674
assert nft2.blockchain == Blockchain.BITCOIN
7775
assert nft2.asset_type == AssetType.AVAILABLE
7876

@@ -108,7 +106,6 @@ def test_parse_nfts_edge_cases(
108106
assert nft.standard == "ordinals"
109107
assert nft.name == "Ordinal #2"
110108
assert nft.amount == 1
111-
assert nft.updated_time == 1234567890
112109
assert nft.blockchain == Blockchain.BITCOIN
113110
assert nft.asset_type == AssetType.AVAILABLE
114111

blockapi/v2/api/nft/unisat.py

Lines changed: 44 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)