Web3 backend and smart contract development for Python developers Musical NFTs part 13: Connecting MetaMask

ilija

ilija

Posted on October 29, 2023

Web3 backend and smart contract development for Python developers Musical NFTs part 13: Connecting MetaMask

In this part we will add ability for crypto users to connect his/her MetaMask and to purches new NFT with our MockUSDC tokens. But before that let's clean a bit our home.html because it started to look a bit messy. What we can do is to create two new templates for crypto buyers and credit card buyer and to loaded them into home.html when we check user payment method status. For this to happen we just need to repackage our home.html and to move code related to crypto buyer to newly created templates/cypto_user.html and then all HTML code related to credit card buyer to templates/credit_card_buyer.html. Then in second step we will use Django tag include to include this two newly created HTML files into our home.html. With this we will clean up cluttery and make our main home.html template a bit more readable.

Create cypto_user.html and credit_card_user.html files inside root templates folder.

cypto_user.html

<table class="table  table-hover">
    <thead class="table-dark">
      <tr>
        <th scope="col">Name</th>
        <th scope="col">Email</th>
        <th scope="col">Total no. NFTs</th>
        <th scope="col">Means of payment</th>
        <th scope="col">Buy NFT</th>
      </tr>
    </thead>
    <tbody>
      <tr>
          <td> {{ customer.first_name }} {{ customer.last_name }} </td>
          <td> {{ customer.email }} </td>
          <td> {{ customer.total_no_of_nfts }} </td>
          <td>
            <form action="" method="post">
            {%csrf_token%}
            {{orderForm.as_p}}
            <input type="submit" value="Save" class="btn btn-secondary">
        </form>
          </td>       
          <td>
            <form action="" method="post">
            {%csrf_token%}
            {{form.as_p}}
            <input type="submit" value="Save" class="btn btn-secondary">
            </form>
          </td>             

        </tr>
      </tbody>
    </table>      
    {% for card in metadata%}
    {% if forloop.counter0|divisibleby:3 %} <div class="row">{%  endif %}
      <div class="card m-5 p-2" style="width: 18rem;">
        {% load static %}
        <img src="{% static 'nft/'%}{{card.cover_file_name}}"  class="card-img-top" alt="..." width="50" height="200"/>
        <div class="card-body">
          <h5 class="card-title">{{card.name}}</h5>
          <br>
          <p class="card-text">{{card.description}}</p>
        </div>
      </div>
    {%  if forloop.counter|divisibleby:3 or forloop.last %}</div> {%  endif %}
    <br>
    {% endfor %}
Enter fullscreen mode Exit fullscreen mode

And then in credit_card_user.html

<table class="table  table-hover">
    <thead class="table-dark">
      <tr>
        <th scope="col">Name</th>
        <th scope="col">Email</th>
        <th scope="col">Total no. NFTs</th>
        <th scope="col">Means of payment</th>
      </tr>
    </thead>
    <tbody>
      <tr>
          <td> {{ customer.first_name }} {{ customer.last_name }} </td>
          <td> {{ customer.email }} </td>
          <td> {{ customer.total_no_of_nfts }} </td>
          <td><form action="" method="post">
            {%csrf_token%}
            {{orderForm.as_p}}
            <input type="submit" value="Save" class="btn btn-secondary">
        </form>
          </td>         
        </tr>
      </tbody>
    </table>        
    {% for card in metadata%}
    {% if forloop.counter0|divisibleby:3 %} <div class="row">{%  endif %}
      <div class="card m-5 p-2" style="width: 18rem;">
        {% load static %}
        <img src="{% static 'nft/'%}{{card.cover_file_name}}"  class="card-img-top" alt="..." width="50" height="200"/>
        <div class="card-body">
          <h5 class="card-title">{{card.name}}</h5>
          <br>
          <p class="card-text">{{card.description}}</p>
        </div>
      </div>
    {%  if forloop.counter|divisibleby:3 or forloop.last %}</div> {%  endif %}
    <br>
    {% endfor %}
    <p> credit card </p>

Enter fullscreen mode Exit fullscreen mode

Now what we need to do is to include back into our home.html this two new files via Django template language tag include. Syntax should go something like this
{% include "crypto_user.html" %}

And now our home.html should look a bit shorter and cleaner

{% extends "base.html" %}
    {% block content%}

    {% if user.is_authenticated %}
        {% if customer.type == "CRYPTO"%}
        {% include "crypto_user.html" %}    

        {% else %}
        {% include "credit_card_user.html" %}    

        {% endif %}
        {% else %}
        <div class="col-md-6 offset-md-3"> 
        <h1> Login </h1>
        <br/>
        <form method="POST" action="{% url 'home' %}"> 
            {% csrf_token %}        
                <div class="mb-3">
                <input type="text" class="form-control" aria-describedby="emailHelp" placeholder="Username" name="username" required>
                </div>
                <div class="mb-3">
                <input type="password" class="form-control" placeholder="Password" name="password" required>
                </div>
                <button type="submit" class="btn btn-secondary">Login</button>
            </form> 
        </div>
    {% endif %}
    {% endblock content%}
Enter fullscreen mode Exit fullscreen mode

Now all things should work exactly the same only thing is that we have more readeble main html document.

