[SOLVED] Build Your Own Web 3.0 Metaverse using Solidity, Javascript and Moralis - Blockchain Development

Hello,

having some issues with test prooject based on “Build Your Own Web 3.0 Metaverse using Solidity, Javascript and Moralis - Blockchain Development” from Yoiutube tutorial. Map never showing up.

Logic.js

//Initializing constants
const serverUrl = "xxxxx";
const appId = "xxxxx";

//Graphics constants
// - map management constants
const tiles = 624;
const plots = tiles * 1;
const roads = tiles * 1;
const initialOffsets = plots +roads;
const plotViewOffsets = plots + (2 * roads);

//- canvas and context
const mainCanvas = document.getElementById("mainCanvas");
const mainCtx = mainCanvas.getContext('2d');
const plotCanvas = document.getElementById("plotCanvas");
const plotCtx = plotCanvas.getContext('2d');
const worldImage = new Image();

//- State
const mapView = {mapOffsetX:-1*initialOffsets,mapOffsetY:-1*initialOffsets};
const plotView = {plotX:0, plotY:0, locationX:0,locationY:0};
const unassignables =  [ 
                        "0x8fa5a0f171f8b06a2006bc8fa5164606fa303f29b43f25438ae585d32f979524",
                        "0x51335e5c0d362b12bbc4877297443f914f0f9849da3bb43485ce6d96fcc6f9c9",
                        "0xbb15fe082bb44a75feb18fcb4072d7e1d817f016d24cc1b1205c4f729e0b69ca",
                        "0x5e2da10292384f87396bd6e706838dcb931f91404f3f01e4ef9538f3e161262e",
                        "0x760a8a1da896267c45023b0d1c17a5bc42b81d12ab49a4bf1ccfec74c1ebe8ae",
                        "0xad2572137374014c87d7abe24a591d3d65aee5cdc781ae15c727a971c5637aa7",
                        "0xc2c26eb9355dec9ceae980da9bb626c7b583fe9af9c779333ea6e19309c2f3a6",
                        "0xbf8142b3eb80adcbdb8a1c1a146995728a859ab1a1dbf29dcf15ebeb32f4a468",
                        "0x3c02bccec034096f69ebbaacc075adbf352d2309a6de6b632d7b18b10b60180c",
                        "0xfaf95d6bec5226ee145a021be8bda12c1062f7817ba6340499023b26d7e7a3f0",
                        "0x98ce6651c1676f0d20fc0553d96755f2162f97c874ec668be5ebc68c3b2b7b41"
                        ]

//web3 constants
const contractAddress = ""; //your own contract
const contractABI = [{"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"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"tokenURI","type":"string"},{"internalType":"bytes","name":"bytesId","type":"bytes"}],"name":"assign","outputs":[],"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"assignee","type":"address"},{"indexed":false,"internalType":"bytes","name":"bytesId","type":"bytes"}],"name":"Assigned","type":"event"},{"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"},{"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":"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":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"bytesId","type":"bytes"}],"name":"exist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"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"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}];
const ethers = Moralis.web3Library;

//canvas drawing functions
function drawCanvas(){
    mainCanvas.width = 3 * plots + 4 * roads;
    mainCanvas.height = 3 * plots + 4 * roads;
    plotCanvas.width = plots
    plotCanvas.height = plots
    worldImage.src = 'static/img/Rainbowland.png';
    worldImage.onload = () => {
        initializeMap()
    }
}

function initializeMap() {
    updatePlotLocation()
    setPlotData();
    drawMapSection(mainCtx,mapView.mapOffsetX,mapView.mapOffsetY);
    drawCursor(plotViewOffsets,plotViewOffsets);
    drawMapSection(plotCtx,-1 * plotView.locationX,-1 *plotView.locationY);
}

//animate functions
function move(direction) {
    const validMove = validateMove(direction);
    if (validMove) {
        updateView(direction);
        updatePlotLocation();
        drawMapSection(mainCtx,mapView.mapOffsetX,mapView.mapOffsetY);
        drawCursor(plotViewOffsets,plotViewOffsets);
        drawMapSection(plotCtx,-1 * plotView.locationX,-1 *plotView.locationY);
        setPlotData();
    }
}

function validateMove(direction) {
    switch(direction){
        case 'ArrowRight': return !(plotView.plotX == 5);
        case 'ArrowUp': return   !(plotView.plotY == 0);
        case 'ArrowLeft': return !(plotView.plotX == 0);
        case 'ArrowDown': return !(plotView.plotY == 5);
    }
}

