Rarible Clone [Part 14] - Uncaught (in promise) TypeError: Cannot read property 'length' of undefined at h.formatParam

index.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>Morarable</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" required 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="numberCreateItemPrice" 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>

    <div id="itemsForSale"></div>

    <div id="itemTemplate">
     <img src="" alt="">
     <h5></h5>
     <p></p>
    </div>

    <div id="marketplaceItemTemplate">
        <img src="" alt="">
        <h6></h6>
        <img src="" alt="">
        <h5></h5>
        <p></p>
        <button></button>
       </div>


    <script src="https://cdn.jsdelivr.net/npm/[email protected]/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>
main.js
Moralis.initialize("748DE3i3o1yJPE6S41YRRXGfo34wQrhq8jziOkd6");

Moralis.serverURL = "https://f2opvlqwefqt.usemoralis.com:2053/server";

const TOKEN_CONTRACT_ADDRESS = "0xa12084e15E28F44D55B41C443A829004973733EE";
const MARKETPLACE_CONTRACT_ADDRESS =
  "0xe8219c8c928943e580779C2dBAbED2B5E732440e";

init = async () => {
  hideElement(openUserItemsButton);
  hideElement(userInfo);
  hideElement(createItemForm);
  window.web3 = await Moralis.Web3.enable();
  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 soldItemsQuery.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){
    if(user.get('accounts').includes(item.attributes.buyer)){
      const params = {uid: `${item.attributes.uid}`};
      const soldItem = await Moralis.Cloud.run('getItem', params);
      if(soldItem){
        getAndRenderItemData(soldItem, renderUserItem);
      }
    };
  }
}


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)){
        getAndRenderItemData(addedItem, renderUserItem);
        return;
        }
      };

      getAndRenderItemData(addedItem, renderItem);
  }
}

initUser = async () => {
  if (await Moralis.User.current()) {
    showElement(userProfileButton);
    showElement(openCreateItemButton);
    showElement(openUserItemsButton);
    hideElement(userConnectButton);
    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("avatar.jpg", userAvatarFile.files[0]);
    user.set("avatar", avatar);
  }

  console.log("Saving...");
  await user.save();
  console.log("Saved!");
  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.value);
  item.set("nftFileHash", nftFileHash.value);
  item.set("metadataFilePath", 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) => {
    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)) return;
    }
    getAndRenderItemData(item, renderItem);
  });
};

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);
};


renderItem = (item) => {
  const itemForSale = marketplaceItemTemplate.cloneNode(true);
  console.log("Are we here");
  if(item.avatar){
    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);
};

const userLogout = document.getElementById("btnLogout");

userLogout.onclick = logout;

const userSaveInfo = document.getElementById("btnSaveUserInfo");

userSaveInfo.onclick = saveUserInfo;

//Item Creation

const createItemForm = document.getElementById("createItem");

const createItemNameField = document.getElementById("txtCreateItemName");
const createItemDescriptionField = document.getElementById(
  "txtCreateItemDescription"
);
const createItemPriceField = document.getElementById("numberCreateItemPrice");
const createItemStatusField = document.getElementById("selectCreateItemStatus");
const createItemFile = document.getElementById("fileCreateItemFile");

document.getElementById("btnCloseCreateItem").onclick = () => {
  hideElement(createItemForm);
};
const createItemButton = document.getElementById("btnCreateItem");

createItemButton.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();

abi.js
var tokenContractAbi = [
  {
    "inputs": [],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "approved",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "Approval",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "operator",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "approved",
        "type": "bool"
      }
    ],
    "name": "ApprovalForAll",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "Transfer",
    "type": "event"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "Items",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "id",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "internalType": "string",
        "name": "uri",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function",
    "constant": true
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "approve",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "balanceOf",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function",
    "constant": true
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "getApproved",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function",
    "constant": true
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "operator",
        "type": "address"
      }
    ],
    "name": "isApprovedForAll",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function",
    "constant": true
  },
  {
    "inputs": [],
    "name": "name",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function",
    "constant": true
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "ownerOf",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function",
    "constant": true
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "safeTransferFrom",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "bytes",
        "name": "_data",
        "type": "bytes"
      }
    ],
    "name": "safeTransferFrom",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "operator",
        "type": "address"
      },
      {
        "internalType": "bool",
        "name": "approved",
        "type": "bool"
      }
    ],
    "name": "setApprovalForAll",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes4",
        "name": "interfaceId",
        "type": "bytes4"
      }
    ],
    "name": "supportsInterface",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function",
    "constant": true
  },
  {
    "inputs": [],
    "name": "symbol",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function",
    "constant": true
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "transferFrom",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "string",
        "name": "uri",
        "type": "string"
      }
    ],
    "name": "createItem",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "tokenURI",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function",
    "constant": true
  }
];

