From d9e719547cc729e624baeecaf5d3d0537a2962f9 Mon Sep 17 00:00:00 2001 From: yawn <69970183+yawn-c111@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:31:35 +0900 Subject: [PATCH 1/3] fix: IPFS upload --- pkgs/frontend/.env.example | 4 +- pkgs/frontend/app/routes/_index.tsx | 79 +++++++++++++++++++++-------- pkgs/frontend/hooks/useIpfs.ts | 68 ++++++++++++++++--------- pkgs/frontend/utils/ipfs.ts | 46 +++++++++++++++++ 4 files changed, 151 insertions(+), 46 deletions(-) create mode 100644 pkgs/frontend/utils/ipfs.ts diff --git a/pkgs/frontend/.env.example b/pkgs/frontend/.env.example index b1024c6..86c625b 100644 --- a/pkgs/frontend/.env.example +++ b/pkgs/frontend/.env.example @@ -12,4 +12,6 @@ VITE_SPLITS_CREATOR_ADDRESS=0x9c3648df4bb82fdf067a9b083900a986f9b27e9a VITE_PIMLICO_API_KEY= -VITE_PINATA_JWT= \ No newline at end of file +VITE_PINATA_JWT= + +VITE_PINATA_GATEWAY= \ No newline at end of file diff --git a/pkgs/frontend/app/routes/_index.tsx b/pkgs/frontend/app/routes/_index.tsx index cdcf4ba..b5c4880 100644 --- a/pkgs/frontend/app/routes/_index.tsx +++ b/pkgs/frontend/app/routes/_index.tsx @@ -1,7 +1,11 @@ -import { Box } from "@chakra-ui/react"; +import { Box, Input } from "@chakra-ui/react"; import type { MetaFunction } from "@remix-run/node"; import { CommonButton } from "~/components/CommonButton"; import { useBigBang } from "hooks/useBigBang"; +import { + useUploadMetadataToIpfs, + useUploadImageFileToIpfs, +} from "hooks/useIpfs"; export const meta: MetaFunction = () => { return [ @@ -11,26 +15,61 @@ export const meta: MetaFunction = () => { }; export default function Index() { - const { bigbang, isLoading } = useBigBang(); + const { bigbang, isLoading } = useBigBang(); + const { uploadMetadataToIpfs, isLoading: isUploadingMetadataToIpfs } = + useUploadMetadataToIpfs(); + const { + uploadImageFileToIpfs, + setImageFile, + isLoading: isUploadingImageFileToIpfs, + } = useUploadImageFileToIpfs(); - const handleBigBang = async () => { - const res = await bigbang({ - owner: "0xdCb93093424447bF4FE9Df869750950922F1E30B", - topHatDetails: "Top Hat Details", - topHatImageURI: "https://example.com/top-hat.png", - hatterHatDetails: "Hatter Hat Details", - hatterHatImageURI: "https://example.com/hatter-hat.png", - trustedForwarder: "0x1234567890123456789012345678901234567890", - }); + const handleBigBang = async () => { + const res = await bigbang({ + owner: "0xdCb93093424447bF4FE9Df869750950922F1E30B", + topHatDetails: "Top Hat Details", + topHatImageURI: "https://example.com/top-hat.png", + hatterHatDetails: "Hatter Hat Details", + hatterHatImageURI: "https://example.com/hatter-hat.png", + trustedForwarder: "0x1234567890123456789012345678901234567890", + }); - console.log(res); - }; + console.log(res); + }; - return ( - - - BigBang - - - ); + const metadata = { + name: "Toban test", + description: "Toban test", + responsibilities: "Toban test", + authorities: "Toban test", + eligibility: true, + toggle: true, + }; + + return ( + + + BigBang + + uploadMetadataToIpfs(metadata)} + > + Upload Metadata to IPFS + + ) => + setImageFile(e.target.files?.[0] || null) + } + /> + + Upload Image File to IPFS + + + ); } diff --git a/pkgs/frontend/hooks/useIpfs.ts b/pkgs/frontend/hooks/useIpfs.ts index 127c718..6b18ffa 100644 --- a/pkgs/frontend/hooks/useIpfs.ts +++ b/pkgs/frontend/hooks/useIpfs.ts @@ -1,9 +1,9 @@ import { useState } from "react"; -import { PinataSDK } from "pinata-web3"; -import { Readable } from "stream"; +import { ipfsUploadJson, ipfsUploadFile } from "utils/ipfs"; export const useUploadMetadataToIpfs = () => { const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); const uploadMetadataToIpfs = async ({ name, @@ -19,13 +19,12 @@ export const useUploadMetadataToIpfs = () => { authorities: string; eligibility: boolean; toggle: boolean; - }) => { + }): Promise<{ ipfsCid: string; ipfsUri: string } | null> => { setIsLoading(true); + setError(null); try { - const pinata = new PinataSDK({ pinataJwt: process.env.VITE_PINATA_JWT }); - - const upload = await pinata.upload.json({ + const upload = await ipfsUploadJson({ type: "1.0", data: { name, @@ -37,45 +36,64 @@ export const useUploadMetadataToIpfs = () => { }, }); - console.log("CID:", upload.IpfsHash); - console.log("URI:", `ipfs://${upload.IpfsHash}`); - } catch (error) { - console.error(error); + const ipfsCid = upload.IpfsHash; + const ipfsUri = `ipfs://${ipfsCid}`; + + console.log("Successfully uploaded metadata to IPFS"); + console.log("IPFS CID:", ipfsCid); + console.log("IPFS URI:", ipfsUri); + + return { ipfsCid, ipfsUri }; + } catch (err) { + setError( + err instanceof Error ? err : new Error("Unknown error occurred") + ); + return null; } finally { setIsLoading(false); } }; - return { uploadMetadataToIpfs, isLoading }; + return { uploadMetadataToIpfs, isLoading, error }; }; -export const useUploadImageToIpfs = () => { +export const useUploadImageFileToIpfs = () => { const [isLoading, setIsLoading] = useState(false); const [imageFile, setImageFile] = useState(null); + const [error, setError] = useState(null); - const uploadImageToIpfs = async () => { - if (!imageFile) return; + const uploadImageFileToIpfs = async (): Promise<{ + ipfsCid: string; + ipfsUri: string; + } | null> => { + if (!imageFile || !imageFile.type.startsWith("image/")) { + setError(new Error("Invalid or no image file selected")); + return null; + } setIsLoading(true); + setError(null); try { - const pinata = new PinataSDK({ pinataJwt: process.env.VITE_PINATA_JWT }); + const upload = await ipfsUploadFile(imageFile); - const buffer = await imageFile.arrayBuffer(); - const stream = Readable.from(Buffer.from(buffer)); + const ipfsCid = upload.IpfsHash; + const ipfsUri = `ipfs://${ipfsCid}`; - const upload = await pinata.upload.stream(stream, { - metadata: { name: `TobanFrontend_${new Date().getTime()}` }, - }); + console.log("Successfully uploaded image file to IPFS"); + console.log("IPFS CID:", ipfsCid); + console.log("IPFS URI:", ipfsUri); - console.log("CID:", upload.IpfsHash); - console.log("URI:", `ipfs://${upload.IpfsHash}`); - } catch (error) { - console.error(error); + return { ipfsCid, ipfsUri }; + } catch (err) { + setError( + err instanceof Error ? err : new Error("Unknown error occurred") + ); + return null; } finally { setIsLoading(false); } }; - return { uploadImageToIpfs, setImageFile, isLoading }; + return { uploadImageFileToIpfs, imageFile, setImageFile, isLoading, error }; }; diff --git a/pkgs/frontend/utils/ipfs.ts b/pkgs/frontend/utils/ipfs.ts new file mode 100644 index 0000000..690f734 --- /dev/null +++ b/pkgs/frontend/utils/ipfs.ts @@ -0,0 +1,46 @@ +import { PinataSDK } from "pinata-web3"; + +const validateEnvVariables = () => { + if (!import.meta.env.VITE_PINATA_JWT) { + throw new Error("VITE_PINATA_JWT is not defined"); + } + if (!import.meta.env.VITE_PINATA_GATEWAY) { + throw new Error("VITE_PINATA_GATEWAY is not defined"); + } +}; + +let ipfsClient: PinataSDK | null = null; + +export const createIpfsClient = () => { + if (ipfsClient) return ipfsClient; + + validateEnvVariables(); + ipfsClient = new PinataSDK({ + pinataJwt: import.meta.env.VITE_PINATA_JWT as string, + pinataGateway: import.meta.env.VITE_PINATA_GATEWAY as string, + }); + + return ipfsClient; +}; + +export const ipfsUploadJson = async (object: object) => { + try { + const ipfsClient = createIpfsClient(); + const upload = await ipfsClient.upload.json(object); + return upload; + } catch (error) { + console.error("Failed to upload JSON to IPFS:", error); + throw error; + } +}; + +export const ipfsUploadFile = async (file: File) => { + try { + const ipfsClient = createIpfsClient(); + const upload = await ipfsClient.upload.file(file); + return upload; + } catch (error) { + console.error("Failed to upload file to IPFS:", error); + throw error; + } +}; From 56da258fa5c4529b24db10922edb7dabde970a5d Mon Sep 17 00:00:00 2001 From: yawn <69970183+yawn-c111@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:37:31 +0900 Subject: [PATCH 2/3] fix: conflict fix mistake in _idex.tsx --- pkgs/frontend/app/routes/_index.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pkgs/frontend/app/routes/_index.tsx b/pkgs/frontend/app/routes/_index.tsx index ad01d41..dabd1c3 100644 --- a/pkgs/frontend/app/routes/_index.tsx +++ b/pkgs/frontend/app/routes/_index.tsx @@ -24,15 +24,6 @@ export default function Index() { isLoading: isUploadingImageFileToIpfs, } = useUploadImageFileToIpfs(); - const handleBigBang = async () => { - const res = await bigbang({ - owner: "0xdCb93093424447bF4FE9Df869750950922F1E30B", - topHatDetails: "Top Hat Details", - topHatImageURI: "https://example.com/top-hat.png", - hatterHatDetails: "Hatter Hat Details", - hatterHatImageURI: "https://example.com/hatter-hat.png", - trustedForwarder: "0x1234567890123456789012345678901234567890", - }); const handleBigBang = async () => { const res = await bigbang({ owner: "0xdCb93093424447bF4FE9Df869750950922F1E30B", From 4c0825c764ad9cb1ec6a429f865e46529f7c9f9d Mon Sep 17 00:00:00 2001 From: yawn <69970183+yawn-c111@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:49:16 +0900 Subject: [PATCH 3/3] refactor: get pinata config --- pkgs/frontend/utils/ipfs.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/pkgs/frontend/utils/ipfs.ts b/pkgs/frontend/utils/ipfs.ts index 690f734..d22d008 100644 --- a/pkgs/frontend/utils/ipfs.ts +++ b/pkgs/frontend/utils/ipfs.ts @@ -1,12 +1,17 @@ import { PinataSDK } from "pinata-web3"; -const validateEnvVariables = () => { - if (!import.meta.env.VITE_PINATA_JWT) { +const getPinataConfig = () => { + const pinataJwt = import.meta.env.VITE_PINATA_JWT; + const pinataGateway = import.meta.env.VITE_PINATA_GATEWAY; + + if (!pinataJwt) { throw new Error("VITE_PINATA_JWT is not defined"); } - if (!import.meta.env.VITE_PINATA_GATEWAY) { + if (!pinataGateway) { throw new Error("VITE_PINATA_GATEWAY is not defined"); } + + return { pinataJwt, pinataGateway }; }; let ipfsClient: PinataSDK | null = null; @@ -14,10 +19,10 @@ let ipfsClient: PinataSDK | null = null; export const createIpfsClient = () => { if (ipfsClient) return ipfsClient; - validateEnvVariables(); + const { pinataJwt, pinataGateway } = getPinataConfig(); ipfsClient = new PinataSDK({ - pinataJwt: import.meta.env.VITE_PINATA_JWT as string, - pinataGateway: import.meta.env.VITE_PINATA_GATEWAY as string, + pinataJwt, + pinataGateway, }); return ipfsClient;