Hey guys
I’ve been following the Moralis tutorial and all is working great until part 8. My issue is I don’t have all the columns in the Moralis database. Is this a issue in the cloud function? I’m not really sure what my problem is. I’ve attached my HTML, Javascript, Solidity and my Cloud function.
Thanks in advance to anyone who can help.
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Morarables</title>
</head>
<body>
<div>
<button id="btnConnect">Connect Wallet</button>
<button id="btnUserInfo">Profile</button>
<button id="btnOpenCreateItem">Create</button>
<button id="btnMyItems">My Items</button>
</div>
<div id="userInfo">
<h4>User Profile</h4>
<input type="text" id="txtUsername" required placeholder="Enter username">
<input type="text" id="txtEmail" placeholder="Enter email">
<small>Optional</small>
<img width="50" height="50" src="" id="imgAvatar" alt="">
<label for="fileAvatar">Select Avatar</label>
<input type="file" id="fileAvatar">
<button id="btnLogOut">Log out</button>
<button id="btnCloseUserInfo">Close</button>
<button id="btnSaveUserInfo">Save</button>
</div>
<div id="createItem">
<h4>Create Item</h4>
<input type="text" id="txtCreateItemName" required placeholder="Enter name">
<textarea id="txtCreateItemDescription" cols="30" rows="5" placeholder="Enter description"></textarea>
<input type="number" min="1" step="1" id="numCreateItemPrice" placeholder="Enter price" required>
<label for="selectCreateItemStatus">Status</label>
<select id="selectCreateItemStatus">
<option value="0">Not for sale</option>
<option value="1">Instant buy</option>
<option value="2">Accept Offers</option>
</select>
<label for="fileCreateItemFile">Select File</label>
<input type="file" id="fileCreateItemFile">
<button id="btnCloseCreateItem">Close</button>
<button id="btnCreateItem">Create!</button>
</div>
<div id="userItems">
<h4>My Items</h4>
<div id="userItemsList"></div>
<button id="btnCloseUserItems">Close</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script>
<script src="https://unpkg.com/moralis/dist/moralis.js"></script>
<script src="abi.js"></script>
<script src="main.js"></script>
</body>
</html>
Javascript:
const serverUrl = "https://hbrhmupyiqgd.usemoralis.com:2053/server";
const appId = "jSR4OAe7qfcWQYDcYVH0nWdvwOxnhUO1tyxrKGWZ";
const TOKEN_CONTRACT_ADDRESS = "0x9a5B99bAb34FE642Bf4946F7fBA349519E0a7b31";
Moralis.start({ serverUrl, appId });
init = async () => {
hideElement(userItemsSection);
hideElement(userInfo);
hideElement(createItemForm);
await Moralis.enableWeb3();
web3 = new Web3(web3.currentProvider);
window.tokenContract = new web3.eth.Contract(tokenContractAbi, TOKEN_CONTRACT_ADDRESS)
initUser();
loadUserItems();
}
initUser = async () => {
if (await Moralis.User.current()){
hideElement(userConnectButton);
showElement(userProfileButton);
showElement(openCreateItemButton);
showElement(openUserItemsButton);
}else{
showElement(userConnectButton);
hideElement(userProfileButton);
hideElement(openCreateItemButton);
hideElement(createItemForm);
hideElement(openUserItemsButton);
}
}
login = async () => {
try {
await Moralis.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 = "";
}
userNameField.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', userNameField.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.webp", 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('nftContractAddres', 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;
}
openUserItems = async () => {
user = await Moralis.User.current();
if (user){
showElement(userItemsSection);
}else{
login();
}
}
loadUserItems = async () => {
const ownedItems = await Moralis.Cloud.run("getUserItems");
console.log(ownedItems);
}
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;
//Userinfo
const userInfo = document.getElementById("userInfo");
const userNameField = 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");
const openCreateItemButton = document.getElementById("btnOpenCreateItem");
openCreateItemButton.onclick = () => showElement(createItemForm);
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;
init();
Cloud Function:
Moralis.Cloud.define("getUserItems", async (request) => {
const query = new Moralis.Query("EthNFTTokenOwners");
query.equalTo("contract_type", "ERC721");
query.containedIn("owner_of", request.user.attributes.accounts);
const queryresults = await query.find();
let sum = 0;
for(let i = 0; i < results.length; ++i) {
results.push({
"tokenObjectId": queryResults[i].id,
"tokenId": queryResults[i].attributes.token_id,
"tokenAddress": queryResults[i].attributes.token_address,
"symbol": queryResults[i].attributes.symbol,
"tokenUri": queryResults[i].attributes.token_uri,
});
}
return results;
});
Solidity:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../node_modules/@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "../node_modules/@openzeppelin/contracts/utils/Counters.sol";
contract MorarableToken is ERC721 {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
constructor () ERC721("MorarableToken", "MORA" ){}
struct Item {
uint256 id;
address creator;
string uri;
}
mapping (uint256 => Item) public Items;
function createItem(string memory uri) public returns (uint256){
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_safeMint(msg.sender, newItemId);
Items[newItemId] = Item(newItemId, msg.sender, uri);
return newItemId;
}
function tokenURI(uint256 tokenId) public view override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
return Items[tokenId].uri;
}
}