fix: update placeholder

This commit is contained in:
Derrick Hammer 2023-10-30 23:29:52 -04:00
parent de04de2bfe
commit fca81d8ead
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
1 changed files with 147 additions and 145 deletions

View File

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