The ultimate end-to-end EOS dApp development tutorial — Part 1
In order to help the EOS Blockchain Development community, our development team at Infinite X Labs decided to run a number of tutorials dedicated to EOS Development. In the previous tutorial, we have learned how to set up our development environment for building EOS dApps. We have launched our local testnet, deployed a sample EOS smart contract and interacted with it. You can find all in the first tutorial from our EOS Blockchain Development series here.
Now it’s time to level up. What’s the best way to learn how to build EOS dApps if not creating one? This is what we’re going to do. Through the development, you’ll learn a lot of new things and at the end of these 4 parts tutorial, you’ll be able to create your own dApps. At the end of the series will share with you some very interesting stuff, but for now, it’s a surprise :)
Keep a note that the EOSIO is still in development and some of the steps might change in the future. However, our team will try to keep the tutorial always up to date.
You can find all the source code for this part in GitHub
Last update — 29 July, 2018 — The tutorial was updated with the latest version of EOSIO — v.1.1.1
So let’s start!
Before we even start with the code we need first an idea. Have you seen “Ready Player One” movie by Steven Spielberg? If you did you already know what the Oasis is! Exciting, right? Let’s imagine for a minute that we’re part of the team who is going to build the next Oasis and our task is to integrate the blockchain technology inside it or at least to show that is possible to be done. What we’re going to do is a small dApp which will have Players. Each player can play a Game and he can receive coins from it. With the coins, he can go to the Marketplace and buy items which will give him power, abilities, health and/or level up. And of course the Oasis contract — our main start point.
What you need to start developing
To be able to develop EOS dApps you’ll need a previous background with C/C++ because this is the programming language used for EOS Smart Contracts
- Previous background with C/C++
An IDE where you’ll write the smart contracts. We’re using VS Code with C/C++ extension, but you can choose whatever best suits you. CLion is a really great choice too
- An IDE — Visual Studio Code
Of course, you’ll need a local testnet node, but you know how to build one. Check our previous tutorial for help here.
- A working local testnet node
Before writing our first contract we need to set up a few things which we will need during the development process.
Step 1. Start the nodeos
Step 2. Create a Wallet
It will store our keys which we’ll use when we’re signing transactions
Note: Don’t forget to save your keys and wallet password
Step 3. Create an Account
The EOS.IO smart contracts run on an account. So an account is required to transfer or otherwise push a transaction to the blockchain. Let’s name our account “anorak“. Sounds familiar?
It’s time to start developing our dApp! Create a project folder named Oasis. Inside add two main subfolders — contracts & tests. Yep, we’re going to write tests too. There will be four subfolders named: Players, Games, Marketplace & Oasis — create them.
Players Smart Contract
Players will be our first EOS smart contract. Each player will have a username, level, health points, energy points, balance, inventory (full of items) and abilities. He will be able to purchase items from the Marketplace which will be added to his inventory, health points, energy points and/or abilities. In order to get coins, he needs to play games with the other players from the Oasis
Create Players.hpp & Players.cpp files inside Players folder
1. Player.hpp is the header file that contains the variables, constants, and functions referenced by the .cpp file.
2. The Player.cpp file is the source file that contains the functions of the contract.
Let’s take a deep look at the basic structure of the EOS contract
Note: We’re adding only the includes in the Players.hpp for now as we won’t use it in other contracts yet.
At the beginning of the contract, we set all includes which we’re going to use. In our case Players.hpp already have them so it’s enough to include only the header in our contract implementation. Most of the time you’ll do that when you’re working with headers.
It’s a good practice to wrap everything in а namespace as it’s shown here with Oasis.
If you’re familiar with C++ you probably know why we have two using clauses before the class implementation. For the others — when we want to call an action from the eosio namespace in C++ we have to call it like that: eosio::some_action() in order to avoid adding eosio:: everytime in front of the name of the action we simple add using namespace eosio at the beginning.
The Players class inherits the “contract” smart contract and use its constructor (using contract::contract).
An important thing you should know when developing EOS dApps is that the smart contracts communicate with each other in the form of actions and shared memory database access. A contract can read the state of another contract’s database as long as it is included within the read scope of the transaction with an async vibe. This can be achieved by using one of two communication modes — inline or deferred. You can think of them as sync & async
From the EOS.IO documentation:
- Inline — Inline is guaranteed to execute with the current transaction or unwind; no notification will be communicated regardless of success or failure. Inline operates with the same scopes and authorities the original transaction had
- Deferred — Defer will get scheduled later at producer’s discretion; it’s possible to communicate the result of the communication or can simply timeout. Deferred can reach out to different scopes and carry the authority of the contract that sends them.
In the class body of our contract, there are two types of access modifiers — public & private. In the public section are the constructor and all actions. An action represents a single operation inside the smart contract. In our contract, we have add, update & getplayer. We’ll take a look at them in a while. In meantime, you should have already noticed that before each action we have “//@abi action“. It’s an indication flag for the eosiocpp script which will generate the .abi file for our smart contract.
In the private section, we keep everything which we don’t want to be accessible from outside the Players contract. Here we’re initializing the multi_index. What is it and why we need it?
As we said an action represents a single operation inside the smart contract. Each action operates within its own environment known as the action context. The context provides several things necessary for the execution of the action. One of those things is the action’s working memory. This is where the action maintains its working state. Before processing an action, EOSIO sets up a clean working memory for the action. Variables that might have been set when another action executed are not available within the new action’s context. The only way to pass state among actions is to persist it to and retrieve it from the EOSIO database.
This can be achieved with the multi-index which allow us to read and modify persistent state in the EOSIO database.
In our contract, we have declared an object template for the multi-index table called player. It’s important to note that as we’re creating a template for the table we need to add a primary_key too. We use the account_name as we want one player per account.
We also have an indication flag for the eosiocpp script — “//@abi table player i64“. We are saying that the name of our table is player and the used index type is i64.
Once the object is ready we need to typedef our multi-index with the following template:
You’re probably wondering what EOSLIB_SERIALIZE and EOSIO_ABI are?
These are C++ macros. EOSIO_ABI encapsulate the logic of the apply method. apply is the action handler, it listens to all incoming actions and reacts according to the specifications within the function. The structure of the macro is really simple. The first parameter is the type (name of the current class) and next parameters are all actions listed as in the example below
The EOSLIB_SERIALIZE macro provides serialize and deserialize methods so that actions can be passed back and forth between the contracts and the nodeos system.
Now let’s take a close look at each action and its implementation. The first one is “add“. It’s responsible for creating a new player in the Oasis.
The action implementation has 4 main key points:
1. As we want only the owner of the account or somebody with the account authority to be able to create players we have added an account verification check. This is very easily done thanks to require_auth function.
2. In order to use the created table — player, we have created an object of type “playerIndex”
3. As next step, we must verify first that the account doesn’t exist yet
4. In the end, we’re adding the new player. As any new player, it starts at level 1 with max health and energy points equal to 1000. Take a note here that the first argument of the emplace function will be the payer of the storage which will store the data. In our case, we have set the current account who trigger the action to be the payer. In some cases, we can set ourselves.
The “update” action is next in the row. It’s responsible to update the level, the health and energy points of a player. The implementation is very similar to the “add” action except that we’re modifying the player state and the modify function takes the iterator as the first parameter. The iterator is pointing to the object to be updated
The last action is “getplayer“. Which print an info about a player. It doesn’t have the require_auth function as anyone can trigger it.
Players Smart Contract Deploy
We definitely did a lot of things and it’s time to deploy our smart contract on the blockchain. First we need to generate the .wast & .abi files. We’ll do it using the eosiocpp script. Go to the Players folder and run the following command in the terminal:
The command will generate the Web Assembly of our contract.
Next, while you’re in the same directory run this command to generate the Players.abi.
Now when we have Players.wast & Players.abi will use them to deploy our contract.
Testing the Smart Contract
Awesome! You have deployed your first EOS contract. Now it’s time to test our actions. Let’s first add a new user. You can do this using the push action command to trigger the action:
Note: With the latest version EOSIO you should always add permission flag before pushing any actions.
Let’s see if the player was created by using “getplayer” action
Everything looks perfect.
Now we’re going to update our player and will use the “getplayer” action again to see if we have succeeded:
Our player has been updated successfully. Good job!
Let’s stop here. We have done a lot of new things today. Take your time to understand everything we did and try to do it by yourself. :)
So far we have covered the basics of an EOS smart contract. In the next part, we’re going to level up again. We’ll extend the Players contract, we’ll create the remaining ones (Marketplace, Oasis and a few Games) and then make the connections between them. And of course, we’ll add some unit tests. It will be a lot easier once you have learned the basics.