alex:
listItem
Thanks for your reply ! and this is my code
My Marketplace Smart contract
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
error AlreadyListedItem(address nftAdddress, uint256 tokenId);
error NotOwner();
error NotListed(address nftAdddress, uint256 tokenId);
error PriceMustGreaterThanZero();
error NotApproved();
error NotEnoughFund(address nftAdddress, uint256 tokenId, uint256 price);
contract MarketplaceV3 is ReentrancyGuard {
struct Listing {
uint256 price;
address seller;
}
event ItemListed(
address indexed seller,
address nftAddress,
uint256 tokenId,
uint256 price
);
event ItemBought(
address indexed buyer,
address indexed nftAddress,
uint256 indexed tokenId,
uint256 price
);
event ItemCanceled(
address indexed seller,
address indexed nftAddress,
uint256 indexed tokenId
);
mapping(address => mapping(uint256 => Listing)) private s_listings;
mapping(address => uint256) private s_proceeds;
modifier Notlisted(
address nftAddress,
uint256 tokenId,
address owner
) {
Listing memory listing = s_listings[nftAddress][tokenId];
if (listing.price > 0) {
revert AlreadyListedItem(nftAddress, tokenId);
}
_;
}
modifier isOwner(
address nftAddress,
uint256 tokenId,
address spender
) {
IERC721 nft = IERC721(nftAddress);
address owner = nft.ownerOf(tokenId);
if (spender != owner) {
revert NotOwner();
}
_;
}
modifier isListed(address nftAddress, uint256 tokenId) {
Listing memory listing = s_listings[nftAddress][tokenId];
if (listing.price <= 0) {
revert NotListed(nftAddress, tokenId);
}
_;
}
function ListItem(
address nftAddress,
uint256 tokenId,
uint256 price
)
external
Notlisted(nftAddress, tokenId, msg.sender)
isOwner(nftAddress, tokenId, msg.sender)
{
if (price <= 0) {
revert PriceMustGreaterThanZero();
}
IERC721 nft = IERC721(nftAddress);
if (nft.getApproved(tokenId) != address(this)) {
revert NotApproved();
}
s_listings[nftAddress][tokenId] = Listing(price, msg.sender);
emit ItemListed(msg.sender, nftAddress, tokenId, price);
}
function cancelListing(address nftAddress, uint256 tokenId)
external
isOwner(nftAddress, tokenId, msg.sender)
isListed(nftAddress, tokenId)
{
delete (s_listings[nftAddress][tokenId]);
emit ItemCanceled(msg.sender, nftAddress, tokenId);
}
function updateListing(
address nftAddress,
uint256 tokenId,
uint256 newPrice
)
external
isListed(nftAddress, tokenId)
isOwner(nftAddress, tokenId, msg.sender)
{
s_listings[nftAddress][tokenId].price = newPrice;
emit ItemListed(msg.sender, nftAddress, tokenId, newPrice);
}
function getListing(address nftAddress, uint256 tokenId)
external
view
returns (Listing memory)
{
return s_listings[nftAddress][tokenId];
}
}
My NFT smart contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
contract NFTV2 is ERC721, ERC721URIStorage, ERC721Enumerable {
using SafeMath for uint256;
uint256 public constant mintPrice = 0;
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal override(ERC721, ERC721Enumerable) {
super._beforeTokenTransfer(from, to, tokenId);
}
function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
super._burn(tokenId);
}
function tokenURI(uint256 tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, ERC721Enumerable)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
constructor() ERC721("NFTMint", "NFT") {}
function mint(string memory _uri) public payable {
uint256 mintIndex = totalSupply();
_safeMint(msg.sender, mintIndex);
_setTokenURI(mintIndex, _uri);
}
}
I successfully deployed it in goerli test net and interact with the ABI successfully via this Mint.js
const Mint = () => {
const { isAuthenticated, user, Moralis } = useMoralis();
const router = useRouter();
const [name, setName] = useState("");
const options = ['Not For Sale','Put on Sell'];
const [status, setStatus] = useState(options[0]);
const [description, setDescription] = useState("");
const [file, setFile] = useState(null);
const [price, setPrice] = useState([]);
const account = Moralis.account;
const dispatch = useNotification()
const {runContractFunction} = useWeb3Contract();
const web3 = new Web3(Web3.givenProvider, {
network: "goerli",
cacheProvider: true,
});
useEffect(() => {
if (!isAuthenticated) router.push("/");
}, [isAuthenticated]);
const onSubmit = async (e) => {
e.preventDefault();
try {
//save image to IPFS
// generate metadata and save to IPFS
// interact with smart contract
const file1 = new Moralis.File(file.name, file);
await file1.saveIPFS();
const file1url = file1.ipfs();
const filePath = file1.hash();
console.log(file1url);
const metadata = {
name: name,
description: description,
image: file1url,
filePath: filePath,
status: status,
price: ethers.utils.parseUnits(price, "ether").toString(),
};
//saving files
const file2 = new Moralis.File(`${name}file.json`, {
base64: btoa(JSON.stringify(metadata)),
});
const file2Path = file2.ipfs();
const file2Hash = file2.hash();
await file2.saveIPFS();
const metadataURL = file2.ipfs();
const contract = new web3.eth.Contract(
nftContractAbi,
nftContractAddress
);
const response = await contract.methods.mint(metadataURL).send({
from: user.get("ethAddress"),
});
const tokenId = response.events.Transfer.returnValues.tokenId;
console.log(tokenId, nftContractAddress);
const NFT = new Moralis.Object.extend("NFT");
const query = new Moralis.Query(NFT);
query.equalTo("tokenId", tokenId);
query.equalTo("nftAddress", nftContractAddress);
query.equalTo("src", file1url);
const alreadyMinted = await query.first();
console.log(`Existed NFT ${JSON.stringify(alreadyMinted)}`);
if (alreadyMinted) {
await alreadyMinted.destroy();
console.log(`NFT Destroy`);
}
const nft = new NFT();
nft.set("nftName", name);
nft.set("src", file1url);
nft.set("tokenURI", metadataURL);
nft.set("price", price);
nft.set("nftFilePath", file1url);
nft.set("nftFileHash", filePath);
nft.set("nftMetadataFilePath", file2Path);
nft.set("nftMetadataFileHash", file2Hash);
nft.set("tokenId", tokenId);
nft.set("nftAddress", nftContractAddress);
nft.set("Status", status);
nft.set("seller", account);
nft.set("description", description);
console.log("Saving NFT to database ...");
console.log(nft);
await nft.save();
alert(
`NFT minted successfully - Contract Address:${nftContractAddress} and tokenId : ${tokenId}`
);
if(status ==options[0]){
return;
}
if(status ==options[1]){
console.log('Put for sell now ');
const approveoptions = {
abi: nftContractAbi,
contractAddress:nftContractAddress,
functionName:'approve',
params:{
to: MarketAddress,
tokenId: tokenId,
},
}
await runContractFunction({
params: approveoptions,
onSuccess: () => handleApproveSuccess(nftContractAddress,tokenId,price),
onError:(error) =>{
console.log(error);
}
})
async function handleApproveSuccess(nftContractAddress,tokenId, price){
console.log('Listing ...');
const listOptions={
abi: MarketABI,
contractAddress: MarketAddress,
functionName:'ListItem',
params:{
nftAddress: nftContractAddress,
tokenId:tokenId,
price: price
},
}
console.log('Ready ...');
await runContractFunction({
params:listOptions,
onSuccess: handleListSuccess,
onError: (error) => console.log(error),
//onError: handleListFailed
})
}
async function handleListSuccess() {
dispatch({
type: "success",
message: "NFT listing",
title: "NFT listed",
position: "topR",
})
}
async function handleListFailed(){
dispatch({
type:"failed",
message:"Listing Failed",
title:"List failed",
position:"topR"
})
}
}
router.push("/");
} catch (error) {
console.error(error);
alert(error);
}
}
return (
<div>
<div className="flew w-screen h-screen items-center justify-center">
<div className="w-1/2 flex flex-col pb-12">
<h3 className="py-4 px-4 font-bold text-3xl"> Mint Your NFTs</h3>
<form onSubmit={onSubmit}>
<div>
<input
value={name}
placeholder="NFT Name"
className="mt-8 border rounded p-4"
onChange={(e) => setName(e.target.value)}
required
/>
</div>
<div>
<input
value={description}
placeholder="Description"
className="mt-8 border rounded p-4"
onChange={(e) => setDescription(e.target.value)}
required
/>
<input
value={price}
placeholder="Price"
className="mt-8 border rounded p-4"
onChange={(e) => setPrice(e.target.value)}
required
/>
</div>
<label>Put on sell ?</label>
<div>
<select onChange={(e) => setStatus(e.target.value)} defaultValue={status}>
{options.map((option, idx) => (
<option key={idx}>{option}</option>
))}
</select>
</div>
<div>
<input
type="file"
placeholder="File"
className="mt-2 border rounded p-4"
onChange={(e) => setFile(e.target.files[0])}
required
/>
</div>
<button
type="submit"
className="font-bold mt-4 bg-pink-500 text-white rounded p-4 shadow-lg"
>
Mint NFT
</button>
</form>
</div>
</div>
</div>
);
};
export default Mint;