Skip to content
Extraits de code Groupes Projets
Valider f9b2c82a rédigé par Mohamed Lemine BAILLAHI's avatar Mohamed Lemine BAILLAHI
Parcourir les fichiers

Merge branch 'feature/HIR-17' into 'develop'

"feature/HIR-17" - Implemented view mint tx button

Closes HIR-17

See merge request !9
parents 2b052e21 c0523593
Branches
1 requête de fusion!9"feature/HIR-17" - Implemented view mint tx button
Pipeline #3350 en échec avec les étapes
in 58 secondes
......@@ -5,3 +5,4 @@ NEXT_PUBLIC_ASSET_URL=https://api-dev-minimal-v6.vercel.app
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID="1b8245f219b45142a25b5993b5aced11"
RPC_PROVIDER ="https://polygon-amoy.infura.io/v3/ec198aefa350461caca7208e444e57a0"
\ No newline at end of file
import { ethers } from 'ethers';
import { useState } from 'react';
import {abi} from "src/web3utils/abi"
const CONTRACT_ADDRESS = '0xaf928bf2570faa745a8ef58761d515e71abc66f5';
const ABI = abi;
const providerUrl = "https://polygon-amoy.infura.io/v3/ec198aefa350461caca7208e444e57a0"
export function useFetchTxHashByTokenId() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetchTxHashByTokenId = async (tokenId: number): Promise<string | null> => {
setLoading(true);
setError(null);
try {
const provider = new ethers.JsonRpcProvider(providerUrl);
const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider);
const events = await contract.queryFilter(
contract.filters.TokenMinted(null, tokenId)
);
setLoading(false);
if (events.length > 0) {
return events[0].transactionHash;
}
return null;
} catch (err) {
setLoading(false);
setError('Failed to fetch transaction hash.');
console.error('Error fetching mint event:', err);
return null;
}
};
return { fetchTxHashByTokenId, loading, error };
}
......@@ -4,7 +4,7 @@ const _nfts: Nft[] = [
{
name: "Quiz Certificate NFT",
description: "Certificate for completing the quiz",
image: "ipfs://Qmcg9xsPTNgf6XEBnrdrjiRhC4iQuLtYeApNZQvrGrWwSs",
image: "ipfs://QmafDdftB4vwZEAuAwKUNpyqtxVib4mpTyAHKxN8RDB6U5",
tokenId: 0,
attributes: [
{ trait_type: "Name", value: "Souhail El Mahdani" },
......@@ -17,7 +17,7 @@ const _nfts: Nft[] = [
{
name: "Quiz Certificate NFT",
description: "Certificate for completing the quiz",
image: "ipfs://Qmcg9xsPTNgf6XEBnrdrjiRhC4iQuLtYeApNZQvrGrWwSs",
image: "ipfs://QmSvLyGMH1fRaQcqFfvyN3LWWgrAuG8z9NusMfNkwmu6a8",
tokenId: 1,
attributes: [
{ trait_type: "Name", value: "Alice Johnson" },
......@@ -30,7 +30,7 @@ const _nfts: Nft[] = [
{
name: "Quiz Certificate NFT",
description: "Certificate for completing the quiz",
image: "ipfs://Qmcg9xsPTNgf6XEBnrdrjiRhC4iQuLtYeApNZQvrGrWwSs",
image: "ipfs://QmakXoLL8yvsQzwLnoX41VwvroieGPzsS7Pq793hBfNf4z",
tokenId: 2,
attributes: [
{ trait_type: "Name", value: "John Smith" },
......@@ -43,7 +43,7 @@ const _nfts: Nft[] = [
{
name: "Quiz Certificate NFT",
description: "Certificate for completing the quiz",
image: "ipfs://Qmcg9xsPTNgf6XEBnrdrjiRhC4iQuLtYeApNZQvrGrWwSs",
image: "ipfs://QmULsRHmtFg2QDaNwSXdxV3EHQSUgdp6Go325iS7AAq9TS",
tokenId: 3,
attributes: [
{ trait_type: "Name", value: "Emily Davis" },
......@@ -56,26 +56,29 @@ const _nfts: Nft[] = [
{
name: "Quiz Certificate NFT",
description: "Certificate for completing the quiz",
image: "ipfs://Qmcg9xsPTNgf6XEBnrdrjiRhC4iQuLtYeApNZQvrGrWwSs",
image: "ipfs://QmT3VR7KgRHduc3xHwWVL77aM9Dndy2ghG9yP8xSSFRx3q",
tokenId: 4,
attributes: [
{ trait_type: "Name", value: "Michael Brown" },
{ trait_type: "Quiz", value: "Tokenomics" },
{ trait_type: "Score", value: "90/100" },
{ trait_type: "Date", value: "05/20/2024" }
{ trait_type: "Name", value: "Sarah Wilson" },
{ trait_type: "Quiz", value: "Blockchain Governance" },
{ trait_type: "Score", value: "95/100" },
{ trait_type: "Date", value: "10/10/2024" },
{ trait_type: "Expiry Date", value: "02/10/2026" }
]
},
{
name: "Quiz Certificate NFT",
description: "Certificate for completing the quiz",
image: "ipfs://Qmcg9xsPTNgf6XEBnrdrjiRhC4iQuLtYeApNZQvrGrWwSs",
image: "ipfs://QmdTkumUDRrWbhQHmRWWGPsM9sRxaGRQxXNUSu4qmjWwkq",
tokenId: 5,
attributes: [
{ trait_type: "Name", value: "Sarah Wilson" },
{ trait_type: "Quiz", value: "Blockchain Governance" },
{ trait_type: "Score", value: "95/100" },
{ trait_type: "Date", value: "10/10/2024" },
{ trait_type: "Name", value: "Michael Brown" },
{ trait_type: "Quiz", value: "Tokenomics" },
{ trait_type: "Score", value: "90/100" },
{ trait_type: "Date", value: "05/20/2024" },
{ trait_type: "Expiry Date", value: "02/10/2026" }
]
}
];
......@@ -83,7 +86,8 @@ const _nfts: Nft[] = [
true,
true,
true,
false,
false
true,
true,
true,
]
export { _nfts, _nftsValidity };
......@@ -4,7 +4,9 @@ import type { Nft } from 'src/types/nft';
import React, { useState, useEffect } from 'react';
import { Box, Card, Stack, Button, Divider, CardMedia, Typography } from '@mui/material';
import { Box, Card, Stack, Alert, Button, Divider, CardMedia, Typography, CircularProgress } from '@mui/material';
import { useFetchTxHashByTokenId } from 'src/hooks/use-eventFetcher';
import { _nftsValidity } from 'src/shared/_mock';
......@@ -18,41 +20,53 @@ interface NftDetailsProps {
const ipfsGateway = "https://ipfs.io/ipfs/";
const NftDetails: React.FC<NftDetailsProps> = ({ nft }) => {
const [NFTValidity, setNFTValidity] = useState<any | null>(null);
const { fetchTxHashByTokenId, loading, error } = useFetchTxHashByTokenId();
const [NFTValidity, setNFTValidity] = useState<boolean | null>(null);
const fetchNFTValidity = async () => {
try {
if (nft.tokenId !== undefined) {
const data = _nftsValidity[nft.tokenId];
console.log(data)
if (data !== undefined) {
setNFTValidity(data); // Make sure data is a boolean or null
} else {
console.error('No validity data found for token ID:', nft.tokenId);
setNFTValidity(false); // Use primitive boolean
}
const handleViewMintTx = async () => {
if (!nft.tokenId && nft.tokenId !== 0 ) return;
try {
const txHash = await fetchTxHashByTokenId(nft.tokenId);
if (txHash) {
const url = `https://amoy.polygonscan.com/tx/${txHash}`;
window.open(url, '_blank');
} else {
console.error('NFT token ID is undefined');
setNFTValidity(false); // Use primitive boolean
console.error('Transaction hash not found');
}
} catch (error) {
console.error('Failed to fetch NFT validity:', error);
setNFTValidity(false); // Use primitive boolean
} catch (fetchError) {
console.error('Error fetching transaction hash:', fetchError);
}
};
useEffect(() => {
const fetchNFTValidity = async () => {
try {
if (nft.tokenId !== undefined) {
const data = _nftsValidity[nft.tokenId];
if (data !== undefined) {
setNFTValidity(data as boolean); // Ensure that data is a boolean
} else {
console.error('No validity data found for token ID:', nft.tokenId);
setNFTValidity(false);
}
} else {
console.error('NFT token ID is undefined');
setNFTValidity(false);
}
} catch (fetchError) {
console.error('Failed to fetch NFT validity:', fetchError);
setNFTValidity(false);
}
};
fetchNFTValidity();
});
}, [nft.tokenId]); // Only re-run when nft.tokenId changes
return (
<Box sx={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', p: 3 }}>
<Box sx={{ flex: 1, mr: 3 }}>
<Card sx={{ borderRadius: '16px', boxShadow: 3 }}>
<CardMedia
component="img"
......@@ -66,9 +80,9 @@ const NftDetails: React.FC<NftDetailsProps> = ({ nft }) => {
{NFTValidity === null ? (
<Label color="warning" sx={{ mb: 3 }}>Verification...</Label>
) : NFTValidity ? (
<Label color="info" sx={{ mb: 3 }}>Valide</Label>
<Label color="info" sx={{ mb: 3 }}>Valid</Label>
) : (
<Label color="error" sx={{ mb: 3 }}>Invalide !</Label>
<Label color="error" sx={{ mb: 3 }}>Invalid!</Label>
)}
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<Typography variant="h4" sx={{ flex: 1 }}>
......@@ -104,15 +118,25 @@ const NftDetails: React.FC<NftDetailsProps> = ({ nft }) => {
color="warning"
variant="contained"
sx={{ whiteSpace: 'nowrap' }}
aria-label="View Profile"
>
Voir profile
View Profile
<Iconify icon='dashicons:external' sx={{ mx: 1 }} />
</Button>
<Button fullWidth size="large" type="submit" variant="contained">
Voir la transaction mint
<Button
fullWidth
size="large"
type="submit"
variant="contained"
onClick={handleViewMintTx}
aria-label="View Mint Transaction"
disabled={loading}
>
{loading ? <CircularProgress size={24} /> : 'View Mint Transaction'}
<Iconify icon='dashicons:external' sx={{ mx: 1 }} />
</Button>
</Stack>
{error && <Alert severity="error" sx={{ mt: 2 }}>{error}</Alert>}
</Box>
</Box>
);
......
module.exports = {
abi: [
{
inputs: [
{ internalType: "string", name: "_courseName", type: "string" },
{ internalType: "string", name: "_tokenName", type: "string" },
],
stateMutability: "nonpayable",
type: "constructor",
},
{
inputs: [{ internalType: "address", name: "owner", type: "address" }],
name: "OwnableInvalidOwner",
type: "error",
},
{
inputs: [{ internalType: "address", name: "account", type: "address" }],
name: "OwnableUnauthorizedAccount",
type: "error",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "address",
name: "owner",
type: "address",
},
{
indexed: false,
internalType: "uint256",
name: "tokenId",
type: "uint256",
},
],
name: "Minted",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "previousOwner",
type: "address",
},
{
indexed: true,
internalType: "address",
name: "newOwner",
type: "address",
},
],
name: "OwnershipTransferred",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "address",
name: "owner",
type: "address",
},
{
indexed: false,
internalType: "uint256",
name: "tokenId",
type: "uint256",
},
],
name: "Revoked",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "_owner",
type: "address",
},
{
indexed: true,
internalType: "uint256",
name: "_tokenId",
type: "uint256",
},
],
name: "TokenMinted",
type: "event",
},
{
inputs: [{ internalType: "address", name: "owner", type: "address" }],
name: "balanceOf",
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
stateMutability: "view",
type: "function",
},
{
inputs: [
{ internalType: "address", name: "_student", type: "address" },
{ internalType: "string", name: "_tokenUri", type: "string" },
],
name: "createCertificate",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "emittedCount",
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
stateMutability: "view",
type: "function",
},
{
inputs: [{ internalType: "address", name: "owner", type: "address" }],
name: "hasValid",
outputs: [{ internalType: "bool", name: "", type: "bool" }],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "holdersCount",
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
stateMutability: "view",
type: "function",
},
{
inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }],
name: "isValid",
outputs: [{ internalType: "bool", name: "", type: "bool" }],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "name",
outputs: [{ internalType: "string", name: "", type: "string" }],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "owner",
outputs: [{ internalType: "address", name: "", type: "address" }],
stateMutability: "view",
type: "function",
},
{
inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }],
name: "ownerOf",
outputs: [{ internalType: "address", name: "", type: "address" }],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "renounceOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }],
name: "revokeToken",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{ internalType: "uint256", name: "tokenId", type: "uint256" },
{ internalType: "string", name: "tokenUri", type: "string" },
],
name: "setTokenURI",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [{ internalType: "address", name: "", type: "address" }],
name: "studentToid",
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
stateMutability: "view",
type: "function",
},
{
inputs: [{ internalType: "bytes4", name: "interfaceId", type: "bytes4" }],
name: "supportsInterface",
outputs: [{ internalType: "bool", name: "", type: "bool" }],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "symbol",
outputs: [{ internalType: "string", name: "", type: "string" }],
stateMutability: "view",
type: "function",
},
{
inputs: [{ internalType: "uint256", name: "index", type: "uint256" }],
name: "tokenByIndex",
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
stateMutability: "view",
type: "function",
},
{
inputs: [
{ internalType: "address", name: "owner", type: "address" },
{ internalType: "uint256", name: "index", type: "uint256" },
],
name: "tokenOfOwnerByIndex",
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
stateMutability: "view",
type: "function",
},
{
inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }],
name: "tokenURI",
outputs: [{ internalType: "string", name: "", type: "string" }],
stateMutability: "view",
type: "function",
},
{
inputs: [{ internalType: "address", name: "owner", type: "address" }],
name: "tokensOfOwner",
outputs: [
{ internalType: "uint256[]", name: "tokens", type: "uint256[]" },
],
stateMutability: "view",
type: "function",
},
{
inputs: [{ internalType: "address", name: "newOwner", type: "address" }],
name: "transferOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
],
};
\ No newline at end of file
0% ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter