Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions blockapi/test/v2/api/data/opensea/collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
{
"address": "0x8acb0bc7f6c77e4e2aef83ea928d5a6c2a0b7fcd",
"chain": "ethereum"
},
{
"address": "0x9acb0bc7f6c77e4e2aef83ea928d5a6c2a0b7fcd",
"chain": "ethereum"
}
],
"editors": [
Expand Down
6 changes: 5 additions & 1 deletion blockapi/test/v2/api/nft/test_opensea.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,11 @@ def test_parse_collection(
assert data.total_stats.floor_price == Decimal('0.008')
assert data.total_stats.coin == COIN_ETH
assert data.blockchain == Blockchain.ETHEREUM
assert data.contract == '0x8acb0bc7f6c77e4e2aef83ea928d5a6c2a0b7fcd'
assert len(data.contracts) == 2
assert data.contracts[0].blockchain == Blockchain.ETHEREUM
assert data.contracts[0].address == '0x8acb0bc7f6c77e4e2aef83ea928d5a6c2a0b7fcd'
assert data.contracts[1].blockchain == Blockchain.ETHEREUM
assert data.contracts[1].address == '0x9acb0bc7f6c77e4e2aef83ea928d5a6c2a0b7fcd'


def test_create_with_unsupported_blockchain():
Expand Down
48 changes: 36 additions & 12 deletions blockapi/v2/api/nft/opensea.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
AssetType,
Blockchain,
Coin,
ContractInfo,
FetchResult,
NftCollection,
NftCollectionIntervalStats,
Expand Down Expand Up @@ -68,6 +69,8 @@ class OpenSeaApi(BlockchainApi, INftProvider, INftParser):
Blockchain.ZORA: 'zora',
}

opensea_blockchains = {n: b for b, n in supported_blockchains.items()}

coin = COIN_ETH
api_options = ApiOptions(
blockchain=Blockchain.ETHEREUM,
Expand Down Expand Up @@ -174,12 +177,16 @@ def fetch_collection(self, collection: str) -> FetchResult:
headers=self._headers,
collection=collection,
chain=self._opensea_chain,
extra=dict(stats=stats.data, stats_errors=stats.errors),
extra=dict(
collection=collection, stats=stats.data, stats_errors=stats.errors
),
)

def parse_collection(self, fetch_result: FetchResult) -> ParseResult:
parsed, error = self._parse_collection(
fetch_result.data, fetch_result.extra.get('stats')
fetch_result.extra.get('collection'),
fetch_result.data,
fetch_result.extra.get('stats'),
)
errors = []
if error:
Expand Down Expand Up @@ -318,9 +325,10 @@ def _yield_parsed_nfts(self, results):
)

def _parse_collection(
self, data: dict, stat_data: dict
self, collection_ident: str, data: dict, stat_data: dict
) -> tuple[Optional[NftCollection], Optional[str]]:
if not stat_data or not data:
logger.warning(f'No data for collection: {collection_ident}')
return None, None

total = stat_data.get('total')
Expand All @@ -346,19 +354,12 @@ def _parse_collection(
week_stats = self._parse_collection_stats(intervals, 'one_week')
month_stats = self._parse_collection_stats(intervals, 'one_month')

contract = data.get('collection')
if contracts := data.get('contracts'):
if contract_filtered := [
c.get('address')
for c in contracts
if c.get('chain') == self._opensea_chain
]:
contract = contract_filtered[0].lower()
contracts = list(self._yield_contracts(collection_ident, data.get('contracts')))

collection = NftCollection.from_api(
ident=data.get('collection'),
name=data.get('name'),
contract=contract,
contracts=contracts,
image=data.get('image_url'),
is_disabled=data.get('is_disabled'),
is_nsfw=data.get('is_nsfw'),
Expand Down Expand Up @@ -559,3 +560,26 @@ def _coalesce(fetch_results: Iterable[Tuple[FetchResult, Optional[str]]]):
cursor=last_cursor,
time=last.time,
)

def _yield_contracts(self, ident: str, contracts: dict) -> Iterable[ContractInfo]:
if not contracts:
return

for item in contracts:
chain = item.get('chain')
blockchain = self.opensea_blockchains.get(chain)
address = item.get('address')

if not blockchain:
logger.warning(
f'Could not map {chain} to known blockchains for collection {ident}'
)
continue

if not address:
logger.warning(
f'No address available for item with chain {chain} for collection {ident}'
)
continue

yield ContractInfo.from_api(blockchain=blockchain, address=address)
16 changes: 13 additions & 3 deletions blockapi/v2/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,11 +716,21 @@ def from_api(
)


@attr.s(auto_attribs=True, slots=True, frozen=True)
class ContractInfo:
blockchain: Blockchain
address: str

@classmethod
def from_api(cls, *, blockchain: Blockchain, address: str):
return cls(blockchain=blockchain, address=address)


@attr.s(auto_attribs=True, slots=True, frozen=True)
class NftCollection:
ident: str
name: str
contract: str
contracts: list[ContractInfo]
image: Optional[str]
is_disabled: bool
is_nsfw: bool
Expand All @@ -736,7 +746,7 @@ def from_api(
*,
ident: str,
name: str,
contract: str,
contracts: list[ContractInfo],
image: Optional[str],
is_disabled: bool,
is_nsfw: bool,
Expand All @@ -749,7 +759,7 @@ def from_api(
return cls(
ident=ident,
name=name,
contract=contract,
contracts=contracts,
image=image,
is_disabled=is_disabled,
is_nsfw=is_nsfw,
Expand Down