diff --git a/astro.config.mjs b/astro.config.mjs index d02771e..bfccaaa 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,30 +1,30 @@ -import { defineConfig } from "astro/config" -import * as fs from "node:fs" +import { defineConfig } from 'astro/config' +import * as fs from 'node:fs' -import react from "@astrojs/react" -import tailwind from "@astrojs/tailwind" -import optimizer from "vite-plugin-optimizer" +import react from '@astrojs/react' +import tailwind from '@astrojs/tailwind' +import optimizer from 'vite-plugin-optimizer' export default defineConfig({ - integrations: [react(), tailwind({ applyBaseStyles: false })], - vite: { - server: { - https: { - cert: fs.readFileSync("./.local-ssl/localhost.pem"), - key: fs.readFileSync("./.local-ssl/localhost-key.pem") - }, - }, - build: { - minify: false - }, - resolve: { - dedupe: ["@lumeweb/libportal", "@lumeweb/libweb", "@lumeweb/libkernel"] - }, - plugins: [ - optimizer({ - "node-fetch": - "const e = undefined; export default e;export {e as Response, e as FormData, e as Blob};" - }) - ] - } + integrations: [react(), tailwind({ applyBaseStyles: false })], + vite: { + server: process.env.MODE === 'development' ? { + https: { + cert: fs.readFileSync('./.local-ssl/localhost.pem'), + key: fs.readFileSync('./.local-ssl/localhost-key.pem') + }, + } : {}, + build: { + minify: false + }, + resolve: { + dedupe: ['@lumeweb/libportal', '@lumeweb/libweb', '@lumeweb/libkernel'] + }, + plugins: [ + optimizer({ + 'node-fetch': + 'const e = undefined; export default e;export {e as Response, e as FormData, e as Blob};' + }) + ] + } }) diff --git a/package-lock.json b/package-lock.json index bff51f7..267bd81 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "opposite-osiris", + "name": "@lumeweb/web3toybox.com", "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "opposite-osiris", + "name": "@lumeweb/web3toybox.com", "version": "0.0.1", "dependencies": { "@astrojs/react": "^3.0.3", diff --git a/package.json b/package.json index af9b878..2d61de9 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "opposite-osiris", + "name": "@lumeweb/web3toybox.com", "type": "module", "version": "0.0.1", "scripts": { diff --git a/src/components/nft-gallery/App.tsx b/src/components/nft-gallery/App.tsx index 2f18bc9..accc380 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 () { - ) + ); } diff --git a/src/pages/index.astro b/src/pages/index.astro index 6812079..dc25c98 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -23,8 +23,8 @@ import '@/styles/global.css' - Welcome to Web3Toybox - This is a place where you can play with different famous web3 apps in a truly decentralized way - no infura, no gateways, no censorship. + Welcome to Web3 Toybox + This is a place where you can play with different web3 use cases by example in a truly decentralized way - no infura, no gateways, no censorship. @@ -43,12 +43,12 @@ import '@/styles/global.css' - + More Coming Soon... - - We're diligently developing more intriguing examples. If you have any suggestions, don't hesitate to join our Discord channel and propose or submit any examples you'd like to see here! + + We're are definitely interested in new ideas for demos! If you have any suggestions, don't hesitate to join our Discord channel and propose or submit any examples you'd like to see here! . -

Brought to you with 💚 by the lumeweb.com team alongside the Sia Foundation

+

Brought to you with 💚 by the Lume team, and grant sponsored by the Sia Foundation