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
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"next-themes": "^0.2.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"sharp": "^0.33.5",
"starknet": "^5.25.5",
"superjson": "1.12.2",
"tailwindcss-animate": "^1.0.7",
Expand Down
162 changes: 107 additions & 55 deletions apps/web/src/server/api/helpers/l2nfts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,102 +8,152 @@ const requestsHeader = {
};
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;
type PortfolioApiResponse = {
data: Array<{
best_offer: null | string;
collection_address: string;
collection_name: string;
currency_address: string;
floor: string;
list_price: string;
metadata?: {
animation_key: null | string;
animation_mime_type: null | string;
animation_url: null | string;
attributes: null | string;
background_color: null | string;
description: null | string;
external_url: null | string;
image: null | string;
image_data: null | string;
image_key: null | string;
image_mime_type: null | string;
name: null | string;
youtube_url: null | string;
};
token_id: string;
}>;
total_count: number;
token_count: number;
};
export async function getL2ContractsForOwner(address: string) {
const url = `${nftApiUrl}/v1/owners/${validateAndParseAddress(
address
)}/contracts`;
const url = `${nftApiUrl}/portfolio/${validateAndParseAddress(address)}`;

const contractsResponse = await fetch(url, {
headers: requestsHeader,
});
const contracts =
(await contractsResponse.json()) as ArkCollectionsApiResponse;
const apiResponse = (await contractsResponse.json()) as PortfolioApiResponse;

return contracts;
return apiResponse;
}

