Create Publication

We are looking for publications that demonstrate building dApps or smart contracts!
See the full list of Gitcoin bounties that are eligible for rewards.

Tutorial Thumbnail
Beginner · 30 minutes

AlgoMinter - a Web App for Minting Assets Using Python, AlgoSigner, and Anvil Platform

This tutorial is mainly aimed at developers with beginner python and web development experience who are starting their journey into the Algorand development ecosystem, want to build prototypes that are easy to host and maintain, and lastly need to enable essential dApp capabilities without hosting an maintaining an Algorand blockchain node by utilizing AlgoSigner and PureStake APIs.

What is Anvil?

Anvil is essentially an online web-app building service that is especially perfect for rapid prototyping, handles the infrastructure configuration and hosting, and most importantly allows using pure python for both frontend and backend code of your web app. One can name a lot of use cases for when an ability to develop deploy and test an idea as a rapid prototype in python can be of value.

What is AlgoSigner?

AlgoSigner is a web browser wallet extension for Algorand-based applications on the web.

Requirements

Background

The steps described in this tutorial are generally very straightforward to follow, however, it is highly recommended to get familiar with Anvil Editor documentation. This will allow you to easily navigate in Anvil’s app-building web-based IDE. Optionally, you can also go over Anvil Quick Start guides to get a further grasp of how to interact with the IDE. However, simply getting familiar with Editor’s documentation should be sufficient to easily follow the steps in this tutorial.

Steps

1. Create a new Blank App

Assuming that you have signed up on the Anvil platform, once you click Start Building you should see a starting page that allows the creation of new blank web app projects.

EditorImages/2021/05/18 23:01/Screenshot_2021-05-19_at_0.57.04.pngFig 1. Start page on Anvil platform

Click on New Blank App and choose Material Design template. The reason for using that particular template is due to some pre-defined components that will make our UI slightly more customizable out of the box.

1.1 Set the app name to AlgoMinter

Let’s call our new sample app AlgoMinter since it will provide a simple user interface to mint new assets on Algorand.

EditorImages/2021/05/18 23:13/fig_1.1.pngFig 1.1 Invoking a panel to rename the app

As demonstrated on Fig 1.1, click on Rename and set the name for our app to AlgoMinter.

1.2 Setup a blank Anvil form

By default, you could see that since we selected a Material Design template, inside the Form builder (middle element in Anvil’s IDE) there are header and navigation panel components loaded up default. However, let’s keep our app plain and simple.

EditorImages/2021/05/18 23:17/fig_1.2.pngFig 1.2 Creating a new Anvil form in client code

As demonstrated in Fig 1.2, click on Add Form and then select Blank Panel. Once clicked, you will immediately see a new blank form loaded into the form editor. Rename the newly created Form2 to AlgoMinterMainForm and delete Form1 entirely. Lastly, click on the drop-down button on AlgoMinterMainForm under Client Code on the left side of the web page and select Set as startup Form. This will ensure that we always load into our new blank form when we run our web app.

2. Creating the UI using Drag and Drop builder

The next step is to define basic UI components.

As demonstrated on the video above, follow the video’s steps and make sure to create a form that will result in something like Fig 2.

EditorImages/2021/05/19 00:08/fig_2.pngFig 2. Assembled UI example

An important detail to follow is to name the components as follows:

TextBoxes
- asset_name_textbox
- unit_name_textbox
- total_units_textbox
- decimals_textbox
- note_textbox

DropDowns
- wallets_dropdown

Buttons
- mint_button
- connect_button

2.1 Setting up basic form’s logic

Essentially, we want our app to operate in 2 steps. Firstly all components below Connect Algosigner will be invisible. Secondly, after AlgoSigner is connected we want to hide the Connect Algosigner button and display rest of the components. As a last detail, we want the Mint button to be disabled by default unless all required fields are filled.

On each TextBox component in the form navigate to Container Properties in the right bottom corner and click on the blue arrows button next to a field called change (see Fig 2.1). Upon clicking on each you will see how Anvil redirects you to auto-generated event functions in the Code tab.

EditorImages/2021/05/19 00:24/fig_2.1.pngFig 2.1 Events panel that auto generates functions to be used on particular event trigger

Similarly to above, for each Button component perform a similar procedure but use the click field instead. This will generate event functions to be triggered when the user clicks on that particular button element.

Click on the Code tab in the Form builder and copy paste a snippet from below:

from ._anvil_designer import AlgoMinterMainFormTemplate
from anvil import *