function updateView(direction) {
    switch(direction){
        case 'ArrowRight': 
            plotView.plotX += 1;
            mapView.mapOffsetX -= plots + roads;
            break
        case 'ArrowDown': 
            plotView.plotY += 1;
            mapView.mapOffsetY -= plots + roads;
            break
        case 'ArrowLeft': 
            plotView.plotX -= 1;
            mapView.mapOffsetX += plots + roads;
            break
        case 'ArrowUp': 
            plotView.plotY -= 1;
            mapView.mapOffsetY += plots + roads;
            break
    }
}

function drawMapSection(ctx,originX,originY){
    ctx.drawImage(worldImage,originX,originY);
}

function drawCursor(x,y) {
    mainCtx.strokeRect(x,y,plots,plots)
}

function updatePlotLocation() {
    plotView.locationX = -1 * mapView.mapOffsetX + plotViewOffsets;
    plotView.locationY = -1 * mapView.mapOffsetY + plotViewOffsets;
}

//UI Functions
function setPlotData() {
    const plotID = ethers.utils.id(JSON.stringify(plotView));
    document.getElementById("plotX").value = plotView.plotX
    document.getElementById("plotY").value = plotView.plotY
    document.getElementById("locationX").value = plotView.locationX
    document.getElementById("locationY").value = plotView.locationY
    document.getElementById("plotID").value = plotID;
    isPlotAssignable(plotID);
}

function isPlotAssignable(plotID) {
    const _unassignable = unassignables.includes(plotID);
    if (_unassignable) {
        document.getElementById("claimButton").setAttribute("disabled",null);
    } 
    else {
        document.getElementById("claimButton").removeAttribute("disabled");
    }
} 

function displayMessage(messageType, message){
    const messages = {
                "00":`<div class= "alert alert-success"> ${message} </div>`,
                "01":`<div class= "alert alert-danger"> ${message} </div>`
            }
    document.getElementById("notifications").innerHTML = messages[messageType];
}

//web3 Functions
async function login(){
    Moralis.Web3.authenticate().then(async function (){
        const chainIdHex = await Moralis.switchNetwork("0x13881");
    });
}

async function assignPlot() {
    const plotID = document.getElementById("plotID").value;
    const assigned = await isPlotAssigned(plotID);
    if (!assigned) {
        const metadata = {
            "PlotID":plotID,
            "PlotX":document.getElementById("plotX").value,
            "PlotY":document.getElementById("plotY").value,
            "LocationX":document.getElementById("locationX").value,
            "LocationY":document.getElementById("locationX").value,
            "image":"https://moralis.io/wp-content/uploads/2021/06/Moralis-Glass-Favicon.svg",
        }
        const metadataFile = new Moralis.File("metadata.json", {base64 : btoa(JSON.stringify(metadata))});
        await metadataFile.saveIPFS();
        const metadataURI = metadataFile.ipfs();
        await mint(metadataURI);
    }
    else{
        displayMessage("01","Plot is already assigned");
    }
}

async function mint(_tokenURI) {
    const contractOptions = {
        contractAddress: contractAddress,
        abi: contractABI,
        functionName: "assign",
        params: {
            tokenURI:_tokenURI,
            bytesId:document.getElementById("plotID").value
        }
    }
    try{
        const transaction = await Moralis.executeFunction(contractOptions);
        await transaction.wait();
        displayMessage("00","Transaction confirmed with hash "+transaction.hash);
    }
    catch(error){
        displayMessage("01","Transaction reverted see console for details");
        console.log(error)
    }
}


async function isPlotAssigned(plotID) {
    const contractOptions = {
        contractAddress: contractAddress,
        abi: contractABI,
        functionName: "exist",
        params: {
            bytesId:plotID
        }
    }
    return await Moralis.executeFunction(contractOptions);
}


Moralis.start({ serverUrl, appId }); 
login();
drawCanvas();
window.addEventListener('keydown' , (e) => {
    move(e.key)
});


