Hey everyone, I recently learned how to make a distributed applications with the ethereum technology, but as a beginner in web development i had a lot of troubles finding a tutorial that could be easy to understand and cover all the aspects of making a dapp, from the first line of code to the deployment on the ethereum network.

The purpose of this tutorial is to make a Sport betting Dapp, where each player can bet a certain amount of ethers on a team, and where the smart-contract redistribute the prices to the players who betted on the right team !

Note : This tutorial is working right now but with the fast growth of blockchain technology it may become depreciated at some points.

Update (27 Dec. 2018) : I edited the solidity code to work with 0.5.x

This tutorial is intended for people who have a little bit of experience about Javascript and Blockchain, and who wish to learn the making of a dApp in a very simple way.

I will use React for developping the front end, and build a working dApp, and the tutorial is also suitable for those who wants to use another framework such as angular or even plain javascript. This tutorial will have the following steps :

Preparing the project environment (Part 1)

Writing, testing, and deploying the smart-contract (Part 1)

Creating the front-end (Part 2)

Preparing the project environment

Prerequisite

For the development of this application, node.js will be needed in order to install various packages so the first step is to be sure that it’s already installed on your device.

As we want to keep it simple, we will develop our App in React using react-create-app, that will generate for us a simple skeleton for our app. It’s not the most flexible and customizable way to develop a react application, but will perfectly do the work for this tutorial. Install it by typing npm install -g create-react-app . The -g flag means that you’re installing it globally for your device.

Another framework for developing the app will be Truffle, that will be useful to compile, test & deploy our smart-contract on an ethereum Blockchain. Install it by typing npm install truffle -g in the terminal.

Creating the application

Once you have those two packages installed on your computer, you can create your application by typing create-react-app bet-eth (or any name you want for this app). It will create a folder called bet-eth in your working directory, with some react useful dependencies installed. Access it with your terminal cd bet-eth . You can type npm start to start your website on localhost:3000 and you will end up on the main default page of create-react-app in your favorite web browser, but we will edit the front end later.

create-react-app default homepage

Now, let’s install the dependencies needed for our dapp: web3, truffle-contract, truffle-wallet-provider & ethereumjs-wallet. You can install it simply with npm install web3 truffle-contract truffle-wallet-provider ethereumjs-wallet --save . The --save flag is useful for saving the dependencies into the config of the app.

The last step is a bit tricky, but should work : we need to initialize Truffle in our project, by typing truffle init . But it’s impossible to do it in a folder that already contains some datas, due to data overwriting protection. There is two way to deal with this :

Content made by using truffle init

Create an empty folder, doing truffle init and Copy-paste the content made in our project folder (let’s do this)

Create all the files and folders manually in our project folder (also work but longer)

At the end, your project folder should look like this :

Bet-eth folder after configuration

Check that all the dependencies are well installed in your package.json file :

{

"name": "bet-eth",

"version": "0.1.0",

"private": true,

"dependencies": {

"ethereumjs-wallet": "^0.6.0",

"react": "^16.4.0",

"react-dom": "^16.4.0",

"react-scripts": "1.1.4",

"truffle-contract": "^3.0.5",

"truffle-wallet-provider": "0.0.5",

"web3": "^1.0.0-beta.34"

},

"scripts": {

"start": "react-scripts start",

"build": "react-scripts build",

"test": "react-scripts test --env=jsdom",

"eject": "react-scripts eject"

}

}

Building and testing the smart contract

Now that we prepared our project, let’s develop the main core of our application : the Smart-Contract.

We could start by making the front-end of the app and then directly write & test a smart-contract with this front-end. But since debugging a smart-contract is though and not very optimized, we will first write & test our smart-contract on the Remix IDE, which is a light and web Solidity IDE that allow us to write, compile and deploy a smart-contract on any network.

Before writing any line of code, let’s write down all the different functions that we need to be clear on how the contract will work and what is needed to make it work. To keep the problem simple, let’s say we want to be able to bet on two options, winning or losing. Each player is able to bet a certain amount of ethereum on a team or another, and following the result, can win back the initial bet and a certain ponderation of the ethers from the losing jackpot.

In a mathematical point of view, the winning amount can be represented with this simple formula :

bet / Total bets on your team represent the ponderation of what you will win, that’s why we multiply it with the losing stake to share.

For instance, let’s imagine a case where :

There is already 60 ETH betted on Team A and 100 ETH on the team B

