How to create scalable dApps and smart contracts in Ethereum with State Channels step-by-step. Part 1
There are lots of different solutions for creating decentralised applications that scale to thousands or even millions of real-time users such as plasma and state channels. In this guide you’ll learn step-by-step how state channels work and how to create scalable applications in Ethereum right now.
You don’t have to wait for future improvements in the blockchain. The technology is here to stay and you can use it for creating all kinds of dApps. Right now, state channels are mostly used for blockchain-based games in Ethereum.
Think gambling with crypto currencies. There are ways to encrypt the information to keep reveal it later which is a key point of this system.
What are state channels?
It’s a scaling solution to create decentralized applications and smart contracts that can be used by millions of users in almost real-time. They work by initiating a channel between 2 or more users in which they exchange encrypted, signed messages with the information of the transaction they want to execute.
They are called “state” because each interaction has to have a state that can be updated. Think about a score of a game or a bank balance.
Why do they exist?
State channels were created because ethereum applications quickly grew in popularity making the blockchain unusable since it was developed with a moderate use. They allow continuous transactions without paying for gas or waiting for miners to process the transactions.
Which means free and fast transactions.
What do we need to set up a state channel?
- At least 2 users that will interact with each other. A channel needs to be opened between 2 or more users. Similar to a chat application.
- A smart contract with the state channel logic that will open and close it.
- If the state channel will be used in a game, an escrow will be required for both users. That escrow in ether will be stored in the smart contract when opening the channel.
- A javascript application that will generate the signed messages that will be exchanged off-chain between the users.
- Metamask or a similar tool for signing messages. Signing messages don’t cost gas and they are executed instantly. It’s required from both users to sign the messages to guarantee that tehy are the ones generating such transaction.
- Email or any external application to exchange those signed messages to make that application possible.
How do they work?
State channel are a bit complex to set up because you have to make sure both players protected in case anything goes wrong, that’s why we need a smart contract. These are the steps:
- In a state channel between 2 users, the first one deploys the smart contract which will “open” the channel.
- The second one executes a function of that smart contract to “join” that state channel”
- Then they can begin exchanging signed messages for the application. Both users have access to a custom javascript application to generate messages with the information that they would do in a smart contract, but off-chain.
- The speed of the transactions depend on how fast each user can create and sign those messages. They keep exchanging messages, playing off-chain until they decide that the game is over.
- When they finish the game, anyone of them can go to the smart contract and execute a function to finish it which will start the phase of “negotiation”.
- In this phase, both users have a timeout of 1 day to upload the latest 2 messages they have to the smart contract. The smart contract checks the latest messages and releases the funds to finish the game based on that information. Each message contains the results of the previous interactions so it’s safe to just check the latest ones.
How can you apply this in a real-world situation?
In this guide I’m going to show you how to create a state channel between 2 users for an Ethereum game. Remember that state channels can be used for any kind of application that has a “state” or a counter. That’s why games are ideal. Because you can track who is winning each game, there’s a state for each game that can be updated.
We’ll create a dice game where player 1 chooses the number of the dice that will come off and player 2 has to guess that number to win. They will be able to play as many games as they want without having to execute transactions on the blockchain. We will also have a web app to show the interface.
This is the index that we’ll follow to create such decentralised application:
- Creating the visual web app. This is the interface, how the game will look like to external users. It will be used as the medium to exchange signed messages for state channels.
- Creating the functionalities required to sign and encrypt messages.
- Creating the smart contract.
1. Creating the visual web app
Before even starting with the code I want to make sure that we clarify the complete details of the web app. How it will look like, what’s the focus of attention.
In this case we want to display similar things for both players. Player 1 will see the 6 faces of dice as images and he’ll have to choose which one will come out, then the second player, will also have to choose between those faces and he’ll be able to see the result.
So the framework will be something like this:
- Player 1 goes to the web app, clicks on a button saying “Start new game”, then he makes a metamask transaction to deploy and set up the smart contract. He receives a smart contract address which he can send to the other player to start the game.
- Player 2 goes to the web app, clicks on a button that says “Join existing game” with the contract address received from player 1, then he makes a metamask transaction to set up the already existing game and sends an escrow.
So let’s start right there. Let’s create a box in the middle of the web app with 2 buttons. Create a folder called dice
and a file inside called index.html
. Here’s the code:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Dice ethereum game</title>
</head>
<body>
<div class="main-content">
<button>Start new game</button>
<button>Join existing game</button>
</div>
</body>
</html>
In that code I just created the basic HTML structure with a div
containing the buttons and a title. Note that the div
has a class called main-content
which we’ll use in a moment.
Let’s make that prettier with some css. Create a file called index.css
with the following code (you can copy and paste this):
body {
font-family: sans-serif;
}.main-content {
margin: auto;
max-width: 500px;
background-color: whitesmoke;
padding: 50px;
border-radius: 10px;
display: grid;
grid-template-rows: 1fr 1fr;
grid-template-columns: 1fr 1fr;
grid-column-gap: 10px;
}.main-content h1 {
grid-column: 1 / span 2;
}.main-content button {
border: none;
color: white;
background-color: #007dff;
padding: 20px;
border-radius: 5px;
cursor: pointer;
}.main-content button:hover {
opacity: 0.8;
}.main-content button:active {
opacity: 0.6;
}
I added a h1
title to the html to make it look better, be sure to update your html by adding the link to the css:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="index.css">
<title>Dice ethereum game</title>
</head>
<body>
<div class="main-content">
<h1>Ethereum Dice</h1>
<button>Start new game</button>
<button>Join existing game</button>
</div>
</body>
</html>
You may have noticed that I’m using the new css grid. That’s because it is mostly available for the big browsers so it’s quite safe to use it at this point since most people will see the css properly.
I decided that the best way to display the next action required from the user is to show a div
in javascript with the required information. So when he clicks on “Start new game” he’ll see a box asking him for how much escrow he wants to set up for the game.
It he clicks on “Join existing game” he’ll be asked for the escrow and contract address of the existing game.
Here’s how the button actions will respond:
To made that possible I created an index.js
file with some javascript logic. Here’s the javascript, be sure to type it with your hands if you want to learn better this:
Let me explain what I did there:
- First I created a function called
start()
which will be executed immediately to wrap up the content so that it’s nice and contained in one big function. - Then I created 2 event listeners that get activated whenever I click the start or join buttons in the html file. One for the
#new-game
button and another for the#join-game
button. I’m usingdocument.querySelector()
which is one of the most powerful ways to select anything in your js code. - Inside those listeners, I show or hide the
div
box of each corresponding element. Basically selecting the boxed withquerySelector
and removing or adding the classhidden
which is setup in css todisplay: none;
.
Then we can connect the js file with our modifie index.html
:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="index.css">
<title>Dice ethereum game</title>
</head>
<body>
<div class="main-content">
<h1>Ethereum Dice</h1>
<button id="new-game">Start new game</button>
<button id="join-game">Join existing game</button> <div class="hidden new-game-setup">
<h3>How much escrow will you use in ETH?</h3>
<input type="number" placeholder="2...">
</div> <div class="hidden join-game-setup">
<h3>What's the smart contract address of the existing game?</h3>
<input type="text" placeholder="0x38dfj39...">
</div> <button id="button-continue" class="hidden">Continue</button>
</div> <script src="index.js"></script>
</body>
</html>
I bolded the new chunks of code added. Following is the updated css to style the new information:
body {
font-family: sans-serif;
}.hidden {
display: none;
}.main-content {
margin: auto;
max-width: 500px;
background-color: whitesmoke;
padding: 50px;
border-radius: 10px;
display: grid;
grid-template-rows: 1fr 80px auto;
grid-template-columns: 1fr 1fr;
grid-column-gap: 10px;
}.main-content h1 {
grid-column: 1 / span 2;
}.main-content button {
border: none;
color: white;
background-color: #007dff;
padding: 20px;
border-radius: 5px;
cursor: pointer;
}.main-content button:hover {
opacity: 0.8;
}.main-content button:active {
opacity: 0.6;
}.main-content button:disabled {
opacity: 0.5;
background-color: grey;
cursor: auto;
}.main-content input {
width: 100%;
border-radius: 10px;
padding: 10px;
border: 1px solid lightgrey;
}.main-content div.new-game-setup, .main-content div.join-game-setup {
grid-column: 1 / span 2;
}#button-continue {
grid-column: 1 / span 2;
margin-top: 20px;
}
The “Continue” button right now does nothing so let’s create that functionality to deploy a new smart contract and open the state channel when a user wants to create a new game in the next section.
2. Creating and connecting the initial Smart Contract
It’s time to create a basic version of the smart contract and connect it with your javascript using web3.js
. For now we only need the constructor and some basic information. Write this code down with your own hands in a new file called Dice.sol
:
pragma solidity 0.4.25;contract Dice {
address public player1;
address public player2;
uint256 public player1Escrow;
uint256 public player2Escrow; constructor() public payable {
require(msg.value > 0);
player1 = msg.sender;
player1Escrow = msg.value;
} function setupPlayer2() public payable {
require(msg.value > 0);
player2 = msg.sender;
player2Escrow = msg.value;
}
}
There are 2 functions, the constructor to setup the address and escrow of the first player and the setupPlayer2()
function to setup the information of the second player.
We want to deploy the contract and execute the constructor with the specified msg.value
right when the user clicks on the “Continue” button. In order to do that, we’ll have to implement web3.js
in our smart contract. Since it’s the main way to communicate with the blockchain on the browser.
Get web3.js in your app folder from here: https://github.com/ethereum/web3.js/blob/develop/dist/web3.js which is the official, update distribution code.
To download it for your project, go to that link, click on raw to see the full code and copy the code to paste it into a new file called web3.js
inside your project folder:
You don’t really have to do that if you are using metamask since metamask injects a version of web3.js for you, but it’s useful to have the web3 library in your project to interact with the blockchain if metamask is not available.
We are using metamask to talk to the blockchain. However it doesn’t work when you open an index.html
file on your browser because the file://
extension is not supported for metamask.
We then need to run a local server that will serve the files to a http://localhost:8080
url since metamask doesn’t work when you open the index.html file directly. To do that, open the terminal and install this:
npm i -g http-server
Then, in your project folder execute http-server
to start a local server for your index.html
:
http-server
That will serve the files on localhost:8080
so that you can access them and inject the web3 from metamask.
With that out of the way, lets focus on deploying the contract that we just created from our web app, right when the user clicks on ‘Continue’.
To deploy a new contract we need the ABI, the constructor parameters and the bytecode. Those are the requirements for web3.js
.
- To generate the ABI go to remix.ethereum.org , paste your code in the main section and click on ABI:
That will copy the ABI code. Go to your project folder and create a file called contractData.js
to paste the code there with a variable called abi
like so:
2. Now we need the bytecode of your smart contract. The bytecode is the compiled smart contract that will be deployed to the blockchain, we need that information to be able to deploy it. To get the bytecode got to remix again and click this button:
And create another variable inside contractData.js
called bytecode
with that information like so:
You can copy that same code if your smart contract is exactly like the one I created above.
Import that javascript file in your html before the index.js file for having the abi and bytecode variables available:
<script src="contractData.js"></script>
<script src="index.js"></script>
Before creating the contract on javascript, we need to add an event listener to the continue button of the “Start new game” section:
What I did there is:
- I added ids
id
to the inputs where the user is asked how much ether he want to put in the escrow and the address of the contract if he’s joining an existing game. - Then I added the javascript import
<script src="contractData.js></script>
aboveindex.js
because we want to have the abi and bytecode available insideindex.js
since it has to be imported first.
Then we add the required logic to make that button work. We will check if the contract address input in the HTML is empty or not.
If it’s not empty, then we’ll assume that the player is starting a new game which interestingly, allows you to start a game by using the join button if you leave the address empty.
Before I show you the entire code, I want to explain you how to deploy a contract using web3.js. It looks simple but I got stuck in some areas.
So when the user clicks on “Start new game”, he gives us the escrow amount in ether and his address, we can deploy a new contract with this function:
Essentially you create the contract instance with the abi
and you execute the method .new()
for that contract with the bytecode.
Then, in the callback, you get an error if any and a result
object. The result
object will contain the .address
of the contract deployed when the transaction is processed by the miners.
Which means that this callback will be executed 2 times. One when you execute the contract creation and another when the address of that contract is available.
You can check when the address of the contract is available with a simple if statement:
if(!result.address) {
// The contract creation has started
} else {
// The contract has been deployed and you can use the address with result.address
}
That’s how you deploy a contract with web3.
But what if you want to access an existing contract on the blockchain?
That’s exactly what we need to “join” a dice game, to create a contract instance. For that purpose we only need the ABI and the address of the contract, the bytecode is not necessary. Here’s how you do it in web3:
Contract = web3.eth.contract(abi)
contractInstance = Contract.at(addressSelected)
After that you can execute functions of that contract like so:
contractInstance.setupPlayer2({
value: web3.toWei(valueSelected),
gas: 4e6
}, (err, result) => {
// Do something after executing the function
})
You only need the instance, the function name, the parameters if any and the callback function.
Now that you understand how the deployment and instantiation of a smart contract work on javascript, I’ll show you the full code of the application:
Ignore everything above, what you have to focus on is in the block of the ‘#button-continue’ listener:
document.querySelector('#button-continue').addEventListener()
Because you only have to care about what happens when the player 1 or player 2 click on the button ‘Continue’. Here’s the breakdown:
- When any player clicks on that button, this event listener is executed
- Inside, I get the values of the inputs to setup the escrow and the address of the deployed contract if the player is joining an existing game. Those are the
valueSelected
andaddressSelected
variables. - Then I create the contract setup variable with the abi which will be needed for both players.
- After that I see if the address of the deployed contract is set or not. If the address is empty, it means that the player clicked on “Start new game” since in that case he won’t see the address input.
- Which means that I deploy a new game or smart contract for that player with the escrow selected.
- That first player will see the address of the smart contract deployed. He will have to share that address with the other player to start a dice game since you need 2 players.
- If he has provided an address, it means that he wants to join an existing game. We can do that by creating an instance of the smart contract using the address of it and then executing the function
setupPlayer2()
. - I’m using the
setInterval
function to check every 1 second if the setup of the player 2 has been completed or not to start the game.
Great! If you made it thus far it means that you are commited and that you are actually learning something. The best part is closer than you think. In the next article you’ll see how to create state channels for your game in javascript to create a scalable Ethereum decentralized application.
Don’t miss out and be the first to read it when it’s completed. Join my exclusive mailing list of Ethereum Developers to receive updates and information directly from me here: http://eepurl.com/dDQ2yX
Part 2 now available right here: https://medium.com/@merunasgrincalaitis/how-to-create-scalable-dapps-and-smart-contracts-in-ethereum-with-state-channels-step-by-step-690f71a9bf2f
If you feel overwhelmed with such advanced information or you are just new to solidity and Ethereum dapps, check my book “Ethereum Developer: Learn Solidity From Scratch” here https://merunas.io/