From bec498485a81e0ce14061476a691cb78d5b5d25b Mon Sep 17 00:00:00 2001 From: Alejo Thomas Ortega Date: Tue, 4 Jun 2024 12:09:27 -0300 Subject: [PATCH] fix: third-party wearables on outfits (#335) --- src/adapters/profiles.ts | 11 ++++---- src/controllers/handlers/outfits-handler.ts | 11 +++++++- src/logic/outfits.ts | 28 ++++++++++++++++++++- src/logic/utils.ts | 2 +- src/types.ts | 2 +- 5 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/adapters/profiles.ts b/src/adapters/profiles.ts index f63905be..fcbef9ae 100644 --- a/src/adapters/profiles.ts +++ b/src/adapters/profiles.ts @@ -1,8 +1,8 @@ import { AppComponents, ProfileMetadata } from '../types' import { Avatar, Entity, Snapshots } from '@dcl/schemas' -import { createTPWOwnershipChecker } from '../ports/ownership-checker/tpw-ownership-checker' import { parseUrn } from '@dcl/urn-resolver' import { splitUrnAndTokenId } from '../logic/utils' +import { createTPWOwnershipChecker } from '../ports/ownership-checker/tpw-ownership-checker' function isBaseWearable(wearable: string): boolean { return wearable.includes('base-avatars') @@ -91,8 +91,7 @@ export async function createProfilesComponent( } profileEntities = profileEntities.filter((entity) => !!entity.metadata) - - const tpwOwnershipChecker = createTPWOwnershipChecker(components) + const thirdPartyWearablesOwnershipChecker = createTPWOwnershipChecker(components) return await Promise.all( profileEntities.map(async (entity) => { @@ -118,16 +117,16 @@ export async function createProfilesComponent( } } } - tpwOwnershipChecker.addNFTsForAddress(ethAddress, wearables) + thirdPartyWearablesOwnershipChecker.addNFTsForAddress(ethAddress, wearables) const [ownedWearables, ownedEmotes, ownedNames] = await Promise.all([ wearablesFetcher.fetchOwnedElements(ethAddress), emotesFetcher.fetchOwnedElements(ethAddress), namesFetcher.fetchOwnedElements(ethAddress), - tpwOwnershipChecker.checkNFTsOwnership() + thirdPartyWearablesOwnershipChecker.checkNFTsOwnership() ]) - const thirdPartyWearables = tpwOwnershipChecker.getOwnedNFTsForAddress(ethAddress) + const thirdPartyWearables = thirdPartyWearablesOwnershipChecker.getOwnedNFTsForAddress(ethAddress) const avatars: Avatar[] = [] for (const avatar of metadata.avatars) { diff --git a/src/controllers/handlers/outfits-handler.ts b/src/controllers/handlers/outfits-handler.ts index 51e2dfba..05a8b08e 100644 --- a/src/controllers/handlers/outfits-handler.ts +++ b/src/controllers/handlers/outfits-handler.ts @@ -4,7 +4,16 @@ import { Entity } from '@dcl/schemas' export async function outfitsHandler( context: HandlerContextWithPath< - 'metrics' | 'content' | 'theGraph' | 'config' | 'fetch' | 'ownershipCaches' | 'wearablesFetcher' | 'namesFetcher', + | 'metrics' + | 'content' + | 'theGraph' + | 'config' + | 'fetch' + | 'ownershipCaches' + | 'wearablesFetcher' + | 'namesFetcher' + | 'thirdPartyProvidersStorage' + | 'logs', '/outfits/:id' > ): Promise<{ status: 200; body: Entity }> { diff --git a/src/logic/outfits.ts b/src/logic/outfits.ts index 25ced23f..e8c8e44c 100644 --- a/src/logic/outfits.ts +++ b/src/logic/outfits.ts @@ -1,16 +1,27 @@ import { Outfit, Outfits } from '@dcl/schemas' import { AppComponents, OnChainWearable, TypedEntity } from '../types' import { splitUrnAndTokenId } from './utils' +import { createTPWOwnershipChecker } from '../ports/ownership-checker/tpw-ownership-checker' export async function getOutfits( components: Pick< AppComponents, - 'metrics' | 'content' | 'theGraph' | 'config' | 'fetch' | 'ownershipCaches' | 'wearablesFetcher' | 'namesFetcher' + | 'metrics' + | 'content' + | 'theGraph' + | 'config' + | 'fetch' + | 'ownershipCaches' + | 'wearablesFetcher' + | 'namesFetcher' + | 'thirdPartyProvidersStorage' + | 'logs' >, ethAddress: string ): Promise | undefined> { const { config, wearablesFetcher, namesFetcher, content } = components const ensureERC721 = (await config.getString('ENSURE_ERC_721')) !== 'false' + const thirdPartyWearablesOwnershipChecker = createTPWOwnershipChecker(components) const outfitsEntities: TypedEntity[] = await content.fetchEntitiesByPointers([`${ethAddress}:outfits`]) @@ -32,12 +43,17 @@ export async function getOutfits( for (const outfit of metadata.outfits) { const wearables: string[] = [] + const thirdPartyWearables: string[] = [] let allWearablesOwned = true for (const wearable of outfit.outfit.wearables) { if (wearable.includes('off-chain') || wearable.includes('base-avatars')) { wearables.push(wearable) continue + } else if (wearable.includes('collections-thirdparty')) { + wearables.push(wearable) + thirdPartyWearables.push(wearable) + continue } const { urn, tokenId } = splitUrnAndTokenId(wearable) @@ -60,6 +76,16 @@ export async function getOutfits( } } + if (thirdPartyWearables.length > 0) { + thirdPartyWearablesOwnershipChecker.addNFTsForAddress(ethAddress, thirdPartyWearables) + await thirdPartyWearablesOwnershipChecker.checkNFTsOwnership() + const thirdPartyWearablesOwned = thirdPartyWearablesOwnershipChecker.getOwnedNFTsForAddress(ethAddress) + + allWearablesOwned = thirdPartyWearables.every((tpWearable: string) => + thirdPartyWearablesOwned.includes(tpWearable) + ) + } + if (allWearablesOwned) { fullyOwnedOutfits.push({ ...outfit, outfit: { ...outfit.outfit, wearables } }) } diff --git a/src/logic/utils.ts b/src/logic/utils.ts index 82d678d3..0b8f1a6a 100644 --- a/src/logic/utils.ts +++ b/src/logic/utils.ts @@ -13,7 +13,7 @@ export async function parseUrn(urn: string) { export function splitUrnAndTokenId(urnReceived: string) { const urnLength = urnReceived.split(':').length - if (urnLength === 7) { + if (urnLength === 7 && !urnReceived.includes('collections-thirdparty')) { const lastColonIndex = urnReceived.lastIndexOf(':') const urnValue = urnReceived.slice(0, lastColonIndex) return { urn: urnValue, tokenId: urnReceived.slice(lastColonIndex + 1) } diff --git a/src/types.ts b/src/types.ts index 709fabd2..fc06c620 100644 --- a/src/types.ts +++ b/src/types.ts @@ -108,7 +108,7 @@ export type ProfileMetadata = Profile & { export interface NFTsOwnershipChecker { addNFTsForAddress: (address: string, nfts: string[]) => void - checkNFTsOwnership: () => void + checkNFTsOwnership: () => Promise getOwnedNFTsForAddress: (address: string) => string[] }