diff --git a/apps/web/src/server/api/helpers/l2nfts.ts b/apps/web/src/server/api/helpers/l2nfts.ts new file mode 100644 index 00000000..678e05cd --- /dev/null +++ b/apps/web/src/server/api/helpers/l2nfts.ts @@ -0,0 +1,123 @@ +import { validateAndParseAddress } from "starknet"; + +import { type NftMedia } from "../types"; + +const requestsHeader = { + "Content-Type": "application/json", + "X-API-KEY": process.env.ARK_API_KEY ?? "", +}; +const nftApiUrl = process.env.NEXT_PUBLIC_ARK_API_DOMAIN ?? ""; + +type ArkCollectionsApiResponse = { + result: Array<{ + contract_address: string; + contract_type: string; + image?: string; + name: string; + symbol: string; + tokens_count: number; + }>; + total_count: number; +}; +export async function getL2ContractsForOwner(address: string) { + const url = `${nftApiUrl}/v1/owners/${validateAndParseAddress( + address + )}/contracts`; + + const contractsResponse = await fetch(url, { + headers: requestsHeader, + }); + const contracts = + (await contractsResponse.json()) as ArkCollectionsApiResponse; + + return contracts; +} + +type ArkBatchNftsApiResponse = { + result: Array<{ + contract_address: string; + metadata?: { normalized: { image?: string; name?: string } }; + owner: string; + token_id: string; + }>; +}; +export async function getL2NftsMetadataBatch( + tokenIds: Array, + contractAddress: string +) { + const url = `${nftApiUrl}/v1/tokens/batch`; + + const nftsResponse = await fetch(url, { + body: JSON.stringify({ + tokens: tokenIds.map((tokenId) => ({ + contract_address: validateAndParseAddress(contractAddress), + token_id: tokenId, + })), + }), + headers: requestsHeader, + }); + + const nfts = (await nftsResponse.json()) as ArkBatchNftsApiResponse; + + return nfts; +} + +type ArkNftsApiResponse = { + result: Array<{ + contract_address: string; + metadata: { + normalized: { image: null | string; name: null | string }; + } | null; + owner: string; + token_id: string; + }>; + total_count: number; +}; +export async function getL2NftsForOwner( + userAddress: string, + contractAddress: string | undefined +) { + const url = `${nftApiUrl}/v1/owners/${validateAndParseAddress( + userAddress + )}/tokens${ + contractAddress !== undefined + ? `?contract_address=${validateAndParseAddress(contractAddress)}` + : "" + }`; + + const nftsResponse = await fetch(url, { + headers: requestsHeader, + }); + + const nfts = (await nftsResponse.json()) as ArkNftsApiResponse; + + return nfts; +} + +type ArkCollectionInfoApiResponse = { + result: { contract_address: string; name: string; symbol: string }; +}; +export async function getL2ContractMetadata(contractAddress: string) { + const url = `${nftApiUrl}/v1/contracts/${validateAndParseAddress( + contractAddress + )}`; + + const contractInfoResponse = await fetch(url, { + headers: requestsHeader, + }); + + const contractInfo = + (await contractInfoResponse.json()) as ArkCollectionInfoApiResponse; + + return contractInfo; +} + +export function getMediaObjectFromUrl(image: string | undefined): NftMedia { + if (image === undefined) { + return { format: "image", src: undefined }; + } + const mediaSrc = image.replace("ipfs://", process.env.IPFS_GATEWAY ?? ""); + const mediaFormat = mediaSrc?.split(".").pop() === "mp4" ? "video" : "image"; + + return { format: mediaFormat, src: mediaSrc }; +} diff --git a/apps/web/src/server/api/routers/l1Nfts.ts b/apps/web/src/server/api/routers/l1Nfts.ts index 51aebd25..055f5d7d 100644 --- a/apps/web/src/server/api/routers/l1Nfts.ts +++ b/apps/web/src/server/api/routers/l1Nfts.ts @@ -47,11 +47,15 @@ export const l1NftsRouter = createTRPCRouter({ .query(async ({ input }) => { const { address, cursor, pageSize } = input; const { contracts, pageKey, totalCount } = - await alchemy.nft.getContractsForOwner(address.toLowerCase(), { - excludeFilters: [NftFilters.SPAM], - pageKey: cursor, - pageSize, - }); + await alchemy.nft.getContractsForOwner( + // address.toLowerCase(), + "0x14dd4FBB0708aF74Bd3F86698122c6F3829b1462", + { + excludeFilters: [NftFilters.SPAM], + pageKey: cursor, + pageSize, + } + ); const whitelistedCollections = (await bridgeL1Contract.read?.getWhiteListedCollections?.()) as @@ -131,13 +135,17 @@ export const l1NftsRouter = createTRPCRouter({ ownedNfts: nfts, pageKey, totalCount, - } = await alchemy.nft.getNftsForOwner(userAddress.toLowerCase(), { - contractAddresses: - contractAddress !== undefined ? [contractAddress] : undefined, - excludeFilters: [NftFilters.SPAM], - pageKey: cursor, - pageSize, - }); + } = await alchemy.nft.getNftsForOwner( + // userAddress.toLowerCase(), + "0x14dd4FBB0708aF74Bd3F86698122c6F3829b1462", + { + contractAddresses: + contractAddress !== undefined ? [contractAddress] : undefined, + excludeFilters: [NftFilters.SPAM], + pageKey: cursor, + pageSize, + } + ); // TODO @YohanTz: Handle videos const ownedNfts: Array = nfts.map((nft) => { diff --git a/apps/web/src/server/api/routers/l2Nfts.ts b/apps/web/src/server/api/routers/l2Nfts.ts index 93a1c717..83f622a2 100644 --- a/apps/web/src/server/api/routers/l2Nfts.ts +++ b/apps/web/src/server/api/routers/l2Nfts.ts @@ -3,45 +3,15 @@ import { z } from "zod"; import { createTRPCRouter, publicProcedure } from "~/server/api/trpc"; +import { + getL2ContractMetadata, + getL2ContractsForOwner, + getL2NftsForOwner, + getL2NftsMetadataBatch, + getMediaObjectFromUrl, +} from "../helpers/l2nfts"; import { type Collection, type Nft } from "../types"; -type ArkNftsApiResponse = { - result: Array<{ - contract_address: string; - metadata: { - normalized: { image: null | string; name: null | string }; - } | null; - owner: string; - token_id: string; - }>; - total_count: number; -}; - -type ArkCollectionsApiResponse = { - result: Array<{ - contract_address: string; - contract_type: string; - image?: string; - name: string; - symbol: string; - tokens_count: number; - }>; - total_count: number; -}; - -type ArkBatchNftsApiResponse = { - result: Array<{ - contract_address: string; - metadata?: { normalized: { image?: string; name?: string } }; - owner: string; - token_id: string; - }>; -}; - -type ArkCollectionInfoApiResponse = { - result: { contract_address: string; name: string; symbol: string }; -}; - export const l2NftsRouter = createTRPCRouter({ getCollectionInfo: publicProcedure .input(z.object({ contractAddress: z.string() })) @@ -49,19 +19,7 @@ export const l2NftsRouter = createTRPCRouter({ const { contractAddress } = input; try { - const url = `${ - process.env.NEXT_PUBLIC_ARK_API_DOMAIN ?? "" - }/v1/contracts/${validateAndParseAddress(contractAddress)}`; - - const contractInfoResponse = await fetch(url, { - headers: { - "Content-Type": "application/json", - "X-API-KEY": process.env.ARK_API_KEY ?? "", - }, - }); - - const contractInfo = - (await contractInfoResponse.json()) as ArkCollectionInfoApiResponse; + const contractInfo = await getL2ContractMetadata(contractAddress); return { name: contractInfo.result.name }; } catch (error) { @@ -77,19 +35,9 @@ export const l2NftsRouter = createTRPCRouter({ } = input; try { - const url = `${ - process.env.NEXT_PUBLIC_ARK_API_DOMAIN ?? "" - }/v1/owners/${validateAndParseAddress(address)}/contracts`; - - const contractsResponse = await fetch(url, { - headers: { - "Content-Type": "application/json", - "X-API-KEY": process.env.ARK_API_KEY ?? "", - }, - }); - const contracts = - (await contractsResponse.json()) as ArkCollectionsApiResponse; - const collections: Array = contracts.result.map( + const contractsForOwner = await getL2ContractsForOwner(address); + + const collections: Array = contractsForOwner.result.map( (contract) => { const mediaSrc = contract.image?.replace( "ipfs://", @@ -111,7 +59,7 @@ export const l2NftsRouter = createTRPCRouter({ } ); - return { collections, totalCount: contracts.total_count }; + return { collections, totalCount: contractsForOwner.total_count }; } catch (error) { console.error("getL2NftCollectionsByWallet error: ", error); return { collections: [], totalCount: 0 }; @@ -133,25 +81,7 @@ export const l2NftsRouter = createTRPCRouter({ } try { - const url = `${ - process.env.NEXT_PUBLIC_ARK_API_DOMAIN ?? "" - }/v1/tokens/batch`; - - const nftsResponse = await fetch(url, { - body: JSON.stringify({ - tokens: tokenIds.map((tokenId) => ({ - contract_address: validateAndParseAddress(contractAddress), - token_id: tokenId, - })), - }), - headers: { - "Content-Type": "application/json", - "X-API-KEY": process.env.ARK_API_KEY ?? "", - }, - method: "POST", - }); - - const nfts = (await nftsResponse.json()) as ArkBatchNftsApiResponse; + const nfts = await getL2NftsMetadataBatch(tokenIds, contractAddress); return nfts.result .filter( @@ -161,16 +91,11 @@ export const l2NftsRouter = createTRPCRouter({ validateAndParseAddress(ownerAddress) ) .map((nft) => { - const mediaSrc = nft.metadata?.normalized.image?.replace( - "ipfs://", - process.env.IPFS_GATEWAY ?? "" - ); - const mediaFormat = - mediaSrc?.split(".").pop() === "mp4" ? "video" : "image"; + const media = getMediaObjectFromUrl(nft.metadata?.normalized.image); return { collectionName: "EveraiDuo", - media: { format: mediaFormat, src: mediaSrc }, + media, tokenId: nft.token_id, tokenName: nft.metadata?.normalized.name ?? `#${nft.token_id}`, }; @@ -201,38 +126,21 @@ export const l2NftsRouter = createTRPCRouter({ } = input; try { - const url = `${ - process.env.NEXT_PUBLIC_ARK_API_DOMAIN ?? "" - }/v1/owners/${validateAndParseAddress(userAddress)}/tokens${ - contractAddress !== undefined - ? `?contract_address=${validateAndParseAddress(contractAddress)}` - : "" - }`; - - const nftsResponse = await fetch(url, { - headers: { - "Content-Type": "application/json", - "X-API-KEY": process.env.ARK_API_KEY ?? "", - }, - }); - - const nfts = (await nftsResponse.json()) as ArkNftsApiResponse; + const nfts = await getL2NftsForOwner(userAddress, contractAddress); const ownedNfts: Array = nfts.result.map((nft) => { - const mediaSrc = nft.metadata?.normalized.image?.replace( - "ipfs://", - process.env.IPFS_GATEWAY ?? "" + const media = getMediaObjectFromUrl( + nft.metadata?.normalized.image ?? undefined ); - const mediaFormat = - mediaSrc?.split(".").pop() === "mp4" ? "video" : "image"; + const name = + nft.metadata?.normalized.name?.length ?? 0 > 0 + ? nft.metadata?.normalized?.name ?? "" + : `${nft.token_id}`; return { contractAddress: nft.contract_address, - media: { format: mediaFormat, src: mediaSrc }, - name: - nft.metadata?.normalized.name?.length ?? 0 > 0 - ? nft.metadata?.normalized?.name ?? "" - : `${nft.token_id}`, + media, + name, tokenId: nft.token_id, }; });