In this tutorial we are going to build a decentralized TicTacToe based on this smart-contract that we’ve written in Pymich. This is a pretty simple smart-contract that allows user create games, join & leave games and play games. The rule is that each player pays 5 XTZ to play the game and the winner gets 10 XTZ and if it’s a draw then you will get your money back.
You can find a demo here.
By the end of this tutorial, you will have a basic understanding of how to create a DApp on the Tezos blockchain using ReactJS, and be equipped with the necessary skills to continue exploring possibilities of decentralized application development on the Tezos network. Let's get started !
If you get lost in the code during the tuto, you can find a full version of the App on githhub !
Before we start, make sure that you check all the following requirements.
Have a basic knowledge of :
- How blockchain works
Init React App
Now that we’re ready to start, let’s initialize the app with create-react-app , run the following command where you want to store the app on your computer.
Once you’ve created the app you should have the following folder structure.
This structure is a default one and we’ll update it to make it more readable and organized. Let’s update the src file structure as followed.
Once you’ve created the new folders, you will have to move some files as the following structure mentions.
After make sure that you update the imports of the files when you move them. If you’re on vscode it updates the import automatically but you still have to update some imports is the following files : index.tsx, app.test.tsx, App.tsx.
Now you can run you server with npm start and you should see the default react template running.
Now that we have all the structure set up let’s create a router for the app to handle routes rendering.
Before beginning this step you will have to install the router library that we will use : react-router-dom
Then you can create a new file under src/app/ called Routes.tsx
Here we are declaring the router with no routes inside. We’ll add routes later !
Now we need to link our router with the app. So let’s jump into the App.tsx file.
You can remove all its content and replace it with this.
Our app can now handle routes rendering.
Here we’ll create a file to store the app providers. Providers are components that provide a way to pass data down through the component tree without having to pass props manually at every level. By wrapping components in a provider, any nested child component can access the data passed down through the provider. This allows for a more efficient and convenient way to manage state and share data between components.
So create a new file under src/app called Providers.tsx.
The Providers component will have a children property that will contain all the children components.
We now need to link the Providers component to the app. Jump into the App.tsx file.
The app can now handle context management.
In this part we’ll work on integrate the HTML structure and all the corresponding styles. Again you can find a demo of the screens here.
Before we start integrating screens let’s do some basic css setup.
You can remove all the content of the src/app/styles/App.css file and paste the below code.
We will handle the app styling with a famous react styling library called styled-components
Setup a theme
Now that we have installed the package, let’s create a theme for handling colors, spacing etc..
In order to proceed, we need to create two files. One for defining the default theme interface and the other for declaring the theme values.
We also need to wrap our app inside the theme provider in order the share the theme context to the whole application.
Create a new page
We will now create a new page to render the home view.
Sounder src/pages/ create a new folder called HomePage.
The first one called HomePage.tsx which will contain the home page component declaration.
And the second one called HomePage.styled.ts which will contain all the style declarations for the home page.
This will be the structure that we will follow for all the pages and components of the App.
Let’s now link this new page with the router.
Create the App header
We will now create the first component of the app: the Header. In order to do that, you can create a new component folder under src/components.
Here are the files for the header component and its corresponding style.
In order to render the header component, let’s add it in the App.tsx file.
We’ll now fill the home page that we created before.
Declare sub components
We can see that there are several subcomponents used inside the page component that we just created. Those subcomponents are categorized as unsharable, this means that they are supposed to be used once. That’s why they will be declared in a subfolder inside the page folder.
We also have the Games subcomponent to declare
And finally we will create another component that is the game card. This one will be shared so let’s declare it inside the components folder.
Once we created the Home page we will now create the second page that our app will render : the GamePage. Let’s proceed.
We need also to declare the TicTacToe Grid as a subcomponent.
Link the page to the App
There is one little thing that left to do, link the page to the app.
In this part we’ll focus on handling the wallet connection.
In order to do this we will use beacon wallet through the taquito library. So we’ll need to install the two following libraries but before we need to downgrade react-scripts version to avoid some breaking changes due to webpack 5 included in the react-scripts v5. Run the three following commands.
Create a new context
The first step to proceed is to create a context for getting and storing wallet informations.
Let’s wrap up the app with the new context.
Create an env file
One of the best practices of creating an app with react is to create an .envfile. So let’s do this and add the 3 following variables. The first two are quite linked because they are defining the network that the app will use to run (Ghostnet here). If those 2 variables are unconsistent this will result as an error. And the last one is defining the contract address that we’ll use to handle games.
In a more scalable app we would rather create one file per environment, for example one for the ghostnet (dev) and the 2 others for the mainnet (staging & production).
Create a taquito wallet handler file
In this file we will declare all the functions that will help us to connect, disconnect or getting the user balance etc…
Create a wallet connection hook
Now that we have created our taquito helpers, we will create a hook to integrate those helpers with the context.
Then we need to link the hook with the context.
Link the frontend with the wallet connection
Now that we have all the wallet data that we want we are going to display them in the frontend.
First of all we’ll create a helper to format a wallet address into a shorter version.
Then jump into the header component where connect wallet buttons are declared.
We also need to change the display to connect button.
The next step is to add the interactions with smart-contract. Our contract has 5 entrypoints but we’ll use only 3 of them for this app. build_game, join_game and play.
Let’s create a file for declaring the required transactions.
Link the entrypoints to the frontend
First we’ll link the entry points build_game and play to the frontend. We’ll handle the third one later.
First download the following library to handle transaction toasts. This library will help us to render the transaction status to the user.
But in order to do that, we have to add a toast container with the following props and also we need to add the toastify css file import at the root of the App.
Build Board entrypoint
Jump into the HomePage component, where the new game button is declared.
The play entrypoint is used in the Grid component, so let’s update it.
Retrieve games data
Retrieving data from the blockchain is necessary to make the app working. But the problem with this is that it needs to involve a third part into to the loop. They are several ways to do that like creating an indexer, getting data from the storage with an API or with taquito. In this tutorial we are going to use an external API to do that, this is the easier and quicker way to proceed. We’ll use the API from TZKT.
Create Data types
As we are using typescript we definitely want to have typed data and avoid using the anytype as much as possible. That’s why we are creating types for the data that we will recieve from the API.
Create a Game context
In order to retrieve those datas we’ll need to create a new context for handling stuff related to games.
Link it in the providers.
Get all games from the contract storage
We are going to use axios to get storage data from TZKT API. So let’s install it.
Create a new file to create an helpers to get all the games from the storage.
Now jump into the GameContext.
Add games statistics
We are now going to render games statistics in the HomePage.
Create a games helpers file to export data calculation outside of the component.
Then jump into the Statistics component to render all those stats.
Home Page update
Now we need to update the home page to dynamise the display when the user chose to see his own game or wants to join a new game.
We also need to update the display of the games list nested in the home page.
And finally we need to update the GameCard component behavior.
Game Page update
The final part of the update is about updating the GamePage with a dynamic version where the user can see all the game details.
Update also the Grid display.
In conclusion, building a decentralized application on the Tezos blockchain can be an exciting and rewarding experience. By leveraging the unique features of the blockchain, such as its on-chain governance and formal verification capabilities, developers can create secure and reliable applications that offer a high degree of transparency and user empowerment. From creating a smart contract to building a front-end interface with React or other web development tools, there are many steps involved in developing a successful DApp on Tezos. However, by following the best practices outlined in this tutorial and continuing to explore the possibilities of the Tezos ecosystem, developers can unlock a wealth of new opportunities and possibilities in the world of decentralized finance and applications.
Of course this application can be improved, here are several options that you can explore to develop your skill with this app.
- Make a real-time app with live data (user does not have to refresh the page to see new data), try to use react-query library to fetch data at a determined interval.
- Create an option for leaving the game. Interact with the leave_game entrypoint.
- Improve the UI/UX to make it more modern and user friendly
It’s now up to you !
As I said earlier, the whole project is hosted on github. There might be some little bugs or improvements to make so feel free to open issues (and/or make PR) on the repo. Also if you have any question you can open issues and we’ll have a talk about your problem !