and Player 1 bet 20 ETH on Team A. The total bet on Team A become 80 ETH so Player 1 bet represent 25% of the Total bet.

bet 20 ETH on Team A. The total bet on Team A become 80 ETH so bet represent 25% of the Total bet. Team A win the game.

Player 1 will then win his initial bet, summed with 25% of the Total bet on the other team (in this example 100 ETH), which represent 45 ETH in total.

What variables and function will we need ?

For the variables we will need :

owner — The address of the contract owner

minimumBet — To define a minimum bet (uint)

totalBetOne — This is for the stake of the first team (uint)

totalBetTwo — And this for the other team (uint)

Player — Object for the player information, that will contain the amount of the bet and the team selected (Struct)

players — An array of addresses which will contain all the players addresses

Mapping between addresses an player infos — To link the player address to some players infos

And for the functions:

kill() — To Kill the contract if needed

CheckPlayerExist() — To verify if the player already played or not

Bet() — To bet on the game

distributePrizes() — To distribute ethers to the winners

AmountOne() — Getter to see the stakes on team one

AmountTwo() — Getter to see the stakes on team two

Now that we have all we need to build our contract, let’s developp it !

Writing the smart-contract

Let’s open the remix IDE, and create a contract called Betting.sol

Creating a new file on the Remix IDE

Let’s start to define the solidity version first, as a convention for the contract pragma solidity >0.4.99;

then let’s define the contract by writing

contract Betting{

//content of the contract here

}

Now lets define the variables that we need in the contract

contract Betting{

uint256 public minimumBet;

uint256 public totalBetOne;

uint256 public totalBetTwo;

uint256 public numberOfBets;

uint256 public maxAmountOfBets = 1000;



address payable[] public players;



struct Player {

uint256 amountBet;

uint16 teamSelected;

}



// Address of the player and => the user info

mapping(address => Player) public playerInfo; }

Let’s add a constructor for this contract, where we will define the minimum bet. 100000000000000 wei correspond to 0.0001 ether

constructor() public {

minimumBet = 100000000000000;

}

Let’s now develop the first function, to check if a player exist or not. If the player is found in the players array, then true is returned. If no one is found, false is returned.

This function take the address of the player in argument, view means is that since it doesn't alter the state, your local node can run it and tell you the result without needing a transaction. This doesn't cost gas because only your node has to run the calculation. returns(bool) means that the function return a boolean.

function checkPlayerExists(address player) public view returns(bool){

for(uint256 i = 0; i < players.length; i++){

if(players[i] == player) return true;

}

return false;

}

The next function we are gonna make is the bet() function : first we check if the player is allowed to play, ie. he didn’t already played, and if his bet is higher than the minimal bet. Then, if all the conditions are correct, we set the players infos (with the mapping playerInfo) and we add the player address to the players array.

function bet(uint8 _teamSelected) public payable {

//The first require is used to check if the player already exist

require(!checkPlayerExists(msg.sender));

//The second one is used to see if the value sended by the player is

//Higher than the minum value

require(msg.value >= minimumBet);



//We set the player informations : amount of the bet and selected team

playerInfo[msg.sender].amountBet = msg.value;

playerInfo[msg.sender].teamSelected = _teamSelected;



//then we add the address of the player to the players array

players.push(msg.sender);



//at the end, we increment the stakes of the team selected with the player bet

if ( _teamSelected == 1){

totalBetOne += msg.value;

}

else{

totalBetTwo += msg.value;

}

}

The second core function of this contract is the one for distributing the ethers to the players, with the right amount following the ponderation of the initial bet.

function distributePrizes(uint16 teamWinner) public {

address payable[1000] memory winners;

//We have to create a temporary in memory array with fixed size

//Let's choose 1000

uint256 count = 0; // This is the count for the array of winners

uint256 LoserBet = 0; //This will take the value of all losers bet

uint256 WinnerBet = 0; //This will take the value of all winners bet

address add;

uint256 bet;

address payable playerAddress;

//We loop through the player array to check who selected the winner team

for(uint256 i = 0; i < players.length; i++){

playerAddress = players[i];

//If the player selected the winner team

//We add his address to the winners array

if(playerInfo[playerAddress].teamSelected == teamWinner){

winners[count] = playerAddress;

count++;

}

}

//We define which bet sum is the Loser one and which one is the winner

if ( teamWinner == 1){

LoserBet = totalBetsTwo;

WinnerBet = totalBetsOne;

}

else{

LoserBet = totalBetsOne;

WinnerBet = totalBetsTwo;

}

//We loop through the array of winners, to give ethers to the winners

for(uint256 j = 0; j < count; j++){

// Check that the address in this fixed array is not empty

if(winners[j] != address(0))

add = winners[j];

bet = playerInfo[add].amountBet;

//Transfer the money to the user

winners[j].transfer((bet*(10000+(LoserBet*10000/WinnerBet)))/10000 );

}



delete playerInfo[playerAddress]; // Delete all the players

players.length = 0; // Delete all the players array

LoserBet = 0; //reinitialize the bets

WinnerBet = 0;

totalBetsOne = 0;

totalBetsTwo = 0;

}