class AlgoMinterMainForm(AlgoMinterMainFormTemplate):
    def __init__(self, **properties):
        # Set Form properties and Data Bindings.
        self.init_components(**properties)

        self.asset_name_textbox.visible = False
        self.unit_name_textbox.visible = False
        self.total_units_textbox.visible = False
        self.decimals_textbox.visible = False
        self.note_textbox.visible = False
        self.mint_button.visible = False
        self.wallets_dropdown.visible = False

    def _reload_mint_button_state(self):
        has_asset_name = self.asset_name_textbox.text
        has_unit_name = self.unit_name_textbox.text
        has_total_units = self.total_units_textbox.text
        has_decimals = self.decimals_textbox.text
        has_note = self.note_textbox.text

        self.mint_button.enabled = all(
            [has_asset_name, has_unit_name, has_total_units, has_decimals, has_note]
        )

    def connect_button_click(self, **event_args):
        """This method is called when the button is clicked"""

        # 1. Load wallets
        self.wallets_dropdown.items = []

        # 2. Enable form
        self.connect_button.visible = False
        self.asset_name_textbox.visible = True
        self.unit_name_textbox.visible = True
        self.total_units_textbox.visible = True
        self.decimals_textbox.visible = True
        self.note_textbox.visible = True
        self.mint_button.visible = True
        self.wallets_dropdown.visible = True

    def note_textbox_change(self, **event_args):
        """This method is called when the text in this text box is edited"""
        self._reload_mint_button_state()

    def decimals_textbox_change(self, **event_args):
        """This method is called when the text in this text box is edited"""
        self._reload_mint_button_state()

    def total_units_textbox_change(self, **event_args):
        """This method is called when the text in this text box is edited"""
        self._reload_mint_button_state()

    def unit_name_textbox_change(self, **event_args):
        """This method is called when the text in this text box is edited"""
        self._reload_mint_button_state()

    def asset_name_textbox_change(self, **event_args):
        """This method is called when the user presses Enter in this text box"""
        self._reload_mint_button_state()

    def mint_button_click(self, **event_args):
        """This method is called when the button is clicked"""

The snippet above defines the basic validation logic for our web app. Observe how we hide the components below the Connect Algosigner button in the __init__ method. In addition to that, observe the _reload_mint_button_state method that is triggered on each change in each TextBox and dynamically changes the state of the Mint button. This gives us essential validation needed for our app’s functionality. The example can always be expanded to handle various intricate details like extra input validation for each field, etc. However, for the sake of simplicity in this beginner tutorial, various tricky validation edge cases are not covered.

3. Integrating AlgoSigner

In this step, we are finally approaching the most interesting parts of this tutorial. We already know at this point that the entire development process in the Anvil platform is done primarily in python. But let’s be honest, what is the world’s most dominant language when it comes to anything web and web-development-related? It’s JavaScript!

Luckily for us, Anvil got us covered with an ability to integrate custom code snippets in JavaScript, which is exactly what we are going to use to integrate AlgoSigner. As mentioned on the official AlgoSigner dApp integration guide on GitHub, AlgoSigner injects a JavaScript library into every web page the browser user visits, which allows the site to interact with the extension. So in theory, regardless of the stack behind a web app running on a specific URL, when we open thatURL in a browser with the extension available if the AlgoSigner extension is installed we should be able to access it directly to invoke a command that it provides.

One important detail to note is we are going to use TestNet for our web app for demonstration purposes. You could always switch to the main net by simply renaming mentions of TestNet to MainNet in the snippets provided below.

3.1 Add AlgoSigner handler functions to Anvil’s Native Libraries

Navigate to Native Libraries in the App Browser and paste the following JavaScript snippet that defines all operations that our python code would need to execute to interact with AlgoSigner.

