Rarible clone part 13,14

Hey guys
I’ve just finished part 13 and 14 of the tutorial series.
Everything on my Dapp works mostly great so far. After my NFTs are minted and they have been put up for sale they wont have the buy now feature and price I typed under them like in the video. I’m also still having a json issue when its trying to fetch the Token Uri. apart from these two issues I haven’t noticed anything else that’s not working. I’ve been doing some trial and error on the fetching of the token Uri but i struggled and got abit confused. Thanks to everyone who has helped me get this far with this Dapp and cant wait to keep moving forward. I’m forever grateful :slight_smile:
Cheers

HTML

<!DOCTYPE html>

<html lang="en">

<head>

    <link rel="shortcut icon" href="">

    <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>Moraribles</title>

    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">

    <link rel="stylesheet" href="main.css">

 

</head>

<body class="bg-dark">

    <nav class="navbar navbar-expand-lg navbar-dark bg-transparent border-bottom border-light">

       

          <a class="navbar-brand btn btn-outline-moralis" href="#">Morarible</a>

          <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">

            <span class="navbar-toggler-icon"></span>

          </button>

          <div class="collapse navbar-collapse" id="navbarNav">

            <form class="mx-2 d-inline flex-md-fill">

              <input type="search" class="bg-transparent border-light form-control text-light mr-sm-2 rounded-pill" placeholder="search by item or creator">

            </form>

            <ul class="navbar-nav mr-auto">

              <li class="nav-item">

                <a class="nav-link" id="btnMyItems" href="#">My Items</a>

              </li>

            </ul>

            <div class="d-flex">

              <button class="btn btn-primary rounded-pill my-2 my-sm-0 mr-md-2" id="btnOpenCreateItem">Create</button>

              <button class="btn btn-outline-moralis rounded-pill my-2 my-sm-0" id="btnConnect">Connect Wallet</button>

              <button class="btn btn-outline-moralis rounded-pill my-2 my-sm-0" id="btnUserInfo">Profile</button>

   

            </div>

           </div>

      </nav>

  <div class="container"></div>

      <div class="row row-cols-1 row-cols-md-4 mt-5" id="itemsForSale">

 </div>

 <div class="col mb-4" id="marketplaceItemTemplate">

  <div class="card h-100 border-light bg-transparent text-light">

      <nav class="card-header navbar navbar-dark text-light p-1">

          <!-- <img width="30" height="30" class="d-inline-block align-top rounded-circle" src="" alt=""> -->

          <span></span>

      </nav>

      <img src="..." class="card-img-top" alt="...">

      <div class="card-body d-flex align-items-end">

          <div class="w-100">

              <h5 class="card-title"></h5>

              <p class="card-text"></p>

              <button class="btn btn-primary btn-block"></button>

          </div>

      </div>

    </div>

</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>

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

    <div id="itemTemplate">

      <img src="" alt="">

      <h5></h5>

      <p></p>

      <input type="number" min="1" step="1">

      <button>Put For Sale</button>

    </div>

    <div id="marketplaceItemTemplate">

      <img src="" alt="">

      <h6></h6>

      <img src="" alt="">

      <h5></h5>

      <p></p>

      <button class="btn btn-primary btn-block"></button>

    </div>

           

    <script src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script>

    <script src="https://unpkg.com/moralis@latest/dist/moralis.js"></script>

    <script src="main.js"></script>

    <script src="abi.js"></script>

    <script src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>

</body>

</html>

Javascript

const serverUrl = "https://vxnx9mu8hh9u.usemoralis.com:2053/server";

const appId = "z75nQcm3wcoe9xd9MK8c6Lye9akXiMw1QldhfvE6";

const TOKEN_CONTRACT_ADDRESS = "0x99495223da7dFE1A14d5224Bc90bad4a01F0e58A";

const MARKETPLACE_CONTRACT_ADDRESS = "0x5b71C0259d1cFa36DD71A2C308E5A6378db5977f";

Moralis.start({ serverUrl, appId });

init = async () => {

    hideElement(userItemsSection);

    hideElement(userInfo);

    hideElement(createItemForm);

    await Moralis.enableWeb3();

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

        loadUserItems();

        loadItems();

        const soldItemsQuery = new Moralis.Query('SoldItems');

        const soldItemsSubscription = await soldItemsQuery.subscribe();

        soldItemsSubscription.on('create', onItemSold);

   

        const itemsAddedQuery = new Moralis.Query('ItemsForSale');

        const itemsAddedQuerySubscription = await itemsAddedQuery.subscribe();

        itemsAddedQuerySubscription.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);

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

}

loadUserItems = async () => {

    const params =  { token_address: TOKEN_CONTRACT_ADDRESS };

    const ownedItems = await Moralis.Cloud.run("getUserItems", params);

    ownedItems.forEach(item => {

        const userItemListing = document.getElementById(`user-item-${item.tokenObjectId}`);

        if (userItemListing) return;

        getAndRenderItemData(item, renderUserItem);

    });

    console.log(ownedItems)

}

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

    });

}

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;

    }

    mintNFT = async (metadataUri) => {

        console.log("mintNFT :", ethereum.selectedAddress)

        const receipt = await tokenContract.methods.createItem(metadataUri).send({from: ethereum.selectedAddress});;

   

        console.log(receipt);

   

        return receipt.events.Transfer.returnValues.tokenId;

    }

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

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;

           

}

buyItem = async (item) => {

    user = await Moralis.User.current();

    const userAddress = user.get('ethAddress');

    if(!user)

    {

        login();

        return;

    }

    await marketplaceContract.methods.buyItem(item.uid).send({from: user.get('ethAddress'), value: item.askingPrice});

}

}

