Initial commit with 🏗️ Scaffold-ETH 2 @ 1.0.5

This commit is contained in:
han
2026-01-10 18:17:37 +07:00
commit 98751c5b87
165 changed files with 29073 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
"use client";
import { useEffect, useState } from "react";
import { NFTCard } from "./NFTCard";
import { useAccount } from "wagmi";
import { useScaffoldContract, useScaffoldReadContract } from "~~/hooks/scaffold-eth";
import { notification } from "~~/utils/scaffold-eth";
import { getMetadataFromIPFS } from "~~/utils/tokenization/ipfs-fetch";
import { NFTMetaData } from "~~/utils/tokenization/nftsMetadata";
export interface Collectible extends Partial<NFTMetaData> {
id: number;
uri: string;
owner: string;
}
export const MyHoldings = () => {
const { address: connectedAddress } = useAccount();
const [myAllCollectibles, setMyAllCollectibles] = useState<Collectible[]>([]);
const [allCollectiblesLoading, setAllCollectiblesLoading] = useState(false);
const { data: yourCollectibleContract } = useScaffoldContract({
contractName: "YourCollectible",
});
const { data: myTotalBalance } = useScaffoldReadContract({
contractName: "YourCollectible",
functionName: "balanceOf",
args: [connectedAddress],
watch: true,
});
useEffect(() => {
const updateMyCollectibles = async (): Promise<void> => {
if (myTotalBalance === undefined || yourCollectibleContract === undefined || connectedAddress === undefined)
return;
setAllCollectiblesLoading(true);
const collectibleUpdate: Collectible[] = [];
const totalBalance = parseInt(myTotalBalance.toString());
for (let tokenIndex = 0; tokenIndex < totalBalance; tokenIndex++) {
try {
const tokenId = await yourCollectibleContract.read.tokenOfOwnerByIndex([
connectedAddress,
BigInt(tokenIndex),
]);
const tokenURI = await yourCollectibleContract.read.tokenURI([tokenId]);
const ipfsHash = tokenURI.replace("https://ipfs.io/ipfs/", "");
const nftMetadata: NFTMetaData = await getMetadataFromIPFS(ipfsHash);
collectibleUpdate.push({
id: parseInt(tokenId.toString()),
uri: tokenURI,
owner: connectedAddress,
...nftMetadata,
});
} catch (e) {
notification.error("Error fetching all collectibles");
setAllCollectiblesLoading(false);
console.log(e);
}
}
collectibleUpdate.sort((a, b) => a.id - b.id);
setMyAllCollectibles(collectibleUpdate);
setAllCollectiblesLoading(false);
};
updateMyCollectibles();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [connectedAddress, myTotalBalance]);
if (allCollectiblesLoading)
return (
<div className="flex justify-center items-center mt-10">
<span className="loading loading-spinner loading-lg"></span>
</div>
);
return (
<>
{myAllCollectibles.length === 0 ? (
<div className="flex justify-center items-center mt-10">
<div className="text-2xl text-primary-content">No NFTs found</div>
</div>
) : (
<div className="flex flex-wrap gap-4 my-8 px-5 justify-center">
{myAllCollectibles.map(item => (
<NFTCard nft={item} key={item.id} />
))}
</div>
)}
</>
);
};

View File

@@ -0,0 +1,66 @@
import { useState } from "react";
import { Collectible } from "./MyHoldings";
import { Address, AddressInput } from "~~/components/scaffold-eth";
import { useScaffoldWriteContract } from "~~/hooks/scaffold-eth";
export const NFTCard = ({ nft }: { nft: Collectible }) => {
const [transferToAddress, setTransferToAddress] = useState("");
const { writeContractAsync } = useScaffoldWriteContract({ contractName: "YourCollectible" });
return (
<div className="card card-compact bg-base-100 shadow-lg w-[300px] shadow-secondary">
<figure className="relative">
{/* eslint-disable-next-line */}
<img src={nft.image} alt="NFT Image" className="h-60 min-w-full" />
<figcaption className="glass absolute bottom-4 left-4 p-4 rounded-xl">
<span className="text-white "># {nft.id}</span>
</figcaption>
</figure>
<div className="card-body space-y-3">
<div className="flex items-center justify-center">
<p className="text-xl p-0 m-0 font-semibold">{nft.name}</p>
<div className="flex flex-wrap space-x-2 mt-1">
{nft.attributes?.map((attr, index) => (
<span key={index} className="badge badge-primary px-1.5">
{attr.value}
</span>
))}
</div>
</div>
<div className="flex flex-col justify-center mt-1">
<p className="my-0 text-lg">{nft.description}</p>
</div>
<div className="flex space-x-3 mt-1 items-center">
<span className="text-lg font-semibold">Owner : </span>
<Address address={nft.owner} />
</div>
<div className="flex flex-col my-2 space-y-1">
<span className="text-lg font-semibold mb-1">Transfer To: </span>
<AddressInput
value={transferToAddress}
placeholder="receiver address"
onChange={newValue => setTransferToAddress(newValue)}
/>
</div>
<div className="card-actions justify-end">
<button
className="btn btn-secondary btn-md px-8 tracking-wide"
onClick={() => {
try {
writeContractAsync({
functionName: "transferFrom",
args: [nft.owner, transferToAddress, BigInt(nft.id.toString())],
});
} catch (err) {
console.error("Error calling transferFrom function", err);
}
}}
>
Send
</button>
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1 @@
export * from "./MyHoldings";

View File

@@ -0,0 +1,70 @@
"use client";
import { MyHoldings } from "./_components";
import type { NextPage } from "next";
import { useAccount } from "wagmi";
import { RainbowKitCustomConnectButton } from "~~/components/scaffold-eth";
import { useScaffoldReadContract, useScaffoldWriteContract } from "~~/hooks/scaffold-eth";
import { notification } from "~~/utils/scaffold-eth";
import { addToIPFS } from "~~/utils/tokenization/ipfs-fetch";
import nftsMetadata from "~~/utils/tokenization/nftsMetadata";
const MyNFTs: NextPage = () => {
const { address: connectedAddress, isConnected, isConnecting } = useAccount();
const { writeContractAsync } = useScaffoldWriteContract({ contractName: "YourCollectible" });
const { data: tokenIdCounter } = useScaffoldReadContract({
contractName: "YourCollectible",
functionName: "tokenIdCounter",
watch: true,
});
const handleMintItem = async () => {
// circle back to the zero item if we've reached the end of the array
if (tokenIdCounter === undefined) return;
const tokenIdCounterNumber = Number(tokenIdCounter);
const currentTokenMetaData = nftsMetadata[tokenIdCounterNumber % nftsMetadata.length];
const notificationId = notification.loading("Uploading to IPFS");
try {
const uploadedItem = await addToIPFS(currentTokenMetaData);
// First remove previous loading notification and then show success notification
notification.remove(notificationId);
notification.success("Metadata uploaded to IPFS");
await writeContractAsync({
functionName: "mintItem",
args: [connectedAddress, uploadedItem.path],
});
} catch (error) {
notification.remove(notificationId);
console.error(error);
}
};
return (
<>
<div className="flex items-center flex-col pt-10">
<div className="px-5">
<h1 className="text-center mb-8">
<span className="block text-4xl font-bold">My NFTs</span>
</h1>
</div>
</div>
<div className="flex justify-center">
{!isConnected || isConnecting ? (
<RainbowKitCustomConnectButton />
) : (
<button className="btn btn-secondary" onClick={handleMintItem}>
Mint NFT
</button>
)}
</div>
<MyHoldings />
</>
);
};
export default MyNFTs;