index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <!--Head Content-->
        <title>Land</title>
        <!-- Required meta tags -->
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <!-- Links -->
        <!-- Make It Pretty Import the Bootstrap stylesheet -->
        <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
    </head>
    <body>
        <main>
            <div class="container mt-5">
                <div class="row">
                    <h1 class="display-1 text-center text-success" > The Rainbowland</h1>
                </div>
                <div class="row">
                    <div class="col-lg-6">
                        <div class="text-center bg-success text-white">
                            <h1>Map</h1>
                        </div>
                        <div id="MainCanvasDiv" class="text-center">
                            <canvas id="mainCanvas"></canvas>
                        </div>
                    </div>
                    <div class="col-lg-6">
                        <div class="text-center bg-success text-white">
                            <h1>Current Plot</h1>
                        </div>
                        <div id="PlotCanvasDiv"class="text-center">
                            <canvas id="plotCanvas"></canvas>
                        </div>
                        <div class="input-group mb-2">
                            <span class="input-group-text">Plot ID</span>
                            <input  disabled="true" id="plotID" type="text" class="form-control">
                        </div>
                        <div class="input-group mb-2">
                            <span class="input-group-text">Plot Coordinates</span>
                            <input disabled="true" id="plotX" type="text" class="form-control">
                            <input disabled="true" id="plotY" type="text" class="form-control">
                        </div>
                        <div class="input-group mb-2">
                            <span class="input-group-text">Location Coordinates</span>
                            <input disabled="true" id="locationX" type="text" class="form-control">
                            <input disabled="true" id="locationY" type="text" class="form-control">
                        </div>
                        <div class="row">
                            <button class="btn btn-success btn-block btn-lg" id="claimButton" onclick="assignPlot();">Claim</button>
                        </div>
                    </div>
                </div>
                <hr>
                <div class="row"> 
                    <div class="text-center bg-success text-white">
                        <h1>Notifications</h1>
                    </div>
                    <div id="notifications" class="container mt-2">         
                    </div>
                </div>   
            </div>
        </main>
    <!-- Scripts -->
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script>
        <script src="https://unpkg.com/moralis/dist/moralis.js"></script>
        <script src="static/js/logic.js"></script>
    </body>
</html>

Will appreciate any suggestions. Thank you!

Do you get any errors (check your terminal and browser console)? Make sure your code more or less matches the final repo.

Inside your index.html, you will also need to use moralis-v1 (for Moralis servers) for the CDN import such as https://unpkg.com/[email protected]/dist/moralis.js - the one you’re using points to the 2.0 SDK.

2 Likes

5 Errors about Roguelike.tsx

<xml version="1.0" const encoding="UTF-8">
<tileset version="1.9" tiledversion="1.9.2" name="Roguelike" tilewidth="16" tileheight="16" tilecount="1767" columns="57">
 <image source="Roguelike.png" width="968" height="526"/>
</tileset>

  1. JSX element ‘xml’ has no corresponding closing tag.
  2. Cannot find name ‘React’.
  3. Cannot find name ‘React’.
  4. Cannot find name ‘React’.
  5. ‘</’ expected.

Do you get any errors (check your terminal and browser console)? Make sure your code more or less matches the final repo

I tried with exactly same code from final repo but same with map, no map, just like in screenshot

Inside your index.html, you will also need to use moralis-v1 (for Moralis servers) for the CDN import such as https://unpkg.com/[email protected]/dist/moralis.js - the one you’re using points to the 2.0 SDK.

It helped, now i have map, but still have this errors in Roguelike.tsx

And i need to figure out with tiles. Thank you @glad

I think those errors from your code editor are fine because this file is not used in a React app/context. But try changing it to match the format of the original pack e.g. use <?xml version="1.0" encoding="UTF-8"?>. And make sure the settings are correct e.g. Roguelike.png is a valid file.

A clone of the repo seems to work on my side (at least up until transaction) after changing the CDN link and adding my own server URL / appId in the script.

1 Like

It helped, now i have map

Inside your index.html, you will also need to use moralis-v1 (for Moralis servers) for the CDN import such as https://unpkg.com/[email protected]/dist/moralis.js - the one you’re using points to the 2.0 SDK.

Thank you @alex

Will “CLAIM” button be active, if i not provided this one?
Contract is deployed:
Smart contract, deployed

const serverUrl = "https://akp4cqh1pt2v.usemoralis.com:2053/server";
const appId = "KqSzcSiqtOtJfbWZbI7m4O3fBuuzZ666UfbUKB65656565656508u4T";

And if not, and i must have it. What i need to provide? I have Moralis API Key and made Playfab, Azure.

It will be my Moralis API key, right?

const appId = "KqSzcSiqtOtJfbWZbI7m4O3fBuuzZ666UfbUKB65656565656508u4T";

What to put here, from Playfab?

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

Thank you!

No, appId and serverUrl are different, these values are meant to come from your Moralis server. If you don’t already have one (from when Moralis offered hosted servers for new users), you will need to self-host.