type ArkBatchNftsApiResponse = {
result: Array<{
contract_address: string;
contract_name: string;
metadata?: { normalized: { image?: string; name?: string } };
type TokenApiResponse = {
data: Array<{
collection_address: string;
collection_image: null | string;
collection_name: null | string;
metadata: {
animation_key: null | string;
animation_mime_type: null | string;
animation_url: null | string;
background_color: null | string;
description: null | string;
external_url: null | string;
image: null | string;
image_data: null | string;
image_key: null | string;
image_key_540_540: null | string;
image_mime_type: null | string;
name: null | string;
youtube_url: null | string;
};
owner: string;
token_id: string;
}>;
};

export async function getL2NftsMetadataBatch(
tokens: Array<{ contract_address: string; token_id: string }>
) {
const url = `${nftApiUrl}/v1/tokens/batch`;

const body = JSON.stringify({
tokens: tokens.map((token) => ({
contract_address: validateAndParseAddress(token.contract_address),
token_id: token.token_id,
})),
});

const nftsResponse = await fetch(url, {
body,
headers: requestsHeader,
method: "POST",
});

const nfts = (await nftsResponse.json()) as ArkBatchNftsApiResponse;
let nfts: {
collection_address: string;
collection_image: null | string;
collection_name: null | string;
metadata: {
animation_key: null | string;
animation_mime_type: null | string;
animation_url: null | string;
background_color: null | string;
description: null | string;
external_url: null | string;
image: null | string;
image_data: null | string;
image_key: null | string;
image_key_540_540: null | string;
image_mime_type: null | string;
name: null | string;
youtube_url: null | string;
};
owner: string;
token_id: string;
}[] = [];

for (const token of tokens) {
const url = `${nftApiUrl}/tokens/${token.contract_address}/0x534e5f4d41494e/${token.token_id}`;
const nftsResponse = await fetch(url, {
body,
headers: requestsHeader,
method: "POST",
});

const response = (await nftsResponse.json()) as TokenApiResponse;
nfts = nfts.concat(response.data);
}

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 url = `${nftApiUrl}/portfolio/${validateAndParseAddress(userAddress)}`;

const nftsResponse = await fetch(url, {
headers: requestsHeader,
});

const nfts = (await nftsResponse.json()) as ArkNftsApiResponse;

return nfts;
const nfts = (await nftsResponse.json()) as PortfolioApiResponse;

return {
data: contractAddress
? nfts.data.filter(
(d) =>
d.collection_address === validateAndParseAddress(contractAddress)
)
: nfts.data,
token_count: nfts.token_count,
};
}

type ArkCollectionInfoApiResponse = {
result: { contract_address: string; name: string; symbol: string };
data: {
address: string;
description: null | string;
image: null | string;
name: null | string;
};
};
export async function getL2ContractMetadata(contractAddress: string) {
const url = `${nftApiUrl}/v1/contracts/${validateAndParseAddress(
const url = `${nftApiUrl}/collections/${validateAndParseAddress(
contractAddress
)}`;
)}/0x534e5f4d41494e`;

const contractInfoResponse = await fetch(url, {
headers: requestsHeader,
Expand Down Expand Up @@ -152,11 +202,13 @@ export async function getL2WhitelistedCollections() {
}
}

export function getMediaObjectFromUrl(image: string | undefined): NftMedia {
export function getMediaObjectFromUrl(
image: null | string | undefined
): NftMedia {
if (image === undefined) {
return { format: "image", src: undefined };
}
const mediaSrc = image.replace("ipfs://", process.env.IPFS_GATEWAY ?? "");
const mediaSrc = image?.replace("ipfs://", process.env.IPFS_GATEWAY ?? "");
const mediaFormat = mediaSrc?.split(".").pop() === "mp4" ? "video" : "image";

return { format: mediaFormat, src: mediaSrc };
Expand Down
23 changes: 9 additions & 14 deletions apps/web/src/server/api/routers/bridgeRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,20 +140,15 @@ export const bridgeRequestRouter = createTRPCRouter({
}, {} as Record<string, { contract_address: string; token_id: string }>)
)
);
requestMetadataByReqHash = requestMetadatas.result.reduce(
(acc, current) => {
return {
...acc,
[`${current.token_id}`]: {
contractName: current.contract_name,
media: getMediaObjectFromUrl(
current.metadata?.normalized.image
),
},
};
},
{}
);
requestMetadataByReqHash = requestMetadatas.reduce((acc, current) => {
return {
...acc,
[`${current.token_id}`]: {
contractName: current.collection_name,
media: getMediaObjectFromUrl(current.metadata?.image),
},
};
}, {});
}

const bridgeRequestsWithMetadata = bridgeRequests.map(
Expand Down
75 changes: 37 additions & 38 deletions apps/web/src/server/api/routers/l2Nfts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const l2NftsRouter = createTRPCRouter({
try {
const contractInfo = await getL2ContractMetadata(contractAddress);

return { name: contractInfo.result.name };
return { name: contractInfo.data.name };
} catch (error) {
return { name: "" };
}
Expand All @@ -36,35 +36,34 @@ export const l2NftsRouter = createTRPCRouter({
} = input;

try {
const contractsForOwner = await getL2ContractsForOwner(address);
const response = await getL2ContractsForOwner(address);

// console.log("=> response", response);

const whitelistedCollections = await getL2WhitelistedCollections();

const collections: Array<Collection> = contractsForOwner.result.map(
(contract) => {
const media = getMediaObjectFromUrl(contract.image);
const isBridgeable =
whitelistedCollections === undefined ||
whitelistedCollections.some(
(whitelistedCollection) =>
validateAndParseAddress(
whitelistedCollection
).toLowerCase() ===
validateAndParseAddress(
contract.contract_address
).toLowerCase()
);
const collections: Array<Collection> = response.data.map((contract) => {
const media = getMediaObjectFromUrl(contract.metadata?.image);
const isBridgeable =
whitelistedCollections === undefined ||
whitelistedCollections.some(
(whitelistedCollection) =>
validateAndParseAddress(whitelistedCollection).toLowerCase() ===
validateAndParseAddress(
contract.collection_address
).toLowerCase()
);

return {
contractAddress: contract.contract_address,
isBridgeable,
media,
name: contract.name ?? contract.symbol ?? "Unknown",
totalBalance: contract.tokens_count,
};
}
);
return {
contractAddress: contract.collection_address,
isBridgeable,
media,
name: contract.collection_name ?? "Unknown",
totalBalance: 1,
};
});

return { collections, totalCount: contractsForOwner.total_count };
return { collections, totalCount: response.token_count };
} catch (error) {
console.error("getL2NftCollectionsByWallet error: ", error);
return { collections: [], totalCount: 0 };
Expand Down Expand Up @@ -93,21 +92,21 @@ export const l2NftsRouter = createTRPCRouter({
}))
);

return nfts.result
return nfts
.filter(
(nft) =>
ownerAddress === undefined ||
validateAndParseAddress(nft.owner) ===
validateAndParseAddress(ownerAddress)
)
.map((nft) => {
const media = getMediaObjectFromUrl(nft.metadata?.normalized.image);
const media = getMediaObjectFromUrl(nft.metadata?.image);

return {
collectionName: nft.contract_name,
collectionName: nft.collection_name,
media,
tokenId: nft.token_id,
tokenName: nft.metadata?.normalized.name ?? `#${nft.token_id}`,
tokenName: nft.metadata?.name ?? `#${nft.token_id}`,
};
});
} catch (error) {
Expand Down Expand Up @@ -138,24 +137,24 @@ export const l2NftsRouter = createTRPCRouter({
try {
const nfts = await getL2NftsForOwner(userAddress, contractAddress);

const ownedNfts: Array<Nft> = nfts.result.map((nft) => {
const media = getMediaObjectFromUrl(
nft.metadata?.normalized.image ?? undefined
);
const ownedNfts: Array<Nft> = nfts.data.map((nft) => {
console.log("=> nft", nft);

const media = getMediaObjectFromUrl(nft.metadata?.image);
const name =
nft.metadata?.normalized.name?.length ?? 0 > 0
? nft.metadata?.normalized?.name ?? ""
nft.metadata?.name?.length ?? 0 > 0
? nft.metadata?.name ?? ""
: `${nft.token_id}`;

return {
contractAddress: nft.contract_address,
contractAddress: nft.collection_address,
media,
name,
tokenId: nft.token_id,
};
});

return { ownedNfts, totalCount: nfts.total_count };
return { ownedNfts, totalCount: nfts.token_count };
} catch (error) {
console.error("getL2OwnerNftsFromCollection error: ", error);
return { ownedNfts: [], totalCount: 0 };
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@
},
"dependencies": {
"dotenv-cli": "^7.0.0"
}
},
"packageManager": "pnpm@8.0.0"
}
Loading
Loading