<script>

  function hasAlgo() {
      return (typeof AlgoSigner !== 'undefined');
  }

  function algoConnect(algo_node_type) {
      return AlgoSigner.connect({
          ledger: "TestNet"
      }).then((d) => {
          return d;
      })
  }

  function algoAccounts(algo_node_type) {
      return AlgoSigner.accounts({
          ledger: "TestNet"
      });
  }

  function algoTxParams(algo_node_type) {
      return AlgoSigner.algod({
          ledger: "TestNet",
          path: "/v2/transactions/params"
      });
  }

  function algoSignAssetTx(fromWallet, assetName, assetUnitName, assetTotal, assetDecimals, note, txParams) {
      return AlgoSigner.sign({
          from: fromWallet,
          assetName: assetName,
          assetUnitName: assetUnitName,
          assetTotal: assetTotal,
          assetDecimals: assetDecimals,
          note: note,
          type: 'acfg',
          fee: txParams['min-fee'],
          firstRound: txParams['last-round'],
          lastRound: txParams['last-round'] + 1000,
          genesisID: txParams['genesis-id'],
          genesisHash: txParams['genesis-hash'],
          flatFee: true
      })
  }

  function algoSendTx(signedTx, algo_node_type) {
      return AlgoSigner.send({
          ledger: "TestNet",
          tx: signedTx.blob
      });
  }

</script>

What this does is giving an ability to invoke custom JavaScript functions via Python code inside our python forms and modules on the Anvil platform. Methods themselves define basic operations to establish a connection, get recommended transaction parameters, get accounts attached to the wallet, sign an asset creation transaction and finally send a signed transaction. What happens on a side of AlgoSigner during invocation is mainly invocation of various REST APIs provided by PureStake. A simple web app like ours is a perfect use case for utilizing a third-party API like that, eliminating compute overhead and time spent on deploying your Algorand Blockchain node.

3.2 Writing a helper Python class to invoke JavaScript code

Now that we have our javascript code defined in Native Libraries, this means that we can now easily invoke those custom functions using a special helper mechanism that transpiles javascript code into wrappers invokable from python.

Click on a + sign in the top left corner under Client Code and select Add Module. Name the new module as AlgoSignerClient. This will be a simple python class with a set of static methods to be used by our AlgoMinerMainForm to perform communication with the user and invoke AlgoSigner to ask users to sign the asset creation transaction.

Cope the snippet below and replace the content of the module:

import anvil.js
from anvil.js.window import hasAlgo, algoConnect, algoAccounts, algoTxParams, algoSignAssetTx, algoSendTx

class AlgoSignerClient:

    @staticmethod
    def has_algo_signer():
        status = hasAlgo()
        return status

    @staticmethod
    def algo_signer_connect():
        connection = algoConnect()
        return connection

    @staticmethod
    def algo_signer_accounts():
        accounts = algoAccounts()

        wallets = []
        for item in accounts:
            wallets.append(item["address"])

        return wallets

    @staticmethod
    def algo_signer_tx_params():
        try:
            tx_params_response = algoTxParams()

            return (
                {
                    "consensus-version": tx_params_response["consensus-version"],
                    "fee": tx_params_response["fee"],
                    "min-fee": tx_params_response["min-fee"],
                    "last-round": tx_params_response["last-round"],
                    "genesis-id": tx_params_response["genesis-id"],
                    "genesis-hash": tx_params_response["genesis-hash"],
                },
                None,
            )
        except Exception as exp:
            return ({}, exp)

    @staticmethod
    def algo_signer_sign_asset_tx(
        from_wallet,
        asset_name,
        asset_unit_name,
        asset_total,
        asset_decimals,
        note,
        tx_params,
    ):
        try:
            signed_tx = anvil.js.call(
                algoSignAssetTx,
                from_wallet,
                asset_name,
                asset_unit_name,
                asset_total,
                asset_decimals,
                note,
                tx_params,
            )
            print(signed_tx)
            return ({"txID": signed_tx["txID"], "blob": signed_tx["blob"]}, None)
        except Exception as exp:
            return ({}, exp)

    @staticmethod
    def algo_signer_send_tx(signed_tx):
        try:
            send_tx = anvil.js.call(algoSendTx, signed_tx)

            return (
                {
                    "txID": send_tx["txId"],
                },
                None,
            )
        except Exception as exp:
            return ({}, exp)

3.3 Final tweaks in AlgoMinterMainForm

Import the following extra abstractions into AlgoMinterMainForm‘s code:

import math
from ..AlgoSignerClient import AlgoSignerClient
from time import sleep

Update the connect_button_click as follows:

def connect_button_click(self, **event_args):
    """This method is called when the button is clicked"""
    has_algosigner = AlgoSignerClient.has_algo_signer()

    if not has_algosigner:
        alert(
            "Make sure to use a browser that supports Algosigner extension!",
            title="Warning",
            dismissible=True,
        )
        return

    # 1. Connect algosigner
    AlgoSignerClient.algo_signer_connect()

    # 2. Load wallets
    self.wallets_dropdown.items = []
    self.wallets_dropdown.items = AlgoSignerClient.algo_signer_accounts()

    # 3. Enable form
    self.connect_button.visible = False
    self.asset_name_textbox.visible = True
    self.unit_name_textbox.visible = True
    self.total_units_textbox.visible = True
    self.decimals_textbox.visible = True
    self.note_textbox.visible = True
    self.mint_button.visible = True
    self.wallets_dropdown.visible = True

The snippet above is pretty straightforward. We can see that firstly we check if the user’s browser has an AlgoSigner and display a warning in case it does not. If it does, we load the wallets into our wallets_dropdown component, and the rest of the snippet is identical to the initial snippet from Step 2.1.

Lastly copy the snippet and update the mint_button_click method:

def mint_button_click(self, **event_args):
    """This method is called when the button is clicked"""
    asset_name = self.asset_name_textbox.text
    unit_name = self.unit_name_textbox.text
    total_units = int(self.total_units_textbox.text) * math.pow(10, 2) # needed because units defined in microalgos
    decimals = int(self.decimals_textbox.text)
    note = self.note_textbox.text
    wallet = self.wallets_dropdown.selected_value


    # 1. Get tx params
    tx_params, tx_params_error = AlgoSignerClient.algo_signer_tx_params()
    sleep(1)

    if tx_params_error:
        alert(
            f"Error occured in algosigner, please try again! {tx_params_error}",
            title="Error",
            dismissible=True,
        )
        return

    # 2. Sign transaction

    signed_tx, signed_tx_error = AlgoSignerClient.algo_signer_sign_asset_tx(
        wallet, asset_name, unit_name, total_units, decimals, note, tx_params
    )
    sleep(1)

    if signed_tx_error:
        print(signed_tx_error)
        alert(
            f"Error occured in algosigner, please try again! {signed_tx_error}",
            title="Error",
            dismissible=True,
        )
        return

    # 3. Send transaction

    send_tx_response, send_tx_response_error = AlgoSignerClient.algo_signer_send_tx(
        signed_tx
    )
    sleep(1)

    if send_tx_response_error:
        print(send_tx_response_error)
        alert(
            f"Error occured in algosigner, please try again! {send_tx_response_error}",
            title="Error",
            dismissible=True,
        )
        return

    # 4. Inform user about successful asset minting
    alert(
        Label(text=f'Here is your transaction id : {send_tx_response}', role="text-wrap"),
        title="Success! Here is your asset info:",
        dismissible=True,
    )

    # 5. Thats it! You can now optionally await for a transaction
    #              but this is not covered in this tutorial

The snippet above is pretty simple and straightforward, we obtain the transaction parameters, sign the transaction based on field values provided by the user and lastly send the transaction. Once the transaction id is obtained we display a simple popup that provides the transaction id to the user. Users then can go to the algoexplorer website to observe the minted asset!

4. Testing the web app

It is time to test the code and functionality of our AlgoMinter app. Click on a gear icon in the top left corner and select Publish App. The reason why we will perform testing separately from using the Run functionality is that when we run the app inside the Anvil’s builder, it won’t be able to access the AlgoSigner even if it is installed in that browser. Hence, we want to run it in a separate tab via the URL that Anvil generates for us and automatically hosting the web app on their infrastructure. Another cool detail to note is that all changes performed in the builder can be reflected live on the published website (mainly for test purposes, do not do that in production!).

Figure 4Fig 4. Example of a running app with filled text boxes

Once you clicked on Publish App, select either Share via private link or Share via the public link, keep in mind that on a free plan you might not have an option to choose the domain name. Once you obtained the URL, simply open it in your new tab and you should observe something similar to Fig 4.

Figure 4.1Fig 4.1 Example popup from algosigner asking for user’s confirmation

Enter values for some dummy asset to be created, sign your transaction, and observe the asset being created in your AlgoSigner wallet!

5. Conclusion

Congratulations! At this point, you should have your AlgoMinter up and running on Anvil’s infrastructure. Even with the free plan, you are already able to share your application with anyone on the web. Additionally, by relying on AlgoSigner, any user of your web app can create their unique Assets on Algorand Blockchain.

In case you would like to clone the instance that was created and demonstrated in the tutorial you can use the following link to clone my instance directly and have used it as a base template for your Anvil web apps with AlgoSigner integration pre-configured!

Thank you for following the tutorial and I hope that you found ways to apply this sample to build simple rapidly prototyped web apps powered by Algorand and Anvil’s platform!