Use two of the hottest techs in Tezos development to build a dapp
The landscape of dapp development on Tezos has evolved a lot these last six months. It went from a painful and long endeavour to a walk in the park. This change has been possible thanks to the introduction of powerful tools that allow developers to focus on providing value and functionalities to their dapps and to forget about the technicalities of plugging their dapp to the blockchain.
Taquito doesn’t need any introduction, it is the de-facto tool to connect your Tezos dapp to the blockchain. But don’t let its monopoly position lure you, it is an amazing tool and will make your job as a developer 100 times faster and easier. With a few lines of code, your dapp will be set up and connected to the blockchain and your smart contract. Taquito will do all the heavy lifting in the background so you don’t have to worry about it.
Beacon is a toolkit developed by Airgap that connects to a wallet (at the moment, to the AirGap Wallet) and signs transactions to be sent to the network. The stable version is pretty recent but already very promising. We are going to use the Beacon extension in this tutorial to sign transactions but you can also use the wallet on your phone! Beacon provides a simple yet sleek and efficient interface with a lot of information and customizable options!
Preparing the workspace
The first thing to do is to download the necessary packages. You can simply run npm install from the root of the project and check the package.json file. You will see there the two main dependencies that we need to create a Tezos dapp: @taquito/taquito ^6.3.0-wallet.4 and @taquito/beacon-wallet ^6.3.1-beta.0. Those are the latest versions available at the time of this tutorial.
Note: you don’t need to install the @airgap/beacon-sdk as Taquito is going to do it for you!
If you check the HTML file, you will see that it is very simple, with a main tag that contains two divs: one that will show the button to connect your wallet and one that will show the contract interface.
The style.css file contains basic styling information and you probably noticed a toast.css file that will be used later when we will override some features of the Beacon wallet.
At this point, you should have the Beacon extension installed in your browser. If you don’t, follow these instructions to install it. You will need it to interact with the smart contract from the dapp.
Connecting your wallet
Let’s start coding now 😅
Open a new JS file called “index.js”. First, let’s import the dependencies we need. If you checked the index.js file provided in the Github repo, you probably saw import "babel-polyfill" at the top. This is only required to use ES6 syntax with Parcel, the bundler used in this project.
Next, we import the two dependencies necessary for this project:
The first line imports the BeaconWallet class that we will need to create the wallet object. The second line imports the Tezos object from Taquito. This is a multipurpose object that we will use in different situations. You can also see that we initialize a variable called contractAddress that will hold the address of the contract we want to connect to (very useful during development when the address may change often).
When you run npm run dev and open a new window on http://localhost:1234, you will be presented with a big teal button. This is the button you will click to connect your wallet. First, we have to connect it to the function that will initialize the wallet:
After that, we can start writing the different steps of the wallet initialization:
Note that the function must be asynchronous. The next step consists of setting up the Tezos object we imported earlier. We will tell it which network we want to connect to (Carthagenet here) by calling its setProvider method:
I generally recommend wrapping the steps of the wallet initialization or the transaction processes into a try ... catch ... statement because a lot of things can go wrong and it is crucial to inform your users if something doesn’t work as expected.
The Tezos singleton instance, i.e the object imported from @taquito/taquito, (referred to as the TezosToolkit in the documentation) has a method called setProvider that accepts an object with different properties, one of which called rpc that must be a link to a Tezos node RPC interface. I generally use the one provided by SmartPy, but you can use the one you prefer.
Now that we told our dapp which network we want to connect to, it’s time to take care of the wallet! First, we create a new instance of the Beacon wallet with the class we imported from the library:
The new wallet object must be instantiated with an object containing different options. At the very least, you should provide a name for your dapp that will appear in the pop-up window to sign transactions. We will add more options later.
Setting up the name of your dapp in the wallet is an important detail for the general user experience as the users of your dapp will know the dapp they are using triggered the pop-up and not something else.
Then, you create a new wallet with new BeaconWallet(options). Once the new wallet is created, you can set the network you want to connect to by creating an object with a type property. By default, the Beacon wallet will connect to mainnet but you can also use the carthagenet value to connect to Carthagenet or custom to connect to a sandboxed node. If you wish, you can also specify the RPC URL to which you want to connect with the rpcUrl property (if not provided, the wallet will connect to its default RPC access point).
After you decided about the network, the wallet object offers a requestPermissions method that will request the permission to connect to the specified network and sign transactions on your behalf. Once allowed, your wallet is properly configured and ready to work! The last thing to do in this configuration step is to set the wallet as the default wallet for Taquito:
The new Wallet API provides a setWalletProvider method on the Tezos object that allows you to indicate to Taquito the wallet you want to use (after setting it up). From now on, Taquito will use the Beacon wallet to send transactions!
Updating the dapp interface
When you create a dapp, it is always recommended to give your users essential information about their account, like their address and their balance. This kind of feedback indicates that their wallet is properly set up and that the dapp is connected to the blockchain and their wallet.
After initializing the wallet, you can easily get the user’s address from wallet.permissions.address. Once you have the address, you can get the user’s balance using the Tezos object provided by Taquito:
At the same time, it would be useful to keep the contract instance in memory so we can have easy access to it when sending a transaction to the blockchain. Once again, the versatile Tezos object will help us. Under the new Wallet API, using Tezos.wallet.at(contractAddress) provides you with an abstraction of your smart contract. While we’re at it, we can use the same abstraction instance to get the storage of the contract and further update our dapp interface with data directly from the contract by calling the storage method on the contract instance:
Now in a real-life situation, you would probably use a framework to build your dapp, like React or Vue, but we are using vanilla JS here, so we have to manually update the DOM. I created a simple function that updates the text inside an HTML tag to follow the DRY (don’t repeat yourself) principle. First, we want to hide the “Connect” button and display the dapp interface, then update all the values with the data we got from the smart contract:
A few general observations here:
- I generally prefer keeping the user’s address and balance at the top level and accessible everywhere in the code in order to avoid unnecessary code to request the address and the balance multiple times. Most wallets dispatch events when a user signs in and out and you can intercept the events to modify the address or the balance (as we will see it later).
- The balance is always returned in microtez, which is great for calculations, but difficult to read for dapp users. Don’t forget to divide the balance by 1,000,000 to get a more readable number. You can go the extra mile and round it up and use toLocaleString("en-US") to make it nicer!
- Taquito keeps the values in the storage as properties of the object returned by contractInstance.storage(). It is then very easy to access the values in the storage (except for maps and big maps that are a little more complex).
Updating the message in the smart contract
Now our dapp is ready to play with the smart contract!
First, we create a changeMessage function and attach it to the click event of the button with the update-message id:
This is the right time to keep some user experience recommendations in mind. A lot of dapp users do not realize that it actually takes one minute to add a transaction to a block on mainnet (around 30 seconds on testnet) and some of them will lose patience and click multiple times on the confirmation button. This will create multiple transactions that they may accept thinking the first one didn’t go through. You must prevent that. When they confirm a transaction, you should disable the actionable parts of the interface and indicate to them clearly that they have to wait. This is the goal of the next two lines of code:
Now, your users cannot send a new transaction before the current one has gone through and they will see a little spinner that indicates that something is loading and they should wait 😊 At the same time, we get the message they entered in the input. If you wish, you can check if there is a message or if the string follows some rules.
Next, it is time to use Taquito again and send the transaction to save the message into the smart contract:
Here is what happens in a few lines of code:
- We use a try ... catch ... finally ... statement to wrap the transaction. If the transaction fails for whatever reason, you MUST inform your users, so they can stop waiting and maybe fix the problem themselves.
- The contract instance we saved earlier exposes a methods property that contains itself properties that reflect the entrypoints of the smart contract. In this case, we want to call changeMessage which expects a string as a parameter. The result provides a send method that will send the transaction to the Tezos node and returns a promise which resolves with a transaction operation object.
- After the transaction is sent, you must wait for its confirmation. The transaction operation object returned one line above provides a confirmation method that just does that. If no parameter is provided, Taquito waits for 1 block confirmation. If you provide a number n as a parameter, Taquito will wait for n block confirmations before executing the following lines.
- If the transaction fails, you get an error object that you can use to display a message to your users.
- After the transaction goes through or fails, you want to return the interface to a working state by enabling the confirmation button and removing the spinner.
Once the transaction is confirmed, there are a few things that you can do:
- You can reset the value of the message. This is, in general, a good clue that something happened and the message is “gone”.
- You should refresh the storage. Although you could also just update the HTML tag containing the message from the storage, it is always better to get a fresh storage after a transaction in case something else changed during this one. If you use a framework, you probably have a state with a storage property and fetching a new storage will update all the data in the interface linked to the state.
- You can do some other minor updates, for example fetching the new balance of the user to reflect the gas cost debited for the transaction.
Customizing the Beacon SDK
The Beacon SDK offers multiple ways to customize the experience of your users with their wallet. Let’s check one of them here.
After the transaction is sent to update the smart contract, you can see a pop-up in the dapp giving you useful information about the transaction:
Maybe you don’t want this pop-up to appear and you want to provide a customized response, like a toast. Fear not, because it will literally take 2 minutes to do it!
Let’s go back in time. Do you remember how we created our new Beacon wallet? With new BeaconWallet(options). The options object contains the name of your dapp. It turns out, you can provide more options to customize your dapp! In this tutorial, we are going to use a toast to inform our users that the transaction has been successfully sent to the network!
In the HTML file, you can see the toast at the very bottom: <div id="toast">Some text...</div>. In the JS file, let’s add a function that will display a message in the toast and that will show it and hide it after 3 seconds:
First, we delay the toast for 3 seconds because it will take roughly 2 seconds for the Beacon wallet window to close after you confirm the transaction. Then we update the message in the toast, we show it for 3 seconds before hiding it again.
Now, let’s go back to the wallet initialization options that should look like that:
We are going to add an eventHandlers property to the options object to tell Beacon what we want to do when the transaction request is sent successfully. The eventHandlers property accepts an object where you can set different properties according to the event you want to catch. Let’s see how it works for the event that’s dispatch when the request is successful:
The property must be one of the events listed in this enum from the Github repo. Each event handler accepts an object with a handler property that you can set to a promise which receives the event data. We use this promise to display our toast when the transaction is sent successfully. Here is the result:
Now, the default Beacon pop-up is gone and replaced with our toast. Using this very simple method, you can customize the response from the Beacon wallet, for example in case of an error or for different events like a connection to a wallet. You can also use it to change the user’s address and balance when they log in with a different address.
This very simple dapp was the occasion to showcase the strengths of two of the best tools you can use right now to build on Tezos: Taquito and the Beacon SDK. Both provide a wide range of features, a high level of customization and an interface that allows you, the developer, to write less code and focus more on the user experience than on the nitty-gritty of interacting with a Tezos node.
Taquito’s new Wallet API is a huge step forward in the direction of using different wallets to connect a dapp to the blockchain and the Beacon wallet finally provides a wallet solution for Tezos dapps that is safe, pleasant to look at and easy to use.
Now the eco-system is finally ready to welcome more dapp developers, so let’s start building 👷♀️👷♂️
You can also get more information about the last release of Taquito and the Wallet API by reading Jev’s last post on the subject.
Build a Tezos dapp using Taquito and the Beacon SDK was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.