Rarible Clone - tokenContract not defined

Hi Folks,

I’ve worked through the tute, cloned the code from git but continue to have 2x errors that I haven’t been able to solve. Appreciate any tips/suggesstions.

I believe my cloud_functions are accurate, I made the change to EthNFTOwners

Error from console -

main.js:182 Uncaught (in promise) ReferenceError: tokenContract is not defined
    at mintNft (main.js:182:21)
    at HTMLButtonElement.createItem (main.js:163:25)

main.js

/* Moralis init code */

const serverUrl = “https://ct6uqqk9xhwm.usemoralis.com:2053/server”;

const appId = “EAW2GL3JFdQE8eZGVTF4pZofH6kO4LukuJDh4CKs”;

const TOKEN_CONTRACT_ADDRESS = “0x5725e2635606a8B956db0C7F563c92178cFD0D0F”;

const MARKETPLACE_CONTRACT_ADDRESS = “0x406DE3e82BB1758574E3639ec30952aC60a79782”;

Moralis.start({ serverUrl, appId });

init = async () => {

hideElement(userItemsSection);

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

    }

    $('#userInfo').modal('show');

}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 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 nftId = await mintNft(nftFileMetadataFilePath);

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

    $('#userItems').modal('show');

}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 = () => $(’#createItem’).modal(‘show’);

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

index.html

<meta charset="UTF-8">

<meta http-equiv="X-UA-Compatible" content="IE=edge">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">

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

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

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

    <button class="navbar-toggler" type="button" data-toggle="collapse" data-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 class="row row-cols-1 row-cols-md-4 mt-5" id="itemsForSale">

    </div>

</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 class="col mb-4" id="itemTemplate">

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

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

                <div class="input-group mb-3">

                    <input type="number" min="1" step="1" class="form-control" placeholder="Price">

                    <div class="input-group-append">

                      <button class="btn btn-outline-secondary" type="button">Put for sale</button>

                    </div>

                  </div>

            </div>

   

        </div>

      </div>

</div>

<!-- Modal -->

<div class="modal fade" id="userInfo" tabindex="-1">

    <div class="modal-dialog">

    <div class="modal-content bg-dark text-light">

        <div class="modal-header">

        <h5 class="modal-title" >User Profile</h5>

        <button type="button" class="close" data-dismiss="modal" aria-label="Close">

            <span aria-hidden="true">&times;</span>

        </button>

        </div>

        <div class="modal-body">

            <div class="form-group">

                <label for="txtUsername">Username</label>

                <input type="text" class="form-control" id="txtUsername" required placeholder="Enter username">

            </div>

            <div class="form-group">

                <label for="txtEmail">Email address</label>

                <input type="email" class="form-control" id="txtEmail" aria-describedby="emailHelp" placeholder="Enter email">

                <small id="emailHelp" class="form-text text-muted">Optional</small>

            </div>

   

            <img width="50" height="50" src="" id="imgAvatar" alt="">

            <div class="form-group">

                <label for="fileAvatar">Select Avatar</label>

                <input type="file" class="form-control-file" id="fileAvatar">

            </div>

        </div>

        <div class="modal-footer">

        <button type="button" id="btnLogout" class="btn btn-secondary" data-dismiss="modal">Log out</button>

        <button type="button" id="btnCloseUserInfo" class="btn btn-secondary" data-dismiss="modal">Close</button>

        <button type="button" id="btnSaveUserInfo" class="btn btn-primary">Save</button>

        </div>

    </div>

    </div>

</div>

    <!-- Modal -->

    <div class="modal fade" id="createItem" tabindex="-1">

        <div class="modal-dialog">

        <div class="modal-content bg-dark text-light">

            <div class="modal-header">

            <h5 class="modal-title" >Create Item</h5>

            <button type="button" class="close" data-dismiss="modal" aria-label="Close">

                <span aria-hidden="true">&times;</span>

            </button>

            </div>

            <div class="modal-body">



                <div class="form-group">

                    <label for="txtUsername">Name</label>

                    <input type="text" class="form-control" id="txtCreateItemName" required placeholder="Enter name">

                </div>



                <div class="form-group">

                    <label for="txtCreateItemDescription">Description</label>

                    <textarea class="form-control" id="txtCreateItemDescription"  cols="30" rows="5" placeholder="Enter description"></textarea>

                </div>

                <div class="form-group">

                    <label for="txtUsername">Price</label>

                    <input type="number" min="1" step="1" id="numCreateItemPrice" placeholder="Enter price" required class="form-control">

                </div>

                <div class="form-group">

                    <label for="selectCreateItemStatus">Status</label>

                    <select class="form-control" id="selectCreateItemStatus">        

                        <option value="0">Not for sale</option>

                        <option value="1">Instant buy</option>

                        <option value="2">Accept Offers</option>

                    </select>

                </div>

                <div class="form-group">

                    <label for="fileCreateItemFile">Select file</label>

                    <input type="file" class="form-control-file" id="fileCreateItemFile">

                </div>

            </div>

            <div class="modal-footer">

            <button type="button" id="btnCloseCreateItem" class="btn btn-secondary" data-dismiss="modal">Close</button>

            <button type="button" id="btnCreateItem" class="btn btn-primary">Create!</button>

            </div>

        </div>

        </div>

    </div>

   

    <!-- Modal -->

    <div class="modal fade" id="userItems" tabindex="-1">

        <div class="modal-dialog modal-xl">

        <div class="modal-content bg-dark text-light">

            <div class="modal-header">

            <h5 class="modal-title" >My Items</h5>

            <button type="button" class="close" data-dismiss="modal" aria-label="Close">

                <span aria-hidden="true">&times;</span>

            </button>

            </div>

            <div class="modal-body row row-cols-1 row-cols-md-4 mt-5" id="userItemsList">



            </div>

            <div class="modal-footer">

                <button type="button" id="btnCloseUserItems" class="btn btn-secondary" data-dismiss="modal">Close</button>

            </div>

        </div>

        </div>

    </div>

<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<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>

Token.sol

pragma solidity ^0.8.0;

import “…/node_modules/@openzeppelin/contracts/token/ERC721/ERC721.sol”;

import “…/node_modules/@openzeppelin/contracts/utils/Counters.sol”;

contract ReReToken is ERC721 {

using Counters for Counters.Counter;

Counters.Counter private _tokenIds;

constructor () ERC721("ReReToken", "RERE"){}

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;

}

}

Thanks!!!

Still searching for the answer.