var marketplaceContractAbi = [
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "id",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "tokenAddress",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "askingPrice",
        "type": "uint256"
      }
    ],
    "name": "itemAdded",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "id",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "buyer",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "askingPrice",
        "type": "uint256"
      }
    ],
    "name": "itemSold",
    "type": "event"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "itemsForSale",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "id",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "tokenAddress",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "address payable",
        "name": "seller",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "askingPrice",
        "type": "uint256"
      },
      {
        "internalType": "bool",
        "name": "isSold",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function",
    "constant": true
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "tokenAddress",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "askingPrice",
        "type": "uint256"
      }
    ],
    "name": "addItemToMarket",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "id",
        "type": "uint256"
      }
    ],
    "name": "buyItem",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function",
    "payable": true
  }
];
MorarableToken.sol
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;
    }
}

Marketplace.sol
pragma solidity ^0.8.0;

import "../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol";

contract MorarableMarketContract {
    struct AuctionItem {
        uint256 id;
        address tokenAddress;
        uint256 tokenId;
        address payable seller;
        uint256 askingPrice;
        bool isSold;
    }

    AuctionItem[] public itemsForSale;
    mapping(address => mapping(uint256 => bool)) activeItems;

    event itemAdded(
        uint256 id,
        uint256 tokenId,
        address tokenAddress,
        uint256 askingPrice
    );
    event itemSold(uint256 id, address buyer, uint256 askingPrice);

    modifier OnlyItemOwner(address tokenAddress, uint256 tokenId) {
        IERC721 tokenContract = IERC721(tokenAddress);
        require(tokenContract.ownerOf(tokenId) == msg.sender);
        _;
    }

    modifier HasTransferApproval(address tokenAddress, uint256 tokenId) {
        IERC721 tokenContract = IERC721(tokenAddress);
        require(tokenContract.getApproved(tokenId) == address(this));
        _;
    }

    modifier ItemExists(uint256 id) {
        require(
            id < itemsForSale.length && itemsForSale[id].id == id,
            "Could not find item"
        );
        _;
    }

    modifier IsForSale(uint256 id) {
        require(itemsForSale[id].isSold == false, "Item is already sold!");
        _;
    }

    function addItemToMarket(
        uint256 tokenId,
        address tokenAddress,
        uint256 askingPrice
    )
        external
        OnlyItemOwner(tokenAddress, tokenId)
        HasTransferApproval(tokenAddress, tokenId)
        returns (uint256)
    {
        require(
            activeItems[tokenAddress][tokenId] == false,
            "Item is already up for sale!"
        );
        uint256 newItemId = itemsForSale.length;
        itemsForSale.push(
            AuctionItem(
                newItemId,
                tokenAddress,
                tokenId,
                payable(msg.sender),
                askingPrice,
                false
            )
        );
        activeItems[tokenAddress][tokenId] = true;

        assert(itemsForSale[newItemId].id == newItemId);
        emit itemAdded(newItemId, tokenId, tokenAddress, askingPrice);

        return newItemId;
    }

    function buyItem(uint256 id)
        external
        payable
        ItemExists(id)
        IsForSale(id)
        HasTransferApproval(
            itemsForSale[id].tokenAddress,
            itemsForSale[id].tokenId
        )
    {
        require(
            msg.value >= itemsForSale[id].askingPrice,
            "Not enough funds sent"
        );

        require(msg.sender != itemsForSale[id].seller);

        itemsForSale[id].isSold = true;
        activeItems[itemsForSale[id].tokenAddress][
            itemsForSale[id].tokenId
        ] = false;

        IERC721(itemsForSale[id].tokenAddress).safeTransferFrom(
            itemsForSale[id].seller,
            msg.sender,
            itemsForSale[id].tokenId
        );

        itemsForSale[id].seller.transfer(msg.value);

        emit itemSold(id, msg.sender, itemsForSale[id].askingPrice);
    }
}

When I click to buy the item I want I get the error as shown. I have tried re-pasting the .json compiled code to abi.js files again but issue still remains.

