I also have the feeling that suddenly EthNFTTokenHolders doesnât show any new minted tokens anymore although we didnât change anything in the deployed code since executing it successfully. at the end of last week. Not sure if that problem we had before or if the problem is inside of our code for mystic reasons.
I am also now having a sort of mystical problem. Havenât changed anything thatI can recall but Iâm now having a hard time getting saved user avatar to render after connecting wallet, or saving any new user data. Havenât tried minting new NFT yet since these problems began. Iâll post my code here in a moment.
Moralis.initialize("9quLXz9G6OoV6LXrakGsZQNPvCiTu9Fo5NJ0DN7y");
Moralis.serverURL = "https://sqmfav2bsl7q.moralis.io:2053/server";
const TOKEN_CONTRACT_ADDRESS = "0x9b22914bfF952c89a8F822fE38beF74CCF287616";
//Nav bar
const userInfo = document.getElementById("userInfo");
const userConnectButton = document.getElementById("btnConnect");
const userProfileButton = document.getElementById("btnUserInfo");
//User Profile
const userUsernameField = document.getElementById("txtUsername");
const userEmailField = document.getElementById("txtEmail");
const userAvatarFile = document.getElementById("fileAvatar");
const userAvatarImg = document.getElementById("imgAvatar");
const openCreateItemButton = document.getElementById("btnOpenCreateItem");
const closeCreateItemButton = document.getElementById("btnCloseCreateItem");
const createItemForm = document.getElementById("createItem");
//Upload item
const createItemNameField = document.getElementById("txtCreateItemName");
const createItemDescriptionField = document.getElementById(
"txtCreateItemDescription"
);
const createItemPrice = document.getElementById("numCreateItemPrice");
const createItemStatus = document.getElementById("selectCreateItemStatus");
const createItemFile = document.getElementById("fileCreateItemFile");
init = async () => {
hideElement(userItemsSection);
hideElement(userInfo);
hideElement(createItemForm);
window.web3 = await Moralis.Web3.enable();
window.tokenContract = new web3.eth.Contract(
tokenContractAbi,
TOKEN_CONTRACT_ADDRESS
);
initUser();
};
createItem = async () => {
if (createItemFile.files.length == 0) {
alert("Please select a file!");
return;
} else if (createItemNameField.value.length == 0) {
alert("please give the item a name!");
return;
}
const nftFile = new Moralis.File("nftFile.jpg", createItemFile.files[0]);
await nftFile.saveIPFS();
const nftFilePath = nftFile.ipfs();
const nftFileHash = nftFile.hash();
const metadata = {
name: createItemNameField.value,
description: createItemDescriptionField.value,
image: nftFilePath,
};
const nftFileMetadataFile = new Moralis.File("metadata.json", {
base64: btoa(JSON.stringify(metadata)),
});
await nftFileMetadataFile.saveIPFS();
const nftFileMetadataFilePath = nftFileMetadataFile.ipfs();
const nftFileMetadataFileHash = nftFileMetadataFile.hash();
const nftId = await mintNft(nftFileMetadataFilePath);
const Item = Moralis.Object.extend("Item");
const item = new Item();
item.set("name", createItemNameField.value);
item.set("description", createItemDescriptionField.value);
item.set("nftFilePath", nftFilePath);
item.set("nftFileHash", nftFileHash);
item.set("metadataFilePath", nftFileMetadataFilePath);
item.set("metadataFileHash", nftFileMetadataFileHash);
item.set("nftId", nftId);
item.set("nftContractAddress", TOKEN_CONTRACT_ADDRESS);
await item.save();
console.log(item);
};
mintNft = async (metadataUrl) => {
const receipt = await tokenContract.methods
.createItem(metadataUrl)
.send({ from: ethereum.selectedAddress });
console.log(receipt);
return receipt.events.Transfer.returnValues.tokenId;
};
hideElement = (e) => {
e.style.display = "none";
};
showElement = (e) => {
e.style.display = "block";
};
initUser = async () => {
if (await Moralis.User.current()) {
hideElement(userConnectButton);
showElement(userProfileButton);
showElement(openCreateItemButton);
showElement(openUserItemsBtn);
loadUserItems();
} else {
showElement(userConnectButton);
hideElement(userProfileButton);
hideElement(openCreateItemButton);
}
};
login = async () => {
try {
await Moralis.Web3.authenticate();
initUser();
} catch (error) {
alert(error);
}
};
userConnectButton.onclick = login;
openUserInfo = async () => {
user = await Moralis.User.current();
if (user) {
const email = user.get("email");
if (email) {
userEmailField.value = email;
} else {
userEmailField.value = "";
}
userUsernameField.value = user.get("username");
const userAvatar = user.get("avatar");
if (userAvatar) {
userAvatarImg.src = userAvatar.url();
showElement(userAvatarImg);
} else {
hideElement(userAvatarImg);
}
showElement(userInfo);
} else {
login();
}
};
userProfileButton.onclick = openUserInfo;
logout = async () => {
await Moralis.User.logOut();
hideElement(userInfo);
initUser();
};
document.getElementById("btnCloseUserInfo").onclick = () =>
hideElement(userInfo);
document.getElementById("btnLogout").onclick = logout;
saveUserInfo = async () => {
user.set("email", userEmailField.value);
user.set("username", userUsernameField.value);
if (userAvatarFile.files.length > 0) {
const avatar = new Moralis.File("avatar.jpg", userAvatarFile.files[0]);
user.set("avatar", avatar);
}
await user.save();
alert("User info saved successfully!");
openUserInfo();
};
openUserItems = async () => {
user = await Moralis.User.current();
if (user) {
showElement(userItemsSection);
} else {
login();
}
};
loadUserItems = async () => {
const ownedItems = await Moralis.Cloud.run("getUserItems");
ownedItems.forEach((item) => {
getAndRenderItemData(item, renderUserItem);
});
};
initTemplate = (id) => {
const template = document.getElementById(id);
template.id = "";
template.parentNode.removeChild(template);
return template;
};
renderUserItem = (item) => {
const userItem = userItemTemplate.cloneNode(true);
userItem.getElementsByTagName("img")[0].src = item.image;
userItem.getElementsByTagName("img")[0].alt = item.name;
userItem.getElementsByTagName("h5")[0].innerText = item.name;
userItem.getElementsByTagName("p")[0].innerText = item.description;
userItems.appendChild(userItem);
};
getAndRenderItemData = (item, renderFunction) => {
fetch(item.tokenUri)
.then((res) => res.json())
.then((data) => {
data.symbol = item.symbol;
data.tokenId = item.tokenId;
data.tokenAddress = item.tokenAddress;
renderFunction(data);
});
};
document.getElementById("btnCreateItem").onclick = createItem;
document.getElementById("btnSaveUserInfo").onclick = saveUserInfo;
openCreateItemButton.onclick = () => showElement(createItemForm);
closeCreateItemButton.onclick = () => hideElement(createItemForm);
//user items
const userItemsSection = document.getElementById("userItems");
const userItems = document.getElementById("userItemsList");
const openUserItemsBtn = document.getElementById("btnMyItems");
const closeUserItemsBtn = document.getElementById("btnCloseUserItems");
openUserItemsBtn.onclick = openUserItems;
closeUserItemsBtn.onclick = () => hideElement(userItemsSection);
const userItemTemplate = initTemplate("itemTemplate");
init();
Fixed this second problem for now, but I am still stuck at trying to find an âEthNFTTokenHoldersâ table in the dashboard.
Problem solved! For anyone following the rarible clone tutorial, keep going past this point, when you complete the market contract section the EthNFTTokenOwners will appear in the dashboard.
Thank you for updating with the solution and the suggestion. Cheers.
thank you for clearing the way.
been stuck for hours! thanx
Hi, I am using the code from Github youtube-tutorials. But I couldnot get populate any table called âNftTokenOwnersâ. I have been stuck here for a while. Can anyone please help?
const appId = "j9Un8xwpv5HW0duTFkEwK3OEHZUw65IDtjE64MT4";
const serverUrl = 'https://0frjwqyxjgun.usemoralis.com:2053/server';
Moralis.start({ serverUrl, appId });
const TOKEN_CONTRACT_ADDRESS = "0xD35145C64571FdAEe05427D8d52D45791A402a78";
const MARKETPLACE_CONTRACT_ADDRESS = "0x271080CdEF4649571CC0762B64f878F1cFc7D939";
init = async () => {
hideElement(userItemsSection);
hideElement(userInfo);
hideElement(createItemForm);
await Moralis.enableWeb3();
window.web3 = new Web3(Moralis.provider)
window.tokenContract = new web3.eth.Contract(tokenContractAbi, TOKEN_CONTRACT_ADDRESS);
window.marketplaceContract = new web3.eth.Contract(marketplaceContractAbi, MARKETPLACE_CONTRACT_ADDRESS);
initUser();
loadItems();
const soldItemsQuery = new Moralis.Query('SoldItems');
const soldItemsSubscription = await soldItemsQuery.subscribe();
soldItemsSubscription.on("create", onItemSold);
const itemsAddedQuery = new Moralis.Query('ItemsForSale');
const itemsAddedSubscription = await itemsAddedQuery.subscribe();
itemsAddedSubscription.on("create", onItemAdded);
}
onItemSold = async (item) => {
const listing = document.getElementById(`item-${item.attributes.uid}`);
if (listing){
listing.parentNode.removeChild(listing);
}
user = await Moralis.User.current();
if (user){
const params = {uid: `${item.attributes.uid}`};
const soldItem = await Moralis.Cloud.run('getItem', params);
if (soldItem){
if (user.get('accounts').includes(item.attributes.buyer)){
getAndRenderItemData(soldItem, renderUserItem);
}
const userItemListing = document.getElementById(`user-item-${item.tokenObjectId}`);
if (userItemListing) userItemListing.parentNode.removeChild(userItemListing);
}
}
}
onItemAdded = async (item) => {
const params = {uid: `${item.attributes.uid}`};
const addedItem = await Moralis.Cloud.run('getItem', params);
if (addedItem){
user = await Moralis.User.current();
if (user){
if (user.get('accounts').includes(addedItem.ownerOf)){
const userItemListing = document.getElementById(`user-item-${item.tokenObjectId}`);
if (userItemListing) userItemListing.parentNode.removeChild(userItemListing);
getAndRenderItemData(addedItem, renderUserItem);
return;
}
}
getAndRenderItemData(addedItem, renderItem);
}
}
initUser = async () => {
if (await Moralis.User.current()){
hideElement(userConnectButton);
showElement(userProfileButton);
showElement(openCreateItemButton);
showElement(openUserItemsButton);
loadUserItems();
}else{
showElement(userConnectButton);
hideElement(userProfileButton);
hideElement(openCreateItemButton);
hideElement(openUserItemsButton);
}
}
login = async () => {
try {
await Moralis.Web3.authenticate();
initUser();
} catch (error) {
alert(error)
}
}
logout = async () => {
await Moralis.User.logOut();
hideElement(userInfo);
initUser();
}
openUserInfo = async () => {
user = await Moralis.User.current();
if (user){
const email = user.get('email');
if(email){
userEmailField.value = email;
}else{
userEmailField.value = "";
}
userUsernameField.value = user.get('username');
const userAvatar = user.get('avatar');
if(userAvatar){
userAvatarImg.src = userAvatar.url();
showElement(userAvatarImg);
}else{
hideElement(userAvatarImg);
}
showElement(userInfo);
}else{
login();
}
}
saveUserInfo = async () => {
user.set('email', userEmailField.value);
user.set('username', userUsernameField.value);
if (userAvatarFile.files.length > 0) {
const avatar = new Moralis.File("avatar1.jpg", userAvatarFile.files[0]);
user.set('avatar', avatar);
}
await user.save();
alert("User info saved successfully!");
openUserInfo();
}
createItem = async () => {
if (createItemFile.files.length == 0){
alert("Please select a file!");
return;
} else if (createItemNameField.value.length == 0){
alert("Please give the item a name!");
return;
}
const nftFile = new Moralis.File("nftFile.jpg",createItemFile.files[0]);
await nftFile.saveIPFS();
const nftFilePath = nftFile.ipfs();
const nftFileHash = nftFile.hash();
const metadata = {
name: createItemNameField.value,
description: createItemDescriptionField.value,
image: nftFilePath,
};
const nftFileMetadataFile = new Moralis.File("metadata.json", {base64 : btoa(JSON.stringify(metadata))});
await nftFileMetadataFile.saveIPFS();
const nftFileMetadataFilePath = nftFileMetadataFile.ipfs();
const nftFileMetadataFileHash = nftFileMetadataFile.hash();
const nftId = await mintNft(nftFileMetadataFilePath);
// Simple syntax to create a new subclass of Moralis.Object.
const Item = Moralis.Object.extend("Item");
// Create a new instance of that class.
const item = new Item();
item.set('name', createItemNameField.value);
item.set('description', createItemDescriptionField.value);
item.set('nftFilePath', nftFilePath);
item.set('nftFileHash', nftFileHash);
item.set('metadataPathFile', nftFileMetadataFilePath);
item.set('metadataFileHash', nftFileMetadataFileHash);
item.set('nftId', nftId);
item.set('nftContractAddress', TOKEN_CONTRACT_ADDRESS);
await item.save();
console.log(item);
user = await Moralis.User.current();
const userAddress = user.get('ethAddress');
switch(createItemStatusField.value){
case "0":
return;
case "1":
await ensureMarketplaceIsApproved(nftId, TOKEN_CONTRACT_ADDRESS);
await marketplaceContract.methods.addItemToMarket(nftId, TOKEN_CONTRACT_ADDRESS, createItemPriceField.value).send({from: userAddress });
break;
case "2":
alert("Not yet supported!");
return;
}
}
mintNft = async (metadataUrl) => {
const receipt = await tokenContract.methods.createItem(metadataUrl).send({from: ethereum.selectedAddress});
console.log(receipt);
return receipt.events.Transfer.returnValues.tokenId;
}
openUserItems = async () => {
user = await Moralis.User.current();
if (user){
showElement(userItemsSection);
}else{
login();
}
}
loadUserItems = async () => {
const ownedItems = await Moralis.Cloud.run("getUserItems");
ownedItems.forEach(item => {
const userItemListing = document.getElementById(`user-item-${item.tokenObjectId}`);
if (userItemListing) return;
getAndRenderItemData(item, renderUserItem);
});
}
loadItems = async () => {
const items = await Moralis.Cloud.run("getItems");
user = await Moralis.User.current();
items.forEach(item => {
if (user){
if (user.attributes.accounts.includes(item.ownerOf)){
const userItemListing = document.getElementById(`user-item-${item.tokenObjectId}`);
if (userItemListing) userItemListing.parentNode.removeChild(userItemListing);
getAndRenderItemData(item, renderUserItem);
return;
}
}
getAndRenderItemData(item, renderItem);
});
}
initTemplate = (id) => {
const template = document.getElementById(id);
template.id = "";
template.parentNode.removeChild(template);
return template;
}
renderUserItem = async (item) => {
const userItemListing = document.getElementById(`user-item-${item.tokenObjectId}`);
if (userItemListing) return;
const userItem = userItemTemplate.cloneNode(true);
userItem.getElementsByTagName("img")[0].src = item.image;
userItem.getElementsByTagName("img")[0].alt = item.name;
userItem.getElementsByTagName("h5")[0].innerText = item.name;
userItem.getElementsByTagName("p")[0].innerText = item.description;
userItem.getElementsByTagName("input")[0].value = item.askingPrice ?? 1;
userItem.getElementsByTagName("input")[0].disabled = item.askingPrice > 0;
userItem.getElementsByTagName("button")[0].disabled = item.askingPrice > 0;
userItem.getElementsByTagName("button")[0].onclick = async () => {
user = await Moralis.User.current();
if (!user){
login();
return;
}
await ensureMarketplaceIsApproved(item.tokenId, item.tokenAddress);
await marketplaceContract.methods.addItemToMarket(item.tokenId, item.tokenAddress, userItem.getElementsByTagName("input")[0].value).send({from: user.get('ethAddress') });
};
userItem.id = `user-item-${item.tokenObjectId}`
userItems.appendChild(userItem);
}
renderItem = (item) => {
const itemForSale = marketplaceItemTemplate.cloneNode(true);
if (item.sellerAvatar){
itemForSale.getElementsByTagName("img")[0].src = item.sellerAvatar.url();
itemForSale.getElementsByTagName("img")[0].alt = item.sellerUsername;
}
itemForSale.getElementsByTagName("img")[1].src = item.image;
itemForSale.getElementsByTagName("img")[1].alt = item.name;
itemForSale.getElementsByTagName("h5")[0].innerText = item.name;
itemForSale.getElementsByTagName("p")[0].innerText = item.description;
itemForSale.getElementsByTagName("button")[0].innerText = `Buy for ${item.askingPrice}`;
itemForSale.getElementsByTagName("button")[0].onclick = () => buyItem(item);
itemForSale.id = `item-${item.uid}`;
itemsForSale.appendChild(itemForSale);
}
getAndRenderItemData = (item, renderFunction) => {
fetch(item.tokenUri)
.then(response => response.json())
.then(data => {
item.name = data.name;
item.description = data.description;
item.image = data.image;
renderFunction(item);
})
}
ensureMarketplaceIsApproved = async (tokenId, tokenAddress) => {
user = await Moralis.User.current();
const userAddress = user.get('ethAddress');
const contract = new web3.eth.Contract(tokenContractAbi, tokenAddress);
const approvedAddress = await contract.methods.getApproved(tokenId).call({from: userAddress});
if (approvedAddress != MARKETPLACE_CONTRACT_ADDRESS){
await contract.methods.approve(MARKETPLACE_CONTRACT_ADDRESS,tokenId).send({from: userAddress});
}
}
buyItem = async (item) => {
user = await Moralis.User.current();
if (!user){
login();
return;
}
await marketplaceContract.methods.buyItem(item.uid).send({from: user.get('ethAddress'), value: item.askingPrice});
}
hideElement = (element) => element.style.display = "none";
showElement = (element) => element.style.display = "block";
// Navbar
const userConnectButton = document.getElementById("btnConnect");
userConnectButton.onclick = login;
const userProfileButton = document.getElementById("btnUserInfo");
userProfileButton.onclick = openUserInfo;
const openCreateItemButton = document.getElementById("btnOpenCreateItem");
openCreateItemButton.onclick = () => showElement(createItemForm);
// User profile
const userInfo = document.getElementById("userInfo");
const userUsernameField = document.getElementById("txtUsername");
const userEmailField = document.getElementById("txtEmail");
const userAvatarImg = document.getElementById("imgAvatar");
const userAvatarFile = document.getElementById("fileAvatar");
document.getElementById("btnCloseUserInfo").onclick = () => hideElement(userInfo);
document.getElementById("btnLogout").onclick = logout;
document.getElementById("btnSaveUserInfo").onclick = saveUserInfo;
// Item creation
const createItemForm = document.getElementById("createItem");
const createItemNameField = document.getElementById("txtCreateItemName");
const createItemDescriptionField = document.getElementById("txtCreateItemDescription");
const createItemPriceField = document.getElementById("numCreateItemPrice");
const createItemStatusField = document.getElementById("selectCreateItemStatus");
const createItemFile = document.getElementById("fileCreateItemFile");
document.getElementById("btnCloseCreateItem").onclick = () => hideElement(createItemForm);
document.getElementById("btnCreateItem").onclick = createItem;
// User items
const userItemsSection = document.getElementById("userItems");
const userItems = document.getElementById("userItemsList");
document.getElementById("btnCloseUserItems").onclick = () => hideElement(userItemsSection);
const openUserItemsButton = document.getElementById("btnMyItems");
openUserItemsButton.onclick = openUserItems;
const userItemTemplate = initTemplate("itemTemplate");
const marketplaceItemTemplate = initTemplate("marketplaceItemTemplate");
// Items for sale
const itemsForSale = document.getElementById("itemsForSale");
init();
I believe this has been removed in the Nitro, if you create a new one, itâs going to be Nitro, to fetch the token owners now you can just use the Web3 API getNFTOwners
https://docs.moralis.io/moralis-server/web3-sdk/token#getnftowners
As @YosephKS mentioned, this has been now removed in the newest version of Moralis (Nitro).
For more reference -
Thanks for you reply @malik @YosephKS. I tried the api to access the nft owners after minting. It successfully add in the Item table but when I use this api to console log, it returns zero elements. I am not sure what am I missing.
Mentions: @cryptokid
Note: I am using local dev eth chain connected with my ganache.
const options = { address: TOKEN_CONTRACT_ADDRESS };
const nftOwners = await Moralis.Web3API.token.getNFTOwners(options);
console.log( nftOwners);
Logs:
Issue resolved. My mistake. Thanks everyone
@malik I understand that itâs removed but how can I fetch the owners with a local dev chain as web3 api is not supported for these chains?
I donât think that you can do it now with a local devchain as web3api will not work with local devchain, it will work if you use a testnet instead of a localdevchain
How was the issue resolved please tell?
I am also stuck in the same way