diff --git a/src/components/nft-gallery/App.tsx b/src/components/nft-gallery/App.tsx index 29a3aff..a3838ef 100644 --- a/src/components/nft-gallery/App.tsx +++ b/src/components/nft-gallery/App.tsx @@ -1,15 +1,15 @@ -import { Input } from "@/components/ui/input" -import { Button } from "@/components/ui/button" -import "@lumeweb/sdk/lib/style.css" -import "@/styles/global.css" +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import "@lumeweb/sdk/lib/style.css"; +import "@/styles/global.css"; import React, { createContext, createRef, type ReactNode, useContext, useEffect, - useState -} from "react" + useState, +} from "react"; import { type AuthContextType, AuthProvider, @@ -22,53 +22,49 @@ import { useAuth, useLumeStatus, useNetworks, - LumeDashboardTrigger -} from "@lumeweb/sdk" -import * as kernel from "@lumeweb/libkernel/kernel" -import { kernelLoaded } from "@lumeweb/libkernel/kernel" + LumeDashboardTrigger, +} from "@lumeweb/sdk"; +import * as kernel from "@lumeweb/libkernel/kernel"; +import { kernelLoaded } from "@lumeweb/libkernel/kernel"; import { dnsClient, ethClient, ipfsClient, networkRegistryClient, peerDiscoveryClient, - swarmClient -} from "@/lib/clients" -import { ethers } from "ethers" -import * as ethersBytes from "@ethersproject/bytes" -import { createProvider } from "@lumeweb/kernel-eth-client" + swarmClient, +} from "@/lib/clients"; +import { ethers } from "ethers"; +import * as ethersBytes from "@ethersproject/bytes"; +import { createProvider } from "@lumeweb/kernel-eth-client"; // @ts-ignore -import jdu from "json-data-uri" -import { ERC721_ABI } from "@/lib/erc721-abi" -import { - Card, - CardContent, - CardHeader, -} from "@/components/ui/card" -import LogoImg from "@/assets/lume-logo.png" -let BOOT_FUNCTIONS: (() => Promise)[] = [] +import jdu from "json-data-uri"; +import { ERC721_ABI } from "@/lib/erc721-abi"; +import { Card, CardContent, CardHeader } from "@/components/ui/card"; +import LogoImg from "@/assets/lume-logo.png"; +let BOOT_FUNCTIONS: (() => Promise)[] = []; -export const AppContext = createContext(undefined) +export const AppContext = createContext(undefined); export function useApp() { - const context = useContext(AppContext) + const context = useContext(AppContext); if (!context) { - throw new Error("useApp must be used within an AppProvider") + throw new Error("useApp must be used within an AppProvider"); } - return context + return context; } interface AppProviderProps { - children: ReactNode + children: ReactNode; } -const provider = createProvider() +const provider = createProvider(); const ERC721_TRANSFER_EVENT_SIGNATURE = ethers.id( "Transfer(address,address,uint256)" -) +); async function findPotentialERC721Contracts( address: string @@ -79,24 +75,24 @@ async function findPotentialERC721Contracts( topics: [ ERC721_TRANSFER_EVENT_SIGNATURE, null, - ethersBytes.hexZeroPad(address, 32) - ] - }) + ethersBytes.hexZeroPad(address, 32), + ], + }); - const potentialContracts = new Set() - logs.forEach((log: any) => potentialContracts.add(log.address)) + const potentialContracts = new Set(); + logs.forEach((log: any) => potentialContracts.add(log.address)); - const confirmedERC721Contracts: string[] = [] + const confirmedERC721Contracts: string[] = []; for (let contractAddress of potentialContracts) { if (await isERC721(contractAddress)) { - confirmedERC721Contracts.push(contractAddress) + confirmedERC721Contracts.push(contractAddress); } } - return confirmedERC721Contracts + return confirmedERC721Contracts; } -const TRANSFER_EVENT_SIGNATURE = ethers.id("Transfer(address,address,uint256)") +const TRANSFER_EVENT_SIGNATURE = ethers.id("Transfer(address,address,uint256)"); async function fetchTokensViaTransferEvent( address: string, @@ -109,93 +105,93 @@ async function fetchTokensViaTransferEvent( topics: [ TRANSFER_EVENT_SIGNATURE, null, - ethersBytes.hexZeroPad(address, 32) - ] - }) + ethersBytes.hexZeroPad(address, 32), + ], + }); - const tokenIds: number[] = [] + const tokenIds: number[] = []; logs.forEach((log) => { if (log.topics && log.topics.length === 4) { - const tokenIdBigNumber = ethers.toNumber(log.topics[3]) - tokenIds.push(tokenIdBigNumber) + const tokenIdBigNumber = ethers.toNumber(log.topics[3]); + tokenIds.push(tokenIdBigNumber); } - }) + }); - return tokenIds + return tokenIds; } async function fetchOwnedNFTs( address: string, confirmedERC721Contracts: string[] ): Promise<{ contract: string; tokenId: number; metadata: any }[]> { - const ownedNFTs = [] + const ownedNFTs = []; for (let contractAddress of confirmedERC721Contracts) { - const contract = new ethers.Contract(contractAddress, ERC721_ABI, provider) - let tokenIds: number[] = [] + const contract = new ethers.Contract(contractAddress, ERC721_ABI, provider); + let tokenIds: number[] = []; try { - const balance = await contract.balanceOf(address) + const balance = await contract.balanceOf(address); for (let i = 0; i < balance; i++) { - const tokenId = await contract.tokenOfOwnerByIndex(address, i) - tokenIds.push(tokenId.toNumber()) + const tokenId = await contract.tokenOfOwnerByIndex(address, i); + tokenIds.push(tokenId.toNumber()); } } catch (error) { // If tokenOfOwnerByIndex is not available, fall back to fetchTokensViaTransferEvent - tokenIds = await fetchTokensViaTransferEvent(address, contractAddress) + tokenIds = await fetchTokensViaTransferEvent(address, contractAddress); } for (let tokenId of tokenIds) { try { - const uri = await contract.tokenURI(tokenId) + const uri = await contract.tokenURI(tokenId); // const metadata = await fetchMetadataFromURI(uri); ownedNFTs.push({ contract: contractAddress, tokenId: tokenId, - metadata: uri - }) + metadata: uri, + }); } catch (error: any) { console.error( `Failed to fetch metadata for token ${tokenId} from contract ${contractAddress}: ${error.message}` - ) + ); } } } - return ownedNFTs + return ownedNFTs; } async function isERC721(address: string): Promise { - const contract = new ethers.Contract(address, ERC721_ABI, provider) + const contract = new ethers.Contract(address, ERC721_ABI, provider); try { // Try calling some ERC-721 methods to confirm if this is an ERC-721 contract. - await contract.name() - await contract.symbol() - return true + await contract.name(); + await contract.symbol(); + return true; } catch (error) { - return false + return false; } } const AppProvider: React.FC = ({ children }) => { - return {children} -} + return {children}; +}; async function boot(status: LumeStatusContextType, auth: AuthContextType) { kernel.init().then(() => { - status.setInited(true) - }) + status.setInited(true); + }); - await kernelLoaded() + await kernelLoaded(); - auth.setIsLoggedIn(true) + auth.setIsLoggedIn(true); BOOT_FUNCTIONS.push( async () => await swarmClient.addRelay( "2d7ae1517caf4aae4de73c6d6f400765d2dd00b69d65277a29151437ef1c7d1d" ) - ) + ); // IRC BOOT_FUNCTIONS.push( @@ -203,40 +199,40 @@ async function boot(status: LumeStatusContextType, auth: AuthContextType) { await peerDiscoveryClient.register( "zrjHTx8tSQFWnmZ9JzK7XmJirqJQi2WRBLYp3fASaL2AfBQ" ) - ) + ); BOOT_FUNCTIONS.push( async () => await networkRegistryClient.registerType("content") - ) + ); BOOT_FUNCTIONS.push( async () => await networkRegistryClient.registerType("blockchain") - ) - BOOT_FUNCTIONS.push(async () => await ethClient.register()) - BOOT_FUNCTIONS.push(async () => await ipfsClient.register()) + ); + BOOT_FUNCTIONS.push(async () => await ethClient.register()); + BOOT_FUNCTIONS.push(async () => await ipfsClient.register()); const resolvers = [ - "zrjEYq154PS7boERAbRAKMyRGzAR6CTHVRG6mfi5FV4q9FA" // ENS - ] + "zrjEYq154PS7boERAbRAKMyRGzAR6CTHVRG6mfi5FV4q9FA", // ENS + ]; for (const resolver of resolvers) { - BOOT_FUNCTIONS.push(async () => dnsClient.registerResolver(resolver)) + BOOT_FUNCTIONS.push(async () => dnsClient.registerResolver(resolver)); } - BOOT_FUNCTIONS.push(async () => status.setReady(true)) + BOOT_FUNCTIONS.push(async () => status.setReady(true)); - await bootup() + await bootup(); - await Promise.all([ethClient.ready(), ipfsClient.ready()]) + await Promise.all([ethClient.ready(), ipfsClient.ready()]); } async function bootup() { for (const entry of Object.entries(BOOT_FUNCTIONS)) { - console.log(entry[1].toString()) - await entry[1]() + console.log(entry[1].toString()); + await entry[1](); } } function LoginDash() { - const { isLoggedIn } = useAuth() - const { ready, inited } = useLumeStatus() + const { isLoggedIn } = useAuth(); + const { ready, inited } = useLumeStatus(); return ( <> @@ -259,130 +255,130 @@ function LoginDash() { )} - ) + ); } async function asyncIterableToUint8Array(asyncIterable: any) { - const chunks = [] - let totalLength = 0 + const chunks = []; + let totalLength = 0; for await (const chunk of asyncIterable) { - chunks.push(chunk) - totalLength += chunk.length + chunks.push(chunk); + totalLength += chunk.length; } - const result = new Uint8Array(totalLength) - let offset = 0 + const result = new Uint8Array(totalLength); + let offset = 0; for (const chunk of chunks) { - result.set(chunk, offset) - offset += chunk.length + result.set(chunk, offset); + offset += chunk.length; } - return result + return result; } function uint8ArrayToBase64(byteArray: Uint8Array) { - let base64 = "" + let base64 = ""; const characters = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - let padding = 0 + let padding = 0; for (let i = 0; i < byteArray.length; i += 3) { - const a = byteArray[i] - const b = byteArray[i + 1] - const c = byteArray[i + 2] + const a = byteArray[i]; + const b = byteArray[i + 1]; + const c = byteArray[i + 2]; - const triplet = (a << 16) + ((b || 0) << 8) + (c || 0) + const triplet = (a << 16) + ((b || 0) << 8) + (c || 0); - base64 += characters.charAt((triplet & 0xfc0000) >> 18) - base64 += characters.charAt((triplet & 0x03f000) >> 12) - base64 += characters.charAt((triplet & 0x000fc0) >> 6) - base64 += characters.charAt(triplet & 0x00003f) + base64 += characters.charAt((triplet & 0xfc0000) >> 18); + base64 += characters.charAt((triplet & 0x03f000) >> 12); + base64 += characters.charAt((triplet & 0x000fc0) >> 6); + base64 += characters.charAt(triplet & 0x00003f); if (byteArray.length - i < 3) { - padding = 3 - (byteArray.length - i) + padding = 3 - (byteArray.length - i); } } // Add padding if necessary if (padding > 0) { - base64 = base64.slice(0, -padding) + (padding === 1 ? "=" : "==") + base64 = base64.slice(0, -padding) + (padding === 1 ? "=" : "=="); } - return base64 + return base64; } function App() { - const status = useLumeStatus() - const auth = useAuth() - const [nftList, setNftList] = useState([]) + const status = useLumeStatus(); + const auth = useAuth(); + const [nftList, setNftList] = useState([]); useEffect(() => { - boot(status, auth) - }, []) + boot(status, auth); + }, []); - const { networks } = useNetworks() + const { networks } = useNetworks(); const ipfsStatus = networks - .filter((item) => item.name.toLowerCase() === "ipfs") - ?.pop() - + .filter((item) => item.name.toLowerCase() === "ipfs") + ?.pop(); + const ethStatus = networks .filter((item) => item.name.toLowerCase() === "ethereum") - ?.pop() + ?.pop(); - const ready = ethStatus?.ready && status.ready + const ready = ethStatus?.ready && status.ready; - const inputRef = createRef() + const inputRef = createRef(); async function search(e: any | Event) { - e.preventDefault() + e.preventDefault(); - let address = inputRef?.current?.value as string + let address = inputRef?.current?.value as string; - address = await ethers.resolveAddress(address, provider) + address = await ethers.resolveAddress(address, provider); - const contracts = await findPotentialERC721Contracts(address) + const contracts = await findPotentialERC721Contracts(address); - const nfts = await fetchOwnedNFTs(address, contracts) + const nfts = await fetchOwnedNFTs(address, contracts); - const list = [] + const list = []; for (const nft of nfts) { - let meta + let meta; if (typeof nft.metadata === "string") { try { - meta = await (await fetch(nft.metadata)).json() + meta = await (await fetch(nft.metadata)).json(); } catch (e) { meta = { image: "", // TODO: Improve this by bringing an actual image name: "Failed to Load", - fail: true - } + fail: true, + }; } } else { - meta = jdu.parse(nft.metadata) + meta = jdu.parse(nft.metadata); } - let image + let image; if (!meta.fail) { - const imageCID = meta.image.replace("ipfs://", "") + const imageCID = meta.image.replace("ipfs://", ""); image = await asyncIterableToUint8Array( ipfsClient.cat(imageCID).iterable() - ) + ); } else { - image = meta.image + image = meta.image; } list.push({ image, name: meta.name, - base64: meta.fail - }) + base64: meta.fail, + }); - setNftList(list) + setNftList(list); } } @@ -418,7 +414,7 @@ function App() { You'll need to wait for a couple minutes before we can start - searching. You are currently locally syncing to the ETH network. Current Progress: {ethStatus?.sync ? `${ethStatus?.sync.toLocaleString()}%` : 'Initializing...'} + searching. You are currently locally syncing to the ETH network.{" "} + + Current Progress:{" "} + {ethStatus?.sync + ? `${ethStatus?.sync.toLocaleString()}%` + : "Initializing..."} + ) : null}
@@ -453,7 +455,7 @@ function App() { ))}
- ) + ); } export default function () { @@ -467,5 +469,5 @@ export default function () { - ) + ); }