Then we add the two getters for the jackpot of Team One and Two, that will be used for the front-end of our front-end

function AmountOne() public view returns(uint256){

return totalBetOne;

}



function AmountTwo() public view returns(uint256){

return totalBetTwo;

}

Our contract is now finished ! here is the final code :

pragma solidity >0.4.99; contract Betting {

address payable public owner;

uint256 public minimumBet;

uint256 public totalBetsOne;

uint256 public totalBetsTwo;

address payable[] public players;

struct Player {

uint256 amountBet;

uint16 teamSelected;

}

// The address of the player and => the user info

mapping(address => Player) public playerInfo;

function() external payable {}



constructor() public {

owner = msg.sender;

minimumBet = 100000000000000;

}

function kill() public {

if(msg.sender == owner) selfdestruct(owner);

}



function checkPlayerExists(address payable player) public view returns(bool){

for(uint256 i = 0; i < players.length; i++){

if(players[i] == player) return true;

}

return false;

}

function bet(uint8 _teamSelected) public payable {

//The first require is used to check if the player already exist

require(!checkPlayerExists(msg.sender));

//The second one is used to see if the value sended by the player is

//Higher than the minimum value

require(msg.value >= minimumBet);

//We set the player informations : amount of the bet and selected team

playerInfo[msg.sender].amountBet = msg.value;

playerInfo[msg.sender].teamSelected = _teamSelected;

//then we add the address of the player to the players array

players.push(msg.sender);

//at the end, we increment the stakes of the team selected with the player bet

if ( _teamSelected == 1){

totalBetsOne += msg.value;

}

else{

totalBetsTwo += msg.value;

}

}

// Generates a number between 1 and 10 that will be the winner

function distributePrizes(uint16 teamWinner) public {

address payable[1000] memory winners;

//We have to create a temporary in memory array with fixed size

//Let's choose 1000

uint256 count = 0; // This is the count for the array of winners

uint256 LoserBet = 0; //This will take the value of all losers bet

uint256 WinnerBet = 0; //This will take the value of all winners bet

address add;

uint256 bet;

address payable playerAddress;

//We loop through the player array to check who selected the winner team

for(uint256 i = 0; i < players.length; i++){

playerAddress = players[i];

//If the player selected the winner team

//We add his address to the winners array

if(playerInfo[playerAddress].teamSelected == teamWinner){

winners[count] = playerAddress;

count++;

}

}

//We define which bet sum is the Loser one and which one is the winner

if ( teamWinner == 1){

LoserBet = totalBetsTwo;

WinnerBet = totalBetsOne;

}

else{

LoserBet = totalBetsOne;

WinnerBet = totalBetsTwo;

}

//We loop through the array of winners, to give ethers to the winners

for(uint256 j = 0; j < count; j++){

// Check that the address in this fixed array is not empty

if(winners[j] != address(0))

add = winners[j];

bet = playerInfo[add].amountBet;

//Transfer the money to the user

winners[j].transfer( (bet*(10000+(LoserBet*10000/WinnerBet)))/10000 );

}



delete playerInfo[playerAddress]; // Delete all the players

players.length = 0; // Delete all the players array

LoserBet = 0; //reinitialize the bets

WinnerBet = 0;

totalBetsOne = 0;

totalBetsTwo = 0;

}

function AmountOne() public view returns(uint256){

return totalBetsOne;

}

function AmountTwo() public view returns(uint256){

return totalBetsTwo;

}

}

Testing the contract

On the Remix IDE, Go on the Run tab on the right, and choose the JavaScript VM Environment. Don’t touch the Gas Limit, and the Value input will be used to enter how many ether you want to bet. Don’t forget to set it to ether to see the balance change easily (the value is initially in wei).

Then select the Betting contract and click on Deploy. Your compiler should look like what’s displayed on the screen.