Skip to content

Commit

Permalink
refactor: extracted starknet nft api calls to its own helper
Browse files Browse the repository at this point in the history
  • Loading branch information
YohanTz committed Apr 4, 2024
1 parent 3a307c3 commit e1ce046
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 128 deletions.
123 changes: 123 additions & 0 deletions apps/web/src/server/api/helpers/l2nfts.ts
Original file line number Diff line number Diff line change
@@ -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<string>,
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 };
}
32 changes: 20 additions & 12 deletions apps/web/src/server/api/routers/l1Nfts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<Nft> = nfts.map((nft) => {
Expand Down
140 changes: 24 additions & 116 deletions apps/web/src/server/api/routers/l2Nfts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,65 +3,23 @@ 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() }))
.query(async ({ input }) => {
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) {
Expand All @@ -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<Collection> = contracts.result.map(
const contractsForOwner = await getL2ContractsForOwner(address);

const collections: Array<Collection> = contractsForOwner.result.map(
(contract) => {
const mediaSrc = contract.image?.replace(
"ipfs://",
Expand All @@ -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 };
Expand All @@ -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(
Expand All @@ -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}`,
};
Expand Down Expand Up @@ -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<Nft> = 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,
};
});
Expand Down

0 comments on commit e1ce046

Please sign in to comment.