Hi Jesal.
I had a similar issue when putting up for sale. I had an error in cloud functions and fixed it with this: https://github.com/MoralisWeb3/youtube-tutorials/blob/main/rarible-clone/cloud_functions.js

Just change the “NFTTokenOwners” for “EthNFTOwners”.
Hope this helps you.

1 Like

Hey @Mateo03 :grinning:
Great job! Thanks for the help.

I think that was really the problem. Will wait for the @Jesal response.

Hi @Mateo03,

Thanks for the response.

I do in fact already have my cloud functions referencing to “EthNFTOwners” already instead of NFTTokenOwners, yet I still have the same issue.

Here is my Cloud Functions code:

Cloud Functions
Moralis.Cloud.define("getUserItems", async (request) => {
  const query = new Moralis.Query("EthNFTOwners");
  query.equalTo("contract_type", "ERC721");
  query.containedIn("owner_of", request.user.attributes.accounts);
  const queryResults = await query.find();
  const results = [];
  
  for (let i = 0; i < queryResults.length; ++i) {
    results.push({
    	"id": queryResults[i].attributes.objectId,
      	"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;
});


Moralis.Cloud.beforeSave("ItemsForSale", async (request) => {
  const query = new Moralis.Query("EthNFTOwners");
  query.equalTo("token_address", request.object.get('tokenAddress'));
  query.equalTo("token_id", request.object.get('tokenId'));
  const object = await query.first();
  if(object){
  	const owner = object.attributes.owner_of;
    const userQuery = new Moralis.Query(Moralis.User);
    userQuery.equalTo("accounts", owner);
    const userObject = await userQuery.first({useMasterKey:true});
    if(userObject){
    	 request.object.set('user', userObject)
    }
    request.object.set('token', object);
  }
 
});


Moralis.Cloud.beforeSave("SoldItems", async (request) => {
  const query = new Moralis.Query("ItemsForSale");
  query.equalTo("uid", request.object.get('uid'));
  const item = await query.first();
  if(item){
    request.object.set('item', item);
    item.set('isSold', true);
    await item.save();

    const userQuery = new Moralis.Query(Moralis.User);
    userQuery.equalTo("accounts", request.object.get('buyer'));
    const userObject = await userQuery.first({useMasterKey:true});
    if(userObject){
    	 request.object.set('user', userObject)
    }
  }
 
});


Moralis.Cloud.define("getItems", async (request) => {
  const logger = Moralis.Cloud.getLogger();
  const query = new Moralis.Query("ItemsForSale");
  query.notEqualTo("isSold", true);

  query.select("uid", "tokenAddress", "tokenId", "askingPrice", "token.token_uri", "token.symbol", "token.owner_of", "user.username", "user.avatar")
  const queryResults = await query.find({useMasterKey:true});
  const results = [];
  
  for (let i = 0; i < queryResults.length; ++i) {
    
    if(!queryResults[i].attributes.token || !queryResults[i].attributes.user) continue;
    results.push({
    	"uid": queryResults[i].attributes.objectId,
      	"tokenId": queryResults[i].attributes.tokenId,
        "tokenAddress": queryResults[i].attributes.tokenAddress,
      	"askingPrice": queryResults[i].attributes.askingPrice,
      	"symbol": queryResults[i].attributes.token.attributes.symbol,
      	"tokenUri": queryResults[i].attributes.token.attributes.token_uri,
        "ownerOf": queryResults[i].attributes.token.attributes.owner_of,
      	"sellerUsername": queryResults[i].attributes.user.attributes.username,
      	"sellerAvatar": queryResults[i].attributes.user.attributes.avatar
    });

  }
  return results;
});



Moralis.Cloud.define("getItem", async (request) => {
  const logger = Moralis.Cloud.getLogger();
  const query = new Moralis.Query("ItemsForSale");
  query.EqualTo("uid", request.params.uid);

  query.select("uid", "tokenAddress", "tokenId", "askingPrice", "token.token_uri", "token.symbol", "token.owner_of", "user.username", "user.avatar")
  const queryResult = await query.first({useMasterKey:true});
  if(!queryResult) return;
  
  return {
    	"uid": queryResult.attributes.uid,
      	"tokenId": queryResult.attributes.tokenId,
        "tokenAddress": queryResult.attributes.tokenAddress,
      	"askingPrice": queryResult.attributes.askingPrice,
      	"symbol": queryResult.attributes.token.attributes.symbol,
      	"tokenUri": queryResult.attributes.token.attributes.token_uri,
        "ownerOf": queryResult.attributes.token.attributes.owner_of,
      	"sellerUsername": queryResult.attributes.user.attributes.username,
      	"sellerAvatar": queryResult.attributes.user.attributes.avatar
    };
});
Moralis.Cloud.define("getUserItems", async (request) => {

    const query = new Moralis.Query("EthNFTOwners");
    query.equalTo("contract_type", "ERC721");
    query.containedIn("owner_of", request.user.attributes.accounts);
    const queryResults = await query.find();
    const results = [];
    for (let i = 0; i < queryResults.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;
  });
  
  Moralis.Cloud.beforeSave("ItemsForSale", async (request) => {
  
    const query = new Moralis.Query("EthNFTOwners");
    query.equalTo("token_address", request.object.get('tokenAddress'));
    query.equalTo("token_id", request.object.get('tokenId'));
    const object = await query.first();
    if (object){
        const owner = object.attributes.owner_of;
      const userQuery = new Moralis.Query(Moralis.User);
        userQuery.equalTo("accounts", owner);
      const userObject = await userQuery.first({useMasterKey:true});
      if (userObject){
          request.object.set('user', userObject);
      }
      request.object.set('token', object);
    }
  });
  
  Moralis.Cloud.beforeSave("SoldItems", async (request) => {
  
    const query = new Moralis.Query("ItemsForSale");
    query.equalTo("uid", request.object.get('uid'));
    const item = await query.first();
    if (item){
      request.object.set('item', item);
      item.set('isSold', true);
      await item.save();
      
    
      const userQuery = new Moralis.Query(Moralis.User);
        userQuery.equalTo("accounts", request.object.get('buyer'));
      const userObject = await userQuery.first({useMasterKey:true});
      if (userObject){
          request.object.set('user', userObject);
      }
    }
  });    
  
  Moralis.Cloud.define("getItems", async (request) => {
      
    const query = new Moralis.Query("ItemsForSale");
    query.notEqualTo("isSold", true);
    query.select("uid","askingPrice","tokenAddress","tokenId", "token.token_uri", "token.symbol","token.owner_of","token.id", "user.avatar","user.username");
  
    const queryResults = await query.find({useMasterKey:true});
    const results = [];
  
    for (let i = 0; i < queryResults.length; ++i) {
  
      if (!queryResults[i].attributes.token || !queryResults[i].attributes.user) continue;
  
      results.push({
        "uid": queryResults[i].attributes.uid,
        "tokenId": queryResults[i].attributes.tokenId,
        "tokenAddress": queryResults[i].attributes.tokenAddress,
        "askingPrice": queryResults[i].attributes.askingPrice,
  
        "symbol": queryResults[i].attributes.token.attributes.symbol,
        "tokenUri": queryResults[i].attributes.token.attributes.token_uri,
        "ownerOf": queryResults[i].attributes.token.attributes.owner_of,
        "tokenObjectId": queryResults[i].attributes.token.id,
        
        "sellerUsername": queryResults[i].attributes.user.attributes.username,
        "sellerAvatar": queryResults[i].attributes.user.attributes.avatar,
      });
    }
  
    return results;
  });
  
  Moralis.Cloud.define("getItem", async (request) => {
      
    const query = new Moralis.Query("ItemsForSale");
    query.equalTo("uid", request.params.uid);
    query.select("uid","askingPrice","tokenAddress","tokenId", "token.token_uri", "token.symbol","token.owner_of","token.id","user.avatar","user.username");
  
    const queryResult = await query.first({useMasterKey:true});
    if (!queryResult) return;
    return {
        "uid": queryResult.attributes.uid,
        "tokenId": queryResult.attributes.tokenId,
        "tokenAddress": queryResult.attributes.tokenAddress,
        "askingPrice": queryResult.attributes.askingPrice,
  
        "symbol": queryResult.attributes.token.attributes.symbol,
        "tokenUri": queryResult.attributes.token.attributes.token_uri,
        "ownerOf": queryResult.attributes.token.attributes.owner_of,
        "tokenObjectId": queryResult.attributes.token.id,
  
        "sellerUsername": queryResult.attributes.user.attributes.username,
        "sellerAvatar": queryResult.attributes.user.attributes.avatar,
      };
  });
2 Likes

That seemed to work, thanks!

1 Like

Thanks for the amazing help @Mateo03! Keep up the awesome work! :raised_hands:

Great Job @Mateo03 :muscle:

Thanks for the help :man_factory_worker: