From 91aa28fa24d4d27326ecda0e15c318c1fa44fe0b Mon Sep 17 00:00:00 2001 From: RutvikGhaskataEalf Date: Thu, 26 Dec 2024 10:20:01 +0530 Subject: [PATCH 1/3] fix: update appropriate types for each functions --- nft/2-dapp/README.md | 4 +- nft/2-dapp/src/hooks/useNFTSmartContract.ts | 15 +- nft/2-dapp/src/lib/commonFunctions.ts | 2 + nft/2-dapp/src/lib/types.ts | 3 + nft/2-dapp/src/lib/utils.tsx | 19 +- nft/2-dapp/src/pages/create-nft/index.tsx | 212 +++++++++++++++++--- nft/2-dapp/src/pages/home/index.tsx | 1 + nft/2-dapp/src/pages/profile/index.tsx | 1 + 8 files changed, 219 insertions(+), 38 deletions(-) create mode 100644 nft/2-dapp/src/lib/types.ts diff --git a/nft/2-dapp/README.md b/nft/2-dapp/README.md index 0eee25c..e0be026 100644 --- a/nft/2-dapp/README.md +++ b/nft/2-dapp/README.md @@ -2,7 +2,7 @@ ## Getting Started -Frontend of our [NFT dApp](https://docs.aelf.dev/quick-start/developers/nft-dapp/#step-4---interact-with-deployed-multi-token-smart-contract). +Frontend of our [NFT dApp](https://docs.aelf.com/quick-start/developers/nft-dapp/#step-4---interact-with-deployed-multi-token-smart-contract). ## Pre-requisites @@ -20,7 +20,7 @@ git clone https://github.com/AElfProject/aelf-samples.git 2. Navigate to the project directory: ```bash -cd aelf-samples/nft-dapp-tutorials +cd aelf-samples/nft/2-dapp ``` 3. Install the necessary dependencies & libraries diff --git a/nft/2-dapp/src/hooks/useNFTSmartContract.ts b/nft/2-dapp/src/hooks/useNFTSmartContract.ts index 70f3174..8839bac 100644 --- a/nft/2-dapp/src/hooks/useNFTSmartContract.ts +++ b/nft/2-dapp/src/hooks/useNFTSmartContract.ts @@ -1,7 +1,6 @@ import { IPortkeyProvider, IChain } from "@portkey/provider-types"; import { useEffect, useState } from "react"; - -type IContract = ReturnType; +import { IContract } from "@/lib/types"; // Custom Hook for interacting with NFT Smart Contracts const useNFTSmartContract = (provider: IPortkeyProvider | null) => { @@ -9,15 +8,11 @@ const useNFTSmartContract = (provider: IPortkeyProvider | null) => { const [mainChainSmartContract, setMainChainSmartContract] = useState>(); const [sideChainSmartContract, setSideChainSmartContract] = useState>(); - //Step A - Function to fetch a smart contract based on chain symbol and contract address - const fetchContract = async () => { - - }; +//Step A - Function to fetch a smart contract based on the chain symbol and the contract address +const fetchContract = async () => {}; - // Step B - Effect hook to initialize and fetch the smart contracts when the provider changes - useEffect(() => { - - }, []); // Dependency array ensures this runs when the provider changes +// Step B - Effect hook to initialize and fetch the smart contracts when the provider changes +useEffect(() => {}, []); // Dependency array ensures this runs when the provider changes // Return the smart contract instances return { diff --git a/nft/2-dapp/src/lib/commonFunctions.ts b/nft/2-dapp/src/lib/commonFunctions.ts index 00001af..53a1403 100644 --- a/nft/2-dapp/src/lib/commonFunctions.ts +++ b/nft/2-dapp/src/lib/commonFunctions.ts @@ -1,3 +1,5 @@ +import { IContract } from "@/lib/types"; + interface Nft { nftSymbol: string; balance?: number; // Adding an optional balance property for clarity diff --git a/nft/2-dapp/src/lib/types.ts b/nft/2-dapp/src/lib/types.ts new file mode 100644 index 0000000..acc2e42 --- /dev/null +++ b/nft/2-dapp/src/lib/types.ts @@ -0,0 +1,3 @@ +import { IChain } from "@portkey/provider-types"; + +export type IContract = ReturnType; diff --git a/nft/2-dapp/src/lib/utils.tsx b/nft/2-dapp/src/lib/utils.tsx index 9ea5b40..d515482 100644 --- a/nft/2-dapp/src/lib/utils.tsx +++ b/nft/2-dapp/src/lib/utils.tsx @@ -1,5 +1,5 @@ import { type ClassValue, clsx } from "clsx"; -import { toast } from "react-toastify"; +import { Id, toast } from "react-toastify"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { @@ -19,3 +19,20 @@ export const CustomToast = ({ title, message }: any) => (

{message}

); + +export const handleError = (loadingId: Id, error: unknown) => { + if (error instanceof Error) { + toast.update(loadingId, { + render: error.message, + type: "error", + isLoading: false, + }); + } else { + toast.update(loadingId, { + render: "An unexpected error occurred.", + type: "error", + isLoading: false, + }); + } + removeNotification(loadingId); +}; \ No newline at end of file diff --git a/nft/2-dapp/src/pages/create-nft/index.tsx b/nft/2-dapp/src/pages/create-nft/index.tsx index c8c14bd..165e9d0 100644 --- a/nft/2-dapp/src/pages/create-nft/index.tsx +++ b/nft/2-dapp/src/pages/create-nft/index.tsx @@ -5,7 +5,7 @@ import { useLocation, useNavigate, useSearchParams } from "react-router-dom"; // @ts-ignore import AElf from "aelf-sdk"; import { Buffer } from "buffer"; -import { toast } from "react-toastify"; +import { Id, toast } from "react-toastify"; import { IPortkeyProvider } from "@portkey/provider-types"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -23,8 +23,14 @@ import { Button } from "@/components/ui/button"; import useNFTSmartContract from "@/hooks/useNFTSmartContract"; import "./create-nft.scss"; -import { CustomToast, delay, removeNotification } from "@/lib/utils"; +import { + CustomToast, + delay, + handleError, + removeNotification, +} from "@/lib/utils"; import { InfoIcon } from "@/components/ui/icons"; +import { IWalletInfo } from "aelf-sdk/types/wallet"; const formSchema = z.object({ tokenName: z.string(), @@ -53,7 +59,7 @@ interface INftParams { interface INftValidateResult { parentChainHeight: string | number; signedTx: string; - merklePath: { merklePathNodes: any }; + merklePath: { merklePathNodes: { hash: string; isLeftChildNode: boolean }[] }; } const wallet = AElf.wallet.getWalletByPrivateKey( @@ -66,7 +72,8 @@ const CreateNftPage = ({ currentWalletAddress: string; }) => { const [provider, setProvider] = useState(null); - const { mainChainSmartContract, sideChainSmartContract } = useNFTSmartContract(provider); + const { mainChainSmartContract, sideChainSmartContract } = + useNFTSmartContract(provider); const [transactionStatus, setTransactionStatus] = useState(false); const [isNftCollectionCreated, setIsNftCollectionCreated] = useState(false); @@ -113,7 +120,7 @@ const CreateNftPage = ({ const form = useForm>(); // Get Token Contract - const getTokenContract = async (aelf: any, wallet: any) => { + const getTokenContract = async (aelf: AElf, wallet: IWalletInfo) => { const tokenContractName = "AElf.ContractNames.Token"; // get chain status const chainStatus = await aelf.chain.getChainStatus(); @@ -134,9 +141,9 @@ const CreateNftPage = ({ }; // Get CrossChain Contract - const getCrossChainContract = async (aelf:any, wallet: any) => { + const getCrossChainContract = async (aelf: AElf, wallet: IWalletInfo) => { const crossChainContractName = "AElf.ContractNames.CrossChain"; - + // get chain status const chainStatus = await aelf.chain.getChainStatus(); // get genesis contract address @@ -151,42 +158,196 @@ const CreateNftPage = ({ await zeroContract.GetContractAddressByName.call( AElf.utils.sha256(crossChainContractName) ); - + return await aelf.chain.contractAt(crossChainContractAddress, wallet); }; //============== Create NFT Collection Steps =================// // step 1 - Create New NFT Collection on MainChain Function - const createNftCollectionOnMainChain = async () => {}; + const createNftCollectionOnMainChain = async (values: { + tokenName: string; + symbol: string; + totalSupply: string; + decimals: string; + }) => { + let createLoadingId: Id; + createLoadingId = toast.loading("Creating NFT Collection.."); + try { + // Create an object with the necessary information for the new NFT collection. + const createNtfInput: INftInput = { + tokenName: values.tokenName, // Name of the nft Collection + symbol: values.symbol, // Symbol of the token (You have to get it from your PortKey wallet on NFT seed from NFT section) + totalSupply: values.totalSupply, // Total supply of the token + decimals: values.decimals, // Decimals of the token + issuer: currentWalletAddress, // Address of the token issuer + isBurnable: true, // Indicates if the token can be burned + issueChainId: sidechain_from_chain_id, // ID of the issuing chain + owner: currentWalletAddress, // Owner's wallet address + }; + + // Call the smart contract method to create the new NFT collection on the main chain. + const result = await mainChainSmartContract?.callSendMethod( + "Create", + currentWalletAddress, + createNtfInput + ); + + // Log the result of the creation for debugging purposes. + console.log("========= result of createNewNft =========", result); + + toast.update(createLoadingId, { + render: "NFT Collection Created Successfully On MainChain", + type: "success", + isLoading: false, + }); + removeNotification(createLoadingId); + + // Return the input data for further use. + return createNtfInput; + } catch (error) { + handleError(createLoadingId, error); + return "error"; + } + }; // step 2 - Validate Collection information existence - const validateNftCollectionInfo = async () => {}; + // This function validates if the token collection information already exists on the main blockchain. + const validateNftCollectionInfo = async (values: INftInput) => { + let validateLoadingId: Id; + // Start Loading before initiate the transaction + validateLoadingId = toast.loading( + + ); + try { + // Create an object with the necessary information for token validation. + const validateInput = { + symbol: values.symbol, // Symbol of the token + tokenName: values.tokenName, // Name of the token + totalSupply: values.totalSupply, // Total supply of the token + decimals: values.decimals, // Decimals of the token + issuer: currentWalletAddress, // Address of the token issuer + isBurnable: true, // Indicates if the token can be burned + issueChainId: sidechain_from_chain_id, // ID of the issuing chain + owner: currentWalletAddress, // Owner's wallet address + }; + + // get mainnet contract + const aelfTokenContract = await getTokenContract(aelf, wallet); + + // prepare Sign the transaction using contract method (ValidateTokenInfoExists Function) + const signedTx = + aelfTokenContract.ValidateTokenInfoExists.getSignedTx(validateInput); + + // send the transaction using signed Transaction + const { TransactionId: VALIDATE_TXID } = await aelf.chain.sendTransaction( + signedTx + ); + + // get Validate Result + let VALIDATE_TXRESULT = await aelf.chain.getTxResult(VALIDATE_TXID); + + // we need to wait till our latest index Hight grater than or equal to our Transaction block number + let heightDone = false; + + while (!heightDone) { + // get latest index Hight + const sideIndexMainHeight = await GetParentChainHeight(); + if ( + // check the latest index Hight is grater than or equal + Number(sideIndexMainHeight) >= + VALIDATE_TXRESULT.Transaction.RefBlockNumber + ) { + VALIDATE_TXRESULT = await aelf.chain.getTxResult(VALIDATE_TXID); + heightDone = true; + } + } + + console.log("VALIDATE_TXRESULT", VALIDATE_TXRESULT); + + // Update the Loading Message + toast.update(validateLoadingId, { + render: "Validating Token Successfully Executed", + type: "success", + isLoading: false, + }); + + // Remove the Loading Message + removeNotification(validateLoadingId); + + // Return necessary details. + return { + transactionId: VALIDATE_TXID, + signedTx: signedTx, + BlockNumber: VALIDATE_TXRESULT.BlockNumber, + }; + } catch (error) { + // If there's an error, log it and alert the user. + console.error(error, "=====error in validateTokenInfoExist"); + handleError(validateLoadingId, error); + return "error"; + } + }; // Step 3: Get the parent chain height - const GetParentChainHeight = async () => {}; + // This function fetches the current height of the parent blockchain. + const GetParentChainHeight = async () => { + try { + const tdvwCrossChainContract = await getCrossChainContract(tdvw, wallet); + // Call the smart contract method to get the parent chain height. + const result = await tdvwCrossChainContract.GetParentChainHeight.call(); + // Return the parent chain height if it exists, otherwise return an empty string. + return result ? (result.value as string) : ""; + } catch (error) { + // If there's an error, log it and return an error status. + console.error(error, "=====error in GetParentChainHeight"); + return "error"; + } + }; + + // step 4 - Fetch the merkle path by transaction Id + const getMerklePathByTxId = async (aelf: AElf, txId: string) => { + try { + const { MerklePathNodes } = await aelf.chain.getMerklePathByTxId(txId); + + const formattedMerklePathNodes = MerklePathNodes.map( + ({ + Hash, + IsLeftChildNode, + }: { + Hash: string; + IsLeftChildNode: boolean; + }) => ({ + hash: Hash, + isLeftChildNode: IsLeftChildNode, + }) + ); - // step 4 - Fetch the Merkle path by Transaction Id - const getMerklePathByTxId = async (aelf: any, txId: string) => {}; + return { merklePathNodes: formattedMerklePathNodes }; + } catch (error) { + console.error("Error fetching Merkle path:", error); + throw new Error("Failed to get Merkle path by transaction ID."); + } + }; - // step 5 - Create a Collection on SideChain + // step 5 - Create a collection on the dAppChain const createCollectionOnSideChain = async () => {}; - //============== Create NFT Token Steps =================// - // step 6 - Create a Collection on SideChain + // step 6 - Create an NFT on the mainchain const createNFTOnMainChain = async () => {}; // step 7 - Validate a NFT Token on MainChain - const validateNftToken = async () => { - }; + const validateNftToken = async () => {}; - // step 8 - Create a NFT on SideChain. - const createNftTokenOnSideChain = async () => { - }; + // step 8 - Create a NFT on dAppChain. + const createNftTokenOnSideChain = async () => {}; - // step 9 - Issue a NFT Function which has been Created on SideChain + // step 9 - Issue a NFT Function which has been Created on dAppChain const issueNftOnSideChain = async () => {}; // step 10 - Call Necessary Function for Create NFT @@ -195,8 +356,7 @@ const CreateNftPage = ({ //============== Handle Submit Form =================// // Step 11 - Handle Submit Form - const onSubmit = async (values: z.infer) => { - }; + const onSubmit = async () => {}; return (
@@ -229,7 +389,9 @@ const CreateNftPage = ({ name="symbol" render={({ field }) => ( - Symbol + + Symbol + diff --git a/nft/2-dapp/src/pages/home/index.tsx b/nft/2-dapp/src/pages/home/index.tsx index 838eeea..0690ed6 100644 --- a/nft/2-dapp/src/pages/home/index.tsx +++ b/nft/2-dapp/src/pages/home/index.tsx @@ -8,6 +8,7 @@ import { NFT_IMAGES } from "@/lib/constant"; import { Button } from "@/components/ui/button"; import useNFTSmartContract from "@/hooks/useNFTSmartContract"; import { fetchUserNftData } from "@/lib/commonFunctions"; +import { IContract } from "@/lib/types"; const HomePage = ({ provider, diff --git a/nft/2-dapp/src/pages/profile/index.tsx b/nft/2-dapp/src/pages/profile/index.tsx index c3e577c..d305fa8 100644 --- a/nft/2-dapp/src/pages/profile/index.tsx +++ b/nft/2-dapp/src/pages/profile/index.tsx @@ -8,6 +8,7 @@ import { NFT_IMAGES } from "@/lib/constant"; import { toast } from "react-toastify"; import { CopyIcon } from "@/components/ui/icons"; import { fetchUserNftData } from "@/lib/commonFunctions"; +import { IContract } from "@/lib/types"; const ProfilePage = ({ provider, From d7edfc2747042c5c818da3954d98bf6b28bcca9f Mon Sep 17 00:00:00 2001 From: RutvikGhaskataEalf Date: Thu, 26 Dec 2024 10:25:11 +0530 Subject: [PATCH 2/3] fix: update appropriate types for each functions --- nft/2-dapp/src/pages/create-nft/index.tsx | 165 +--------------------- 1 file changed, 4 insertions(+), 161 deletions(-) diff --git a/nft/2-dapp/src/pages/create-nft/index.tsx b/nft/2-dapp/src/pages/create-nft/index.tsx index 165e9d0..360bb8c 100644 --- a/nft/2-dapp/src/pages/create-nft/index.tsx +++ b/nft/2-dapp/src/pages/create-nft/index.tsx @@ -165,173 +165,16 @@ const CreateNftPage = ({ //============== Create NFT Collection Steps =================// // step 1 - Create New NFT Collection on MainChain Function - const createNftCollectionOnMainChain = async (values: { - tokenName: string; - symbol: string; - totalSupply: string; - decimals: string; - }) => { - let createLoadingId: Id; - createLoadingId = toast.loading("Creating NFT Collection.."); - try { - // Create an object with the necessary information for the new NFT collection. - const createNtfInput: INftInput = { - tokenName: values.tokenName, // Name of the nft Collection - symbol: values.symbol, // Symbol of the token (You have to get it from your PortKey wallet on NFT seed from NFT section) - totalSupply: values.totalSupply, // Total supply of the token - decimals: values.decimals, // Decimals of the token - issuer: currentWalletAddress, // Address of the token issuer - isBurnable: true, // Indicates if the token can be burned - issueChainId: sidechain_from_chain_id, // ID of the issuing chain - owner: currentWalletAddress, // Owner's wallet address - }; - - // Call the smart contract method to create the new NFT collection on the main chain. - const result = await mainChainSmartContract?.callSendMethod( - "Create", - currentWalletAddress, - createNtfInput - ); - - // Log the result of the creation for debugging purposes. - console.log("========= result of createNewNft =========", result); - - toast.update(createLoadingId, { - render: "NFT Collection Created Successfully On MainChain", - type: "success", - isLoading: false, - }); - removeNotification(createLoadingId); - - // Return the input data for further use. - return createNtfInput; - } catch (error) { - handleError(createLoadingId, error); - return "error"; - } - }; + const createNftCollectionOnMainChain = async () => {}; // step 2 - Validate Collection information existence - // This function validates if the token collection information already exists on the main blockchain. - const validateNftCollectionInfo = async (values: INftInput) => { - let validateLoadingId: Id; - // Start Loading before initiate the transaction - validateLoadingId = toast.loading( - - ); - try { - // Create an object with the necessary information for token validation. - const validateInput = { - symbol: values.symbol, // Symbol of the token - tokenName: values.tokenName, // Name of the token - totalSupply: values.totalSupply, // Total supply of the token - decimals: values.decimals, // Decimals of the token - issuer: currentWalletAddress, // Address of the token issuer - isBurnable: true, // Indicates if the token can be burned - issueChainId: sidechain_from_chain_id, // ID of the issuing chain - owner: currentWalletAddress, // Owner's wallet address - }; - - // get mainnet contract - const aelfTokenContract = await getTokenContract(aelf, wallet); - - // prepare Sign the transaction using contract method (ValidateTokenInfoExists Function) - const signedTx = - aelfTokenContract.ValidateTokenInfoExists.getSignedTx(validateInput); - - // send the transaction using signed Transaction - const { TransactionId: VALIDATE_TXID } = await aelf.chain.sendTransaction( - signedTx - ); - - // get Validate Result - let VALIDATE_TXRESULT = await aelf.chain.getTxResult(VALIDATE_TXID); - - // we need to wait till our latest index Hight grater than or equal to our Transaction block number - let heightDone = false; - - while (!heightDone) { - // get latest index Hight - const sideIndexMainHeight = await GetParentChainHeight(); - if ( - // check the latest index Hight is grater than or equal - Number(sideIndexMainHeight) >= - VALIDATE_TXRESULT.Transaction.RefBlockNumber - ) { - VALIDATE_TXRESULT = await aelf.chain.getTxResult(VALIDATE_TXID); - heightDone = true; - } - } - - console.log("VALIDATE_TXRESULT", VALIDATE_TXRESULT); - - // Update the Loading Message - toast.update(validateLoadingId, { - render: "Validating Token Successfully Executed", - type: "success", - isLoading: false, - }); - - // Remove the Loading Message - removeNotification(validateLoadingId); - - // Return necessary details. - return { - transactionId: VALIDATE_TXID, - signedTx: signedTx, - BlockNumber: VALIDATE_TXRESULT.BlockNumber, - }; - } catch (error) { - // If there's an error, log it and alert the user. - console.error(error, "=====error in validateTokenInfoExist"); - handleError(validateLoadingId, error); - return "error"; - } - }; + const validateNftCollectionInfo = async () => {}; // Step 3: Get the parent chain height - // This function fetches the current height of the parent blockchain. - const GetParentChainHeight = async () => { - try { - const tdvwCrossChainContract = await getCrossChainContract(tdvw, wallet); - // Call the smart contract method to get the parent chain height. - const result = await tdvwCrossChainContract.GetParentChainHeight.call(); - // Return the parent chain height if it exists, otherwise return an empty string. - return result ? (result.value as string) : ""; - } catch (error) { - // If there's an error, log it and return an error status. - console.error(error, "=====error in GetParentChainHeight"); - return "error"; - } - }; + const GetParentChainHeight = async () => {}; // step 4 - Fetch the merkle path by transaction Id - const getMerklePathByTxId = async (aelf: AElf, txId: string) => { - try { - const { MerklePathNodes } = await aelf.chain.getMerklePathByTxId(txId); - - const formattedMerklePathNodes = MerklePathNodes.map( - ({ - Hash, - IsLeftChildNode, - }: { - Hash: string; - IsLeftChildNode: boolean; - }) => ({ - hash: Hash, - isLeftChildNode: IsLeftChildNode, - }) - ); - - return { merklePathNodes: formattedMerklePathNodes }; - } catch (error) { - console.error("Error fetching Merkle path:", error); - throw new Error("Failed to get Merkle path by transaction ID."); - } - }; + const getMerklePathByTxId = async () => {}; // step 5 - Create a collection on the dAppChain const createCollectionOnSideChain = async () => {}; From c1837bef08dacfb52d6c6fd41f6d7771f383ef0b Mon Sep 17 00:00:00 2001 From: RutvikGhaskataEalf Date: Thu, 26 Dec 2024 11:03:19 +0530 Subject: [PATCH 3/3] fix: handled error --- nft/2-dapp/src/pages/transfer-nft/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nft/2-dapp/src/pages/transfer-nft/index.tsx b/nft/2-dapp/src/pages/transfer-nft/index.tsx index 1c3a720..5c356e7 100644 --- a/nft/2-dapp/src/pages/transfer-nft/index.tsx +++ b/nft/2-dapp/src/pages/transfer-nft/index.tsx @@ -17,7 +17,7 @@ import "./transfer-nft.scss"; import { Button } from "@/components/ui/button"; import { NFT_IMAGES } from "@/lib/constant"; import useNFTSmartContract from "@/hooks/useNFTSmartContract"; -import { delay, removeNotification } from "@/lib/utils"; +import { delay, handleError, removeNotification } from "@/lib/utils"; import { zodResolver } from "@hookform/resolvers/zod"; const formSchema = z.object({