diff --git a/packages/recoil/src/atoms/nft.tsx b/packages/recoil/src/atoms/nft.tsx index 29ca52173..b7c64bc48 100644 --- a/packages/recoil/src/atoms/nft.tsx +++ b/packages/recoil/src/atoms/nft.tsx @@ -48,12 +48,15 @@ export const nftCollectionsWithIds = selector< }) ) ); - return allWalletCollections; + return allWalletCollections.filter(Boolean) as Array<{ + publicKey: string; + collections: Array; + }>; }, }); export const nftById = equalSelectorFamily< - Nft, + Nft | null, { publicKey: string; connectionUrl: string; nftId: string } >({ key: "nftById", @@ -132,7 +135,7 @@ export const nftsByIds = selectorFamily< nftIds: { nftId: string; publicKey: string }[]; blockchain: Blockchain; }) => - async ({ get }: any) => { + async ({ get }) => { const connectionUrl = blockchain === Blockchain.ETHEREUM ? get(ethereumConnectionUrl) @@ -149,7 +152,7 @@ export const nftsByIds = selectorFamily< }) ) ); - return allNfts; + return allNfts.filter(Boolean) as Array; }, }); diff --git a/packages/recoil/src/atoms/solana/isONELive.tsx b/packages/recoil/src/atoms/solana/isONELive.tsx index 5ca6f195b..1c9eac2b7 100644 --- a/packages/recoil/src/atoms/solana/isONELive.tsx +++ b/packages/recoil/src/atoms/solana/isONELive.tsx @@ -15,8 +15,7 @@ export const isOneLive = selector<{ key: "isOneLive", get: async ({ get }) => { const wallet = get(activeWallet); - const user = get(authenticatedUser); - if (wallet?.blockchain !== Blockchain.SOLANA || !user) { + if (wallet?.blockchain !== Blockchain.SOLANA) { return { isLive: false }; } return ( diff --git a/packages/recoil/src/atoms/solana/nft.tsx b/packages/recoil/src/atoms/solana/nft.tsx index 62411410a..ee9696f73 100644 --- a/packages/recoil/src/atoms/solana/nft.tsx +++ b/packages/recoil/src/atoms/solana/nft.tsx @@ -25,7 +25,7 @@ export const solanaWalletCollections = selectorFamily< { publicKey: string; collections: Array; - }, + } | null, { publicKey: string } >({ key: "solanaWalletCollections", @@ -33,8 +33,11 @@ export const solanaWalletCollections = selectorFamily< ({ publicKey }) => ({ get }) => { const metadataMap = get(solanaMetadataMap({ publicKey })); - const { publicKey: pk, collections } = - intoSolanaCollectionsMap(metadataMap); + const collectionsMap = intoSolanaCollectionsMap(metadataMap); + if (!collectionsMap) { + return null; + } + const { publicKey: pk, collections } = collectionsMap; let sortedCollections = Object.values(collections).sort((a, b) => { if (a.isMadlads) { return -1; @@ -53,110 +56,127 @@ export const solanaWalletCollections = selectorFamily< // Returns the nft metadata map for a given public key. // Maps metadata pubkey -> account data. -const solanaMetadataMap = selectorFamily({ +const solanaMetadataMap = selectorFamily< + MetadataMap | null, + { publicKey: string } +>({ key: "metadataMap", get: ({ publicKey }) => ({ get }) => { - const connectionUrl = get(solanaConnectionUrl); - const { nfts } = get( - customSplTokenAccounts({ publicKey, connectionUrl }) - ); - // Transform into the map now. - const nftMap = {}; - for (let k = 0; k < nfts.nftTokens.length; k += 1) { - const nftToken = nfts.nftTokens[k]; - const nftTokenMetadata = nfts.nftTokenMetadata[k]!; - if (nftTokenMetadata) { - nftMap[nftTokenMetadata.publicKey] = { - metadataPublicKey: nftTokenMetadata.publicKey, - nftToken, - nftTokenMetadata, - }; + try { + const connectionUrl = get(solanaConnectionUrl); + const { nfts } = get( + customSplTokenAccounts({ publicKey, connectionUrl }) + ); + // Transform into the map now. + const nftMap = {}; + for (let k = 0; k < nfts.nftTokens.length; k += 1) { + const nftToken = nfts.nftTokens[k]; + const nftTokenMetadata = nfts.nftTokenMetadata[k]!; + if (nftTokenMetadata) { + nftMap[nftTokenMetadata.publicKey] = { + metadataPublicKey: nftTokenMetadata.publicKey, + nftToken, + nftTokenMetadata, + }; + } } + return { + publicKey, + metadata: nftMap, + }; + } catch (e) { + console.error(e); + return null; } - return { - publicKey, - metadata: nftMap, - }; }, }); export const solanaNftById = equalSelectorFamily< - Nft, + Nft | null, { publicKey: string; connectionUrl: string; nftId: string } >({ key: "nftById", get: ({ publicKey, connectionUrl, nftId }) => async ({ get }) => { - const { connection } = get(anchorContext); - const metadataMap = get(solanaMetadataMap({ publicKey })); - const { nftToken, nftTokenMetadata } = metadataMap.metadata[nftId]; - - const resp = await connection.customSplMetadataUri( - [nftToken], - [nftTokenMetadata] - ); - const [_, uriData] = resp[0] ?? []; - const collectionName = (() => { - if (!uriData) { - return ""; - } else if (uriData.metadata.collection) { - // TODO: there is a verified boolean on the object. We should probably check it. - const metadata = get( - solanaNftCollection({ - collectionPublicKey: uriData.metadata.collection.key.toString(), - }) - ); - return metadata?.data.name; - } else if (uriData.tokenMetaUriData.collection) { - return uriData.tokenMetaUriData?.collection?.name; - } else { - return uriData.metadata.data.name; + try { + const { connection } = get(anchorContext); + const metadataMap = get(solanaMetadataMap({ publicKey })); + if (!metadataMap) { + return null; } - })()?.replace(/\0/g, ""); + const { nftToken, nftTokenMetadata } = metadataMap.metadata[nftId]; - const nft: Nft = { - id: nftTokenMetadata?.publicKey ?? "", - blockchain: Blockchain.SOLANA, - publicKey: nftToken.key!, - mint: nftTokenMetadata?.account.mint, - metadataCollectionId: uriData?.metadata?.collection?.key.toString(), - name: ( - nftTokenMetadata?.account.data.name ?? - (uriData ? uriData.tokenMetaUriData.name : "Unknown") - )?.replace(/\0/g, ""), - description: uriData ? uriData.tokenMetaUriData.description : "", - externalUrl: uriData - ? externalResourceUri( - uriData.tokenMetaUriData.external_url?.replace(/\0/g, "") - ) - : "", - imageUrl: - uriData && uriData.tokenMetaUriData.image + const resp = await connection.customSplMetadataUri( + [nftToken], + [nftTokenMetadata] + ); + const [_, uriData] = resp[0] ?? []; + const collectionName = (() => { + if (!uriData) { + return ""; + } else if (uriData.metadata.collection) { + // TODO: there is a verified boolean on the object. We should probably check it. + const metadata = get( + solanaNftCollection({ + collectionPublicKey: + uriData?.metadata.collection.key.toString(), + }) + ); + return metadata?.data.name; + } else if (uriData.tokenMetaUriData.collection) { + return uriData.tokenMetaUriData?.collection?.name; + } else { + return uriData.metadata.data.name; + } + })()?.replace(/\0/g, ""); + + const nft: Nft = { + id: nftTokenMetadata?.publicKey ?? "", + blockchain: Blockchain.SOLANA, + publicKey: nftToken.key!, + mint: nftTokenMetadata?.account.mint, + metadataCollectionId: uriData?.metadata?.collection?.key.toString(), + name: ( + nftTokenMetadata?.account.data.name ?? + (uriData ? uriData.tokenMetaUriData.name : "Unknown") + )?.replace(/\0/g, ""), + description: uriData ? uriData.tokenMetaUriData.description : "", + externalUrl: uriData ? externalResourceUri( - uriData.tokenMetaUriData.image?.replace(/\0/g, "") - ) - : UNKNOWN_NFT_ICON_SRC, - // ensuring attributes is an array - attributes: - uriData && uriData?.tokenMetaUriData?.attributes?.map - ? uriData?.tokenMetaUriData?.attributes?.map( - (a: { trait_type: string; value: string }) => ({ - traitType: a.trait_type, - value: a.value, - }) + uriData.tokenMetaUriData.external_url?.replace(/\0/g, "") ) - : [], - properties: uriData?.tokenMetaUriData?.properties ?? {}, - creators: uriData.metadata.data.creators ?? [], - collectionName, - }; - if (isMadLads(nft.creators)) { - nft.lockScreenImageUrl = nft.properties?.files?.[0]?.uri; + : "", + imageUrl: + uriData && uriData.tokenMetaUriData.image + ? externalResourceUri( + uriData.tokenMetaUriData.image?.replace(/\0/g, "") + ) + : UNKNOWN_NFT_ICON_SRC, + // ensuring attributes is an array + attributes: + uriData && uriData?.tokenMetaUriData?.attributes?.map + ? uriData?.tokenMetaUriData?.attributes?.map( + (a: { trait_type: string; value: string }) => ({ + traitType: a.trait_type, + value: a.value, + }) + ) + : [], + properties: uriData?.tokenMetaUriData?.properties ?? {}, + creators: uriData?.metadata?.data?.creators ?? [], + collectionName, + }; + if (isMadLads(nft.creators)) { + nft.lockScreenImageUrl = nft.properties?.files?.[0]?.uri; + } + return nft; + } catch (e) { + console.error(e); + return null; } - return nft; }, equals: (m1, m2) => JSON.stringify(m1) === JSON.stringify(m2), }); @@ -190,12 +210,15 @@ const solanaNftCollection = selectorFamily< // Given all the token account data for a given wallet, transform into a // collection array for UI presentation. -function intoSolanaCollectionsMap(metadataMap: MetadataMap): { +function intoSolanaCollectionsMap(metadataMap: MetadataMap | null): null | { publicKey: string; collections: { [collectionId: string]: NftCollection; }; } { + if (!metadataMap) { + return null; + } const collections = {}; Object.values(metadataMap.metadata).forEach((value) => { const [collectionId, metadataCollectionId] = (() => { @@ -209,7 +232,7 @@ function intoSolanaCollectionsMap(metadataMap: MetadataMap): { id: collectionId, metadataCollectionId, name: collectionId, - symbol: value.nftTokenMetadata?.account.data.symbol, + symbol: value.nftTokenMetadata?.account?.data?.symbol, tokenType: "", totalSupply: "", itemIds: [],