From 8f2dfca46aee37d687abd42a23325f45da9cbfe3 Mon Sep 17 00:00:00 2001 From: Simon Sefcik <56121054+CoCoNuTeK@users.noreply.github.com> Date: Thu, 19 Jun 2025 07:21:08 +0000 Subject: [PATCH 1/3] feat: enhance handling of icon_url in unisat parse_collection --- blockapi/v2/api/nft/unisat.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/blockapi/v2/api/nft/unisat.py b/blockapi/v2/api/nft/unisat.py index c323eff..edcad37 100644 --- a/blockapi/v2/api/nft/unisat.py +++ b/blockapi/v2/api/nft/unisat.py @@ -293,8 +293,12 @@ def parse_collection(self, fetch_result: FetchResult) -> ParseResult: # Format the icon URL icon = stats.get("icon") icon_url = None + if icon: - icon_url = f"https://static.unisat.io/content/{icon}" + if icon.startswith(("http://", "https://")): + icon_url = icon + else: + icon_url = f"https://static.unisat.io/content/{icon.lstrip('/')}" floor_price = raw_to_decimals(stats.get("floorPrice", 0), self.coin.decimals) From 1e4290c87ef9702122c7c8938ae747ce9b1264a8 Mon Sep 17 00:00:00 2001 From: CoCoNuTeK Date: Mon, 23 Jun 2025 10:28:29 +0200 Subject: [PATCH 2/3] polished NftCollectionTotalStats object attributes --- .../unisat/collection_stats_full_url.json | 22 +++++ blockapi/test/v2/api/nft/test_unisat.py | 99 ++++++++++++------- blockapi/v2/api/nft/unisat.py | 11 +-- 3 files changed, 87 insertions(+), 45 deletions(-) create mode 100644 blockapi/test/v2/api/data/unisat/collection_stats_full_url.json diff --git a/blockapi/test/v2/api/data/unisat/collection_stats_full_url.json b/blockapi/test/v2/api/data/unisat/collection_stats_full_url.json new file mode 100644 index 0000000..f68c037 --- /dev/null +++ b/blockapi/test/v2/api/data/unisat/collection_stats_full_url.json @@ -0,0 +1,22 @@ +{ + "code": 0, + "msg": "ok", + "data": { + "collectionId": "rune-mania-miner", + "name": "Rune Mania Miner", + "desc": "Rune Mania: Utilize your RMM to mine Runes using:⛏️ Mining Boosts🧱 Block Boosts🧪 Mana Boosts🗿 Stone Boosts ✨ Rune Boosts", + "icon": "https://creator-hub-prod.s3.us-east-2.amazonaws.com/ord-rmm_pfp_1708461604099.png", + "btcValue": 0, + "btcValuePercent": 0, + "floorPrice": 80000, + "listed": 9, + "total": 3800, + "supply": null, + "attrs": [], + "twitter": "https://twitter.com/RuneManiaMiner", + "discord": null, + "website": "https://www.ord.io/61549984", + "pricePercent": 0, + "verification": false + } +} diff --git a/blockapi/test/v2/api/nft/test_unisat.py b/blockapi/test/v2/api/nft/test_unisat.py index 186d173..678447f 100644 --- a/blockapi/test/v2/api/nft/test_unisat.py +++ b/blockapi/test/v2/api/nft/test_unisat.py @@ -24,36 +24,67 @@ test_nft_type = BtcNftType.COLLECTION -def test_fetch_collection(requests_mock, unisat_client, collection_stats): +def test_fetch_collection_icon_code(requests_mock, unisat_client, collection_stats): + """ + UniSat sometimes sends only the icon *code* ― no URL. + We expect `parse_collection` to prepend the static CDN prefix automatically. + """ requests_mock.post( - f"{unisat_client.api_options.base_url}v3/market/collection/auction/collection_statistic", + f"{unisat_client.api_options.base_url}" + "v3/market/collection/auction/collection_statistic", text=collection_stats, ) - test_collection = "pixel-pepes" - fetch_result = unisat_client.fetch_collection(test_collection) - assert not fetch_result.errors, f"Fetch errors: {fetch_result.errors}" + fetch_result = unisat_client.fetch_collection("pixel-pepes") + assert not fetch_result.errors + + parsed = unisat_client.parse_collection(fetch_result) + assert not parsed.errors and len(parsed.data) == 1 + + col: NftCollection = parsed.data[0] + assert col.ident == "pixel-pepes" + assert col.name == "Pixel Pepes" + assert ( + col.image == "https://static.unisat.io/content/" + "47c1d21c508f6d49dfde64d958f14acd041244e1bb616f9b78114b8d9dc7b945i0" + ) + assert str(col.total_stats.floor_price) == "0.0099" + assert str(col.total_stats.owners_count) == "1563" + assert str(col.total_stats.sales_count) == "20" + assert str(col.total_stats.volume) == "0.399" + assert str(col.total_stats.market_cap) == "15.4737" + + +def test_fetch_collection_icon_full_url( + requests_mock, unisat_client, collection_stats_full_url +): + """ + UniSat may also deliver a *fully-qualified* icon URL. + In that case we should **not** touch the value. + """ + requests_mock.post( + f"{unisat_client.api_options.base_url}" + "v3/market/collection/auction/collection_statistic", + text=collection_stats_full_url, + ) + + fetch_result = unisat_client.fetch_collection("rune-mania-miner") + assert not fetch_result.errors - parse_result = unisat_client.parse_collection(fetch_result) - assert not parse_result.errors, f"Parse errors: {parse_result.errors}" - assert len(parse_result.data) == 1 + parsed = unisat_client.parse_collection(fetch_result) + assert not parsed.errors and len(parsed.data) == 1 - collection = parse_result.data[0] - assert isinstance(collection, NftCollection) - assert collection.ident == "pixel-pepes" - assert collection.name == "Pixel Pepes" + col: NftCollection = parsed.data[0] + assert col.ident == "rune-mania-miner" + assert col.name == "Rune Mania Miner" assert ( - collection.image - == "https://static.unisat.io/content/47c1d21c508f6d49dfde64d958f14acd041244e1bb616f9b78114b8d9dc7b945i0" + col.image == "https://creator-hub-prod.s3.us-east-2.amazonaws.com/" + "ord-rmm_pfp_1708461604099.png" ) - assert not collection.is_disabled - assert not collection.is_nsfw - assert collection.blockchain == Blockchain.BITCOIN - assert str(collection.total_stats.floor_price) == "0.0099" - assert str(collection.total_stats.owners_count) == "1563" - assert str(collection.total_stats.sales_count) == "20" - assert str(collection.total_stats.volume) == "0.399" - assert str(collection.total_stats.market_cap) == "15.4737" + # quick reality checks + assert str(col.total_stats.floor_price) == "0.0008" # 80 000 sat + assert str(col.total_stats.owners_count) == "3800" + assert str(col.total_stats.sales_count) == "9" def test_fetch_listings(requests_mock, unisat_client, listings_data): @@ -153,21 +184,6 @@ def unisat_client(fake_sleep_provider): return UnisatApi(api_key="test_key", sleep_provider=fake_sleep_provider) -@pytest.fixture -def inscription_data(): - return read_file('data/unisat/inscription_data.json') - - -@pytest.fixture -def inscription_data_edge_cases(): - return read_file('data/unisat/inscription_data_edge_cases.json') - - -@pytest.fixture -def collection_edge_cases(): - return read_file('data/unisat/collection_edge_cases.json') - - @pytest.fixture def listings_data(): return read_file('data/unisat/listings.json') @@ -180,4 +196,11 @@ def offers_data(): @pytest.fixture def collection_stats(): - return read_file('data/unisat/collection_stats.json') + """Pixel Pepes – icon **code** only.""" + return read_file("data/unisat/collection_stats.json") + + +@pytest.fixture +def collection_stats_full_url(): + """Rune Mania Miner – icon is a full URL.""" + return read_file("data/unisat/collection_stats_full_url.json") diff --git a/blockapi/v2/api/nft/unisat.py b/blockapi/v2/api/nft/unisat.py index edcad37..a24c480 100644 --- a/blockapi/v2/api/nft/unisat.py +++ b/blockapi/v2/api/nft/unisat.py @@ -304,16 +304,13 @@ def parse_collection(self, fetch_result: FetchResult) -> ParseResult: volume = raw_to_decimals(stats.get("btcValue", 0), self.coin.decimals) - total_nfts = stats.get("total", 0) - market_cap = floor_price * total_nfts if total_nfts else 0 - total_stats = NftCollectionTotalStats.from_api( volume=str(volume), - sales_count=str(stats.get("listed", 0)), - owners_count=str(total_nfts), - market_cap=str(market_cap), + sales_count=None, + owners_count=None, + market_cap=None, floor_price=str(floor_price), - average_price="0", + average_price=None, coin=self.coin, ) From 516114a034d26368ead4474285f671573b01a664 Mon Sep 17 00:00:00 2001 From: CoCoNuTeK Date: Mon, 23 Jun 2025 10:37:16 +0200 Subject: [PATCH 3/3] fix:unisat test fixed assertion values --- blockapi/test/v2/api/nft/test_unisat.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/blockapi/test/v2/api/nft/test_unisat.py b/blockapi/test/v2/api/nft/test_unisat.py index 678447f..d50eb3c 100644 --- a/blockapi/test/v2/api/nft/test_unisat.py +++ b/blockapi/test/v2/api/nft/test_unisat.py @@ -49,10 +49,7 @@ def test_fetch_collection_icon_code(requests_mock, unisat_client, collection_sta "47c1d21c508f6d49dfde64d958f14acd041244e1bb616f9b78114b8d9dc7b945i0" ) assert str(col.total_stats.floor_price) == "0.0099" - assert str(col.total_stats.owners_count) == "1563" - assert str(col.total_stats.sales_count) == "20" assert str(col.total_stats.volume) == "0.399" - assert str(col.total_stats.market_cap) == "15.4737" def test_fetch_collection_icon_full_url( @@ -81,10 +78,8 @@ def test_fetch_collection_icon_full_url( col.image == "https://creator-hub-prod.s3.us-east-2.amazonaws.com/" "ord-rmm_pfp_1708461604099.png" ) - # quick reality checks - assert str(col.total_stats.floor_price) == "0.0008" # 80 000 sat - assert str(col.total_stats.owners_count) == "3800" - assert str(col.total_stats.sales_count) == "9" + + assert str(col.total_stats.floor_price) == "0.0008" def test_fetch_listings(requests_mock, unisat_client, listings_data):