initTemplate = (id) => {

    const template = document.getElementById(id);

    template.id = "";

    template.parentNode.removeChild(template);

    return template;

}

    renderUserItem = async  (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);

        console.log(userItems);

   

    }

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")[0].src = item.image;

    itemForSale.getElementsByTagName("img")[0].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}`;

    itemForSale.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');

   

    console.log("ensureMarketplaceIsApproved",  userAddress);

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

    }

}

ensureMarketplaceIsApproved = async (tokenId, tokenAddress) => {

    console.log(tokenId);

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

    }

}

hideElement = (element) => element.style.display = "none";

showElement = (element) => element.style.display = "block";

openUserItems = async () => {

    user = await Moralis.User.current();

    if (user){    

        showElement(userItemsSection);

    }else{

        login();

    }

}

//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;

const userItemTemplate = initTemplate("itemTemplate");

const marketplaceItemTemplate = initTemplate("marketplaceItemTemplate");

//Items for sale

const itemsForSale = document.getElementById("itemsForSale");

init();

Cloud function

Moralis.Cloud.define("getUserItems", async (request) => {

  const options = { address: request.params.token_address, chain: "ropsten" };

  const NFTowners = await Moralis.Web3API.token.getNFTOwners(options);

  const queryResults = NFTowners.result.filter(x => x.owner_of == request.user.attributes.accounts[0]);

  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("EthNFTTransfers");

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

 if (request.user){

   query.notContainedIn("token.owner_of", request.user.attributes.accounts);

 }

 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,

     

      "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", "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,

      "tokenObjectId": queryResult.attributes.token.id,

      "sellerUsername": queryResult.attributes.user.attributes.username,

      "sellerAvatar": queryResult.attributes.user.attributes.avatar,

    };

});

Marketplace smart contract

pragma solidity ^0.8.0;

//SPDX-License-Identifier: UNLICENSED

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 them");

    _;

}

modifier IsForSale(uint256 id){

    require(itemsForSale[id].isSold == false, "Item is already sold!");

    _;

}

    function addItemToMarket(uint256 tokenId, address tokenAddress, uint256 askingPrice) OnlyItemOwner(tokenAddress,tokenId) HasTransferApproval(tokenAddress,tokenId) external 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) payable external 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);

}

}

Morarible Token smart contract

pragma solidity ^0.8.0;

//SPDX-License-Identifier: UNLICENSED

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;

    }

}

Hello, so for token URI, it could be that the api call is returning null as token_uri, so I suggest looking at which token ids have trouble fetching the uri and check if there is actually a uri or if it is just null.

And then for the 1st problem I belive you have a typo on your last line of renderItem function, should it not be itemsForSale.appendChild(itemForSale)?

1 Like

Ok Thank you
Ill have a look at that now. I do have the token Uri’s after I mint and list NFT’s for sale. I’m pretty sure they are not null. :slight_smile:

Oh, I see, the problem you are having is the unexpected token in position 0?

1 Like

yes that is the issue I’m having :slight_smile:

Hey guys
I’ve been able to get some of the errors fixed. I’m now having a error in the getItems Cloud function and I’m struggling to correct it the right way. Does anything stick out that could help me fix this issue?

Cloud function

Moralis.Cloud.define("getUserItems", async (request) => {

  	const options = { address: request.params.token_address, chain: "ropsten" };
  	const NFTowners = await Moralis.Web3API.token.getNFTOwners(options);
  	const queryResults = NFTowners.result.filter(x => x.owner_of == request.user.attributes.accounts);
    const results = [];
 	for (let i = 0; i < queryResults.length; ++i) {
      results.push({
        "tokenObjectId": queryResults[i].id,
        "tokenid": queryResults[i].token_id,
        "tokenAddress": queryResults[i].token_address,
        "symbol": queryResults[i].symbol,
        "tokenUri": queryResults[i].token_uri,
      });
    }
    return results;
  });

Moralis.Cloud.beforeSave("ItemsForSale", async (request) => {
  
    const query = new Moralis.Query("EthNFTTransfers");
  	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);

      if (request.user){
     query.notContainedIn("token.owner_of", request.user.attributes.accounts);
   }
    query.select("uid", "tokenAddress", "tokenId", "askingPrice", "token.token_uri", "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.uid,
        "tokenId": queryResults[i].attributes.tokenId,
        "tokenAddress": queryResults[i].attributes.tokenAddress,
        "askingPrice": queryResults[i].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": queryResults[i].attributes.user.username,
        "sellerAvatar": queryResults[i].attributes.user.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,
      };
  });

Hey guys
I’ve managed to get rid of these errors but I now have the 404 not found and json error for my getAndRenderItem function. This feels like a round about but its very good learning
Cheers :slight_smile:

Here are some of the repsonses for some of the undefined in networks.

You need to look at what you’re trying to fetch in fetch(item.tokenUri). It seems there is no tokenUri value in item.

Hey Glad
when I console.log(item) I get the tokenUri in the console. I understand its not fetching the tokenUri correctly and its response comes back as a html. Could it be a path to something that’s local and json doesnt like that?

Im now getting a error for my getUserItems cloud function.

What is the getUserItems error?

Looks like you’re getting a tokenUri, are you logging this (item.tokenUri) within getAndRenderItemData? Check your results to make sure tokenUri exists in all the objects.

The getUserItems error isn’t there anymore. it just disappeared after I had lunch.
Yes I have token Uri’s in all objects.

How are you logging each item.tokenUri? Make sure it’s at the top of the function, not in the then - otherwise only items with valid token URIs that were fetched will be logged.

Hi Glad
I’ve been able to get this error fixed now.
I have some other issues I’m working through today. I made another post about it.
Thank you for your help.
Cheers :slight_smile: