Everything you need to know to create a simple Dapp on Tezos
The dapp will be React-based. To be honest, I am more of a Svelte guy, but React being the most popular front-end framework at the moment, it seems logical to use it in order to share this knowledge with as many people as possible. In addition of React, we will also use different tools developed to work with the Tezos blockchain: Truffle to compile and deploy contracts, a modified version of the Tezos Starter Kit from Stove Labs to run a sandboxed node, Tezbridge to connect our wallet with the dapp and sign transactions and finally Taquito to interact with the blockchain and the smart contracts. These tools are generally presented separately but I am confident it will be interesting to see how they work together.
- This dapp is not production-ready! Although it is useable as you will see, there are a few considerations that were not taken for the sake of simplicity. For example, the React code is not optimized to remain simple in order for beginners as well as advanced users to follow along.
- This is not a React tutorial! This tutorial requires a basic knowledge of React.js and JavaScript. I will copy here only the important pieces of code, the entire code for the dapp is available in the associated Github repository.
Setting up the environment
$ npm i -g truffle@tezos
$ git clone https://github.com/claudebarde/tezos-react-tutorial $ cd tezos-react-tutorial $ npm install
Now, let’s install React dependencies:
$ cd client $ npm install
$ cd .. $ npm run start-sandbox -- carthage
Compiling the contract and deploying it
- It fetches the contract to deploy -> artifacts.require(“PointOfSale”)
- It fetches the information of the account we will use to deploy the contract (in the alice variable).
- It sets the initial storage (the values will be automatically converted to Ligo compatible values by Taquito).
- It exports the deployer function and the initial storage.
$ npm run compile
Now we can deploy the contract to our sandboxed node:
$ npm run migrate
Starting migrations... ====================== > Network name: 'development' > Network id: NetXdQprcVkpaWU > Block gas limit: 10400000 1_deploy_point_of_sale.js ========================== Replacing 'PointOfSale' ----------------------- > operation hash: oo3JG8jJhGLtQ71qMtqZqKzAniadiZDeQNqe6sA7W8ZXuydSfeS > Blocks: 1 Seconds: 8 > contract address: KT1SVc1wpWdBmJUMruacMjfjS9Gib5WLmz28 > block number: 6631 > block timestamp: 2020-03-13T14:47:47Z > account: tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb > balance: 1999999.536464 > gas used: 42199 > storage used: 1208 bytes > fee spent: 5.73 mtz > burn cost: 1.465 tez > value sent: 0 XTZ > total cost: 1.47073 XTZ > Saving artifacts ------------------------------------- > Total cost: 1.47073 XTZ Summary ======= > Total deployments: 1 > Final cost: 1.47073 XTZ
Building the front-end
Now let’s look at the top of the App.js file:
- shortenAddress will turn tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb into tz1VSU…8Cjcjb, which is, I am sure you will agree with me, easier on the eyes!
- mutezToTez will turn values in microtez into tez. As a rule, I only work with mutez values, in my opinion, they are easier to work with, less prone to calculation errors, you can always easily display a user-friendly value on the front-end and you avoid errors of tez <=> mutez conversion or handling mutez while you thought they were tez and vice versa!
<script src=”https://www.tezbridge.com/plugin.js”> just before the <title> tag. This line is necessary to import Tezbridge. Tezbridge is a tool that allows you to use any Tezos address you want to sign transactions. In a blockchain context, “signing a transaction” simply means that you approve the transaction. In the example of Café Tezos, we will sign a transaction when we want to buy a coffee. Tezbridge plugin will give us a few useful functions that we can use to sign transactions and do other important actions.
$ npm install @taquito/taquito $ npm install @taquito/tezbridge-signer
const contract = await Tezos.contract.at(contractAddress);
This is why it was important to update the contract address earlier with the one returned from the console after truffle migrate, Taquito will look for your contract using the port you provided for the RPC. Then, we will save the contract instance because we will use it again later with the useState hook.
const storage = await contract.storage();
storage.menu.forEach((value, key) => { coffees.push({ name: key, price: value.c[0] }); });
You know you got the right secret key because it generates the right public key (the one you can find under the pkh property of the alice object). As a manager name, type “alice” (or any other name) and choose a simple password (it is only associated to this account for your development environment and you will type it quite a few times). Hit Confirm and you are ready to use Tezbridge!
As a good practice in a dapp, we don’t want to automatically connect or have startling windows welcoming our users. We want to let them decide when it is a good time for them to connect their wallet. “Café Tezos” has a button at the top right corner that will initialize the wallet when our user presses it. We want a few things to happen when the users connect their wallet:
- We will get their address and display it on the button to show them that they are indeed connected.
- We will fetch their balance so they can know very quickly if they have enough money to purchase a coffee.
- If the owner of the contract logs in, we will present them with a button to withdraw the balance of the contract.
This is how it will look like:
The tezbridge object exposes a method called “request” where we will pass an object with the method property set to get_source. This allows the users to select the wallet they want to connect to the dapp and returns the associated address. Tezbridge will open a new tab next to the dapp, you can leave it open after you are done or close it. After the users are connected, we save their address to update the button.
With the help of Taquito, we use await Tezos.tz.getBalance(userAddress) to fetch the user’s balance. Once again thanks to the incredible functions provided by Taquito, we can fetch the state of the storage. We only need the contract instance we created a bit earlier and we call the storage method on it. The storage object will have properties named after the fields of our storage. As we explained before, a few functions are now available on each property of the storage object. We want to get the number of points of the current user, so we use storage.customers.get(userAddress) to find it. Because the user’s address could be missing from the map and have an undefined value in JavaScript, we will set the number of points to 0 in our front-end dapp if this is the case.
Buying a coffee
One of the most fascinating things in programming is that, from a user’s point of view, it’s just a click on a button, but from a developer’s point of view, it may be a dozen or hundreds of calculations and checks to change a value!
This will be the case when you send a transaction to a Tezos contract. The function is located in the Menu component. You must be sure to have the right parameters before sending it, you must verify that the transaction has been applied and you must wait for its completion. Once again, Taquito will be of great help in order to achieve it. This is how buying a coffee will look like:
const op = await contractInstance .methods .buy(coffee) .send({amount: price, mutez: true})
Let’s break it down: first, we need the contract instance that we declared earlier. This contract instance gives us access to the methods method which contains the different entry points of the contract. We will use buy. If you remember, the buy entry point in the smart contract expects the name of the coffee the user wants to buy, so we pass it as a parameter to the buy method. To finish, we call the send method and pass to it an object with two properties: amount with the amount of tezzies the user is paying for the coffee and mutez sets to true to tell Taquito the value we are passing is in microtez. Then the transaction returns an object with different properties that we are going to use next.
This operation will switch the user’s screen to the Tezbridge tab (or open a new one if they closed it) and they will be prompted to approve (or reject if they change their mind) the transaction. As soon as it is done, the status property of the object returned by the transaction should change to “applied“. This means the transaction was sent. Now, we can sit down and relax while the sandboxed node confirms the transaction and includes it in a block. In order to do that, we call await op.confirmation(1) which will wait for 1 confirmation before going on with your code. Once the transaction has been included in a block, the includedInBlock property will change from Infinity to a number (the block number). This is the signal, you can start brewing some coffee ????
As you can notice from the code, during this flow, we update the state of the dapp. It is extremely important to inform the users of what is happening, as the confirmation of transactions can take up to a minute and you want to prevent them from clicking multiple times on the button and ordering 10 coffees!
As you can see, we are also using the contract instance to send the transaction but remember, the withdraw entry point doesn’t expect any parameter. However, the main function in the contract does! To make it work, you pass an array containing another array with the string “unit” as the first element to the method representing your entry point, so withdraw([[“unit”]]). For the send method, there is no need to pass any parameter.
As shown above, you then wait for the confirmation of the transaction being included in a block and you update the state of the dapp.
And that’s it! Now you know how to build a simple dapp using React, Ligo, Tezbridge and Taquito
Recap
- React: no need to introduce the most widely used front-end framework, React works seamlessly with the rest of the tools and its Hook API allows us to update efficiently the state of our dapp to keep it synced with the storage of the smart contract.
- Taquito: Taquito is an exceptionally useful library that allows multiple types of interaction with smart contracts and data manipulations.
- Tezbridge: this small but convenient tool allows dapp users to choose a wallet to sign their transactions. If you are familiar with Ethereum, it is similar to what MetaMask achieves.
- They connect their wallet.
- They request a transaction to be sent to the smart contract.
- They sign the transaction.
- The transaction is checked and processed by the smart contract.
- The dapp interface displays a confirmation (or error) message.
Conclusion
After spending a few weeks looking for the best solution, I am confident that you cannot go wrong if you want to build a dapp on Tezos if you use the following stack: [Your favorite front-end JS framework] / Ligo / Taquito / Tezbridge.
It is also worth remembering that developing dapps on Tezos is quite new and the libraries are continuously updated. Taquito is bundled in your dapp, so this won’t be a problem, but an update in Tezbridge, for example, could break your dapp. It is then very important that you follow the developments of the Tezos community and update your dapp accordingly.
This article has been published from the source link without modifications to the text. Only the headline has been changed.