Let's move to web3 part. For now our aim is to integrate MetaMask (you can easlly experiment with WalletConnect if you want alternative version of this code). This will allow crypto user to purches new NFT by using MetaMask. For this to happen we will need to use some JavaScript (in next iteration of this app, when we start to use React for our frontend we will integrate WalletConnect instead of MetaMask).

Ones crypto user login into his profile he will see two new buttons: connect and buy NFT. He first need to press connect and MetaMask will pop-up asking him to provide his credentials. Ones he finish login into MetaMask he will be able to use buy NFT button and to pass number of NFTs he would like to buy.

Step 1: In your static folder create new connect_wallet.js file. And inside that file pass following code (Please check if static parameters are set correctly inside your settings.py It should look something like this =>

  STATIC_URL = "/static/"
    STATICFILES_DIRS = (
        os.path.join(BASE_DIR, 'static'),
    )
    STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
Enter fullscreen mode Exit fullscreen mode

JavaScript file need to handle connection to MetaMask as well as to update list items to connected or connect to metamask status how they change. For this achive we will need few more functions.

 //function used to connect to MetaMask wallet
    connect = async () => {
        const address = await ethereum
        .request({
            method: 'eth_requestAccounts',
            params: [],
        })
        .then((res) => console.log('request accounts', res))
        .catch((e) => console.log('request accounts ERR', e));
        document_test = document.getElementById("connect");
        document_test.innerHTML = "Connected"  
    }   

    // Simple function used to manipulate HTML elements according to connected or user disconnencted status
    checkMetaMaskState = async () => {
        const account = await window.ethereum.request({method: 'eth_accounts'})
        const liElement = document.createElement("li");
        liElement.setAttribute("class", "nav-item")
        const aElement = document.createElement("a");
        aElement.setAttribute("class", "nav-link p-3")
        if (typeof account[0] == "undefined") {
            aElement.setAttribute("href", "javascript:connect()")
            aElement.setAttribute("id", "link")
            liElement.setAttribute("id", "connect")
            liElement.innerHTML = "Connect to MetaMask"
            aElement.appendChild(liElement);
        document.getElementById("logout").appendChild(aElement);
    } else if (account[0].includes("0x")) {
        aElement.setAttribute("id", "link")
        liElement.setAttribute("id", "connect")
        liElement.innerHTML = "Connected"
        aElement.appendChild(liElement);
        document.getElementById("logout").appendChild(aElement);
    } else {
        console.log("There is some problem with MetaMask")        
    }
    }

    // Detect change in connect or dissconcet wallet status and updated front-end accordingly
    window.ethereum.on('accountsChanged', async () => {
    let ilElement = document.getElementById("connect")
    const account = await window.ethereum.request({method: 'eth_accounts'})
    if (typeof account[0] == "undefined") {
        ilElement.innerHTML = "Connect to MetaMask"
    } else if (account[0].includes("0x")) {
        ilElement.innerHTML = "Connected"
    }
    });
Enter fullscreen mode Exit fullscreen mode

Then we need to add onload in body element of our base.html. And then to give checkMetaMaskState JS funciton as value. This function is used to generate new HTML document according to MetaMask connect/disconnect status.

Now base.html should look something like this (bascially the same as beafore just with <body onload="checkMetaMaskState()"> added)

{% load static %}
    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Muscial NFT</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
        <script type="text/javascript" src="{% static 'connect_wallet.js' %}"></script>

    </head>
    <body onload="checkMetaMaskState()">
        {% include "navbar.html"%}
        <div class="container ">       
            <br/>
            <br/>
            {% if messages %}
            {% for message in messages%}
            <div class="alert alert-warning alert-dismissible fade show" role="alert">
            {{ message }}
            <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
            </div>

            {% endfor%}
            {% endif %}

            {% block content %}
            {% endblock content %}
        </div>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
    </body>
    </html>

Enter fullscreen mode Exit fullscreen mode

Then in second step we will erase one list item element from our navbar.html template (this place will be populated directly from javascript level according to MetaMask wallet status).

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
      <div class="container-fluid">
        <a class="navbar-brand" href="{% url 'home' %}">Musical NFT </a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
          <ul class="navbar-nav me-auto mb-2 mb-lg-0" id="logout">
            {% if user.is_authenticated%}
              {% if customer.type == "CRYPTO"%}
              {% load static %}
              <script type="text/javascript" src="{% static 'connect_wallet.js' %}"></script>
                <li class="nav-item"  >
                  <a class="nav-link p-3" href="{% url 'logout'%}"  >Logout</a>
                </li>
                {% else %}
                <li class="nav-item">
                  <a class="nav-link" href="{% url 'logout'%}">Logout</a>
                </li>

                {% endif %}

          {% else %}
          <li class="nav-item">
            <a class="nav-link" href="{% url 'register'%}">Register</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="{% url 'home'%}">Login</a>
          </li>
          {% endif%}
              </ul>
            </li>
          </ul>
        </div>
      </div>
    </nav>
Enter fullscreen mode Exit fullscreen mode

If everything went well ones you login Denis user into his account you should be able to see something like this.

Image description

Code can be found in this github repo

💖 💪 🙅 🚩
ilija
ilija

Posted on October 29, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related