MetaMask init login auth problem - creating NFT dashboard tutorial

Hi all, Iā€™ve been following crypto and Ivan for years (miss those gogogogogo good morning crypto streams) and hope to meet Ivan in person sometime. I enrolled in the academy and am trying to put together a front-end and back-end dashboard now which would allow for showing NFTs per address, have the fiat on-ramp plugin, play around with some CSS then later on, etc.

I am stuck at a crucial first step though, which is getting MetaMask to initialize and authenticateā€¦ first I was getting an error in the console about web3 being deprecated and to use .ethereum instead, so I put in some new code, and now i am getting a mish-mash, could someone help me set it up properly?
I want to have a front page with a sign-in button that would authenticate via metamask, which would then (if properly authenticated via MetaMask) would reveal ther user more options - to see the nft-dashboard - to access a fiat on ramp etc. could someone please help me with this?

  • a question that comes to mind too (sorry if too basic) but does it matter if the main.js file has the .js extension or .json?

Here are HTML and JS (CSS is empty for now)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=yes">

    <title>Dan's Dashboard</title>
    <script src="https://unpkg.com/@metamask/legacy-web3@latest/dist/metamask.web3.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script src="https://unpkg.com/moralis/dist/moralis.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <link rel="stylesheet" href="./style.css">
  </head>
  <body>
    <div class="jumbotron jumbotron-fluid">
      <div class="container">
        <h1 class="display-4">Welcome to Dan's NFT Dashboard!!! :)</h1>
      </div>
    </div>
    <div class="container">
      <div>
        <button id="btnConnect">Connect Wallet</button>
        <button id="btnUserInfo">Profile</button>
    </div>
      <button class="enableEthereumButton" onclick="login()">Login with Metamask</button>
      <h2>Account: <span class="showAccount"></span></h2>


        <div class="row" id="app"></div>


    <script type="text/javascript" src="./main.js"></script>
  </body>
</html>

main.js

Moralis.initialize("aYYuFkYRdM5AeqaIxxdH0Ir5qRVjAqk0GsYoNsci"); // Application id from moralis.io
Moralis.serverURL = "https://nxg5nmdlyysy.moralishost.com:2053/server"; //Server url from moralis.io
const CONTRACT_ADDRESS = "0x5eaf190b963a4b3f67005c573761392a5186b52a"

init = async () => {
        window.web3 = await Moralis.Web3.enable();
        initUser();
}

login = async () => {
    if (await Moralis.User.current()){
        hideElement(userConnectButton);
        showElement(userProfileButton);
    }else{
        showElement(userConnectButton);
        hideElement(userProfileButton);
    }

    } catch (error) {
        alert(error)
    }


initUser = async () => {
    if (await Moralis.User.current()){
        hideElement(userConnectButton);
        showElement(userProfileButton);
    }else{
        showElement(userConnectButton);
        hideElement(userProfileButton);
    }
}

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

const userConnectButton = document.getElementById("btnConnect");
userConnectButton.onclick = login;

const userProfileButton = document.getElementById("btnUserInfo");

init();

document.getElementById("login_button").onclick = login;

const ethereumButton = document.querySelector('.enableEthereumButton');
const showAccount = document.querySelector('.showAccount');

async function login(){
    console.log("login clicked");
    var user = await Moralis.Web3.authenticate();
    if(user){
        console.log(user);
        user.save();
    }
}
ethereumButton.addEventListener('click', () => {
    getAccount();
});

async function getAccount() {
    const accounts = await ethereum.request({ method: 'eth_requestAccounts' });
    const account = accounts[0];
    showAccount.innerHTML = account;
}



function fetchNFTMetadata(NFTs){
    let promises = [];
	for (let i = 0; i < NFTs.length; i++) { let nft = NFTs[i]; let id = nft.token_id;
        let nft = NFTs[i];
        let id = nft.token_id;
        //Call Moralis Cloud function -> Static JSON file
        promises.push(fetch("https://nxg5nmdlyysy.moralishost.com:2053/server/functions/getNFT?_ApplicationId=aYYuFkYRdM5AeqaIxxdH0Ir5qRVjAqk0GsYoNsci=" + id)
        .then(res => res.json())
        .then(res => JSON.parse(res.result))
        .then(res => {nft.metadata = res})
        .then(res => {
            const options = { address: "", token_id: id, chain: "rinkeby" };        
            return Moralis.Web3API.token.getTokenIdOwners(options)
        })
        .then( (res) => {console.log(res)
            nft.owners = [];
            res.result.forEach(element => {
                nft.owners.push(element.ownerOf);
            });
            return nft;
        }))
        
    }
    return Promise.all(promises);
}

function renderInventory(NFTs){
    const parent = document.getElementById("app");
    for (let i = 0; i < NFTs.length; i++) {
        const nft = NFTs[i];
        let htmlString = `
        <div class="app">
            <img class="card-img-top" src="${nft.metadata.image}" alt="Card image cap">
            <div class="card-body">
                <h5 class="card-title">${nft.metadata.name}</h5>
                <p class="card-text">${nft.metadata.description}.</p>
                <p class="card-text">Number of owners: ${nft.owners.length}</p>
                <a href="#" class="btn btn-primary">Go somewhere</a>
            </div>
        </div>
        `
        let col = document.createElement("div");
        col.classname = "col col-md-4"
        col.innerHTML = htmlString;
        parent.appendChild(col);
    }
    }

    async function initializeApp(){
        let currentUser = Moralis.User.current();
        if(!currentUser){
            currentUser = await Moralis.Web3.authenticate();
        }
        alert("User is signed in");
        const options = { address: CONTRACT_ADDRESS, chain: "rinkeby" };
        let NFTs = await Moralis.Web3API.token.getAllTokenIds(options);
        renderInventory(NFTWithMetadata);
    }


cloud function on admin.moralis.io is as follows:

Moralis.Cloud.define("getNFT", async (request) => {
	const logger = Moralis.Cloud.getLogger();
    
	let NFTId = request.params.nftId;
	let hexId = parselnt(NFTId).toString(16);
	let paddedHex = ("0000000000000000000000000000000000000000000000000000000000000000" + hexId).slice(-64) 
    logger.info(paddedHex);
	return Moralis.Cloud.httpRequest({url: "https://nxg5nmdlyysy.moralishost.com/" + paddedHex + ".json"})
	.then(function(httpResponse){
          return httpResponse.text;
          })
  
})
  • any tips on how to make it work and how to continue with my goal? am I wrong to assume that with MetaMask updates - some of the tutorials are slightly out of date now? I used solidity to get my test NFTs onto OpenSea rinkeby testnet, that works fineā€¦

  • also i have some confusion when I deployā€¦ the site itself is:
    https://nxg5nmdlyysy.moralishost.com
    so should i use this or https://nxg5nmdlyysy.moralishost.com:2053/server

  • eventually have a generative NFT project there too and 1inch integration

  • lastly, how would I go about making this project (when complete) work on a unstoppable domain hosting of my own that I have? have a redirect there, or there a neater way of going about it?

Thanks to whoever is willing to lend a guiding hand to set me straight!

p.s.: sorry I had to split this into two posts and replace ā€œhttpsā€ with ā€œLINKā€ instead, otherwise, forum rules are limiting me to ā€œnew users are only allowed to post two links per postā€ and I canā€™t post at all

p.s. 2: lol by posting this I can now post multiple links, so I replaced it back with originals, no more LINK instead of https for the sake of clarity

This is a simple example of login logout interface:

<!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>Login MetaMask</title>
</head>
<body>
    <div>
        <button id="login_button">Login with MetaMask</button>
        <button id="logout_button">Logout</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>
        Moralis.initialize("APP_ID");
        Moralis.serverURL = "SERVER_URL";

main = async () => {
    window.web3 = await Moralis.enable();
    user = await Moralis.User.current();
    if (!user) {
        document.getElementById("login_button").style.display = "block";
        document.getElementById("logout_button").style.display = "none";

    } else {
        document.getElementById("login_button").style.display = "none";
        document.getElementById("logout_button").style.display = "block";
    }
}


async function login() {
    try {
        user = await Moralis.User.current();
        if (!user) {
            user = await Moralis.authenticate();
        }
        document.getElementById("login_button").style.display = "none";
        document.getElementById("logout_button").style.display = "block";
        console.log("login done");

    } catch (error) {
        console.log(error);
    }
}

async function logout() {
    try {
        await Moralis.User.logOut();
        console.log("logout done");
        await main();
    } catch (error) {
        console.log(error);
    }
}

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

document.getElementById("login_button").onclick = login;
document.getElementById("logout_button").onclick = logout;


main();    
    </script>
</body>
</html>
1 Like

ok so I used your code on a basic site and it worked seemingly wellā€¦

then I tried making the site (the same as in my original post) look a bit nicer, as well as have the login logout functionality and now itā€™s not working againā€¦ would you or anyone happen to be able to point me in the right direction on how to get it to work?

hereā€™s the link to it again:
https://nxg5nmdlyysy.moralishost.com/

you have to share html + js code
if you open browser console youā€™ll see some errors like:

uncaught SyntaxError: Identifier ā€˜nftā€™ has already been declared
(index):88 Uncaught TypeError: Cannot set properties of null (setting ā€˜onclickā€™)
at (index):88

I dug into the code and got it to somewhat work. But itā€™s somewhat messy.
Do you have any pointers for me on how to get my HTML/CSS to work easily a simply? I used Adobe Dreamweaver somewhat (back in my days when I did simple pages there was Microsoft FrontPage lol - which had many faults on its own).

I am trying to strike a balance between not spending hours/days/weeks on HTML/CSS, and ideally use a good WISYWG and learn by doing, then start tacking JS and moralis on to it. But would love to get your thoughts on how I could go about getting going.

My goal is to have a neat dashboard, which has metamask functionality through JS, and then add a page for an NFT gallery, FIAT on-ramp, generative NFTS then when I get further along. Thanks for your input. Really appreciate your thoughts.

Iā€™m used to use simple HTML and vanilla javascript, it seems easy to me but there is an initial effort to learn javascript.