This article concludes a series on creating scalable decentralized applications (dApps) and smart contracts in Ethereum using State Channels, focusing on implementing the final functionality to end a game and distribute funds.
Abstract
In the final part of a comprehensive guide, the article delves into the technical aspects of completing a decentralized game on Ethereum using State Channels. It provides a step-by-step explanation of how to implement the "Finish" functionality in a Smart Contract to distribute the winnings based on the final game balances. The author walks through the Solidity code for the Smart Contract, detailing the verifyPlayerBalance function that checks the validity of players' encrypted messages containing their balances and bets. The article also covers the necessary modifications to the client-side and server-side code to facilitate the game's conclusion, including the addition of a "Finish Game" button and the corresponding JavaScript to interact with the Smart Contract. The server's role is simplified to broadcasting the latest game state to the clients for verification. The article emphasizes the importance of understanding the complex interactions within the Smart Contract and encourages developers to apply this knowledge to create their own scalable Ethereum applications.
Opinions
The author believes that understanding the intricate details of the verifyPlayerBalance function is crucial for developers working with State Channels.
There is an emphasis on the necessity of a robust conflict-resolution system in real-world applications to handle disputes between players.
The author suggests that the complexity of the Smart Contract's finish function may require multiple reads to fully comprehend.
The article promotes the idea that developers should test their understanding by creating their own versions of scalable Ethereum applications.
Encouragement is given to join a community of Ethereum developers to share knowledge and contribute to the ecosystem's growth.
The author endorses an AI service as a cost-effective alternative to ChatGPT Plus (GPT-4) for developers interested in AI assistance.
How to create scalable dApps and smart contracts in Ethereum with State Channels step-by-step. Final part 3
In this last part of the series of creating scalable decentralised applications with state channels, you’ll learn how to implement the finishing functionality to end the game and distribute the funds using Smart Contracts.
You’ll also see the final code of the Smart Contract, server and client that will help you understand how it all ties together.
If you haven’t read the past articles, be sure to check them out in order here:
In the part 2 of the series we set up a real-time server where both players exchanged signed and encrypted messages with their respective bets.
We implemented the exchange of messages so what we now need is the finishing functionality where any player will be able to click on “Finish” and both players will receive their Ether bet depending on the final balance after several games.
So if one user won 7 ETH and the other won 2 ETH after 10 games, the first player will receive 7 ETH while the other 2 ETH after closeing the state channel.
You’ll see how to close a State Channel. However we won’t go through conflict-resolution systems in cases where both players disagree with the results since we are using a sever who verifies the information before closing the channel.
In a real application you should implement such functionality to solve situations where players disagree with the final results.
Index
To make the needed changes we’ll have to modify the server, the client and the smart contract:
Solidity Smart Contract code
Client side code
Server side code
1. Solidity Smart Contract code
The first step is to modify our existing Smart Contract called “Dice.sol” to add the needed functions to finish the game. If you remember, this is the code that we developed previously in the last episode:
As you can see, the contract is really simple. It only has 2 functions. The constructor to setup the first player address and escrow and the setupPlayer2() function to store the address and escrow of the second user.
Before creating the finish function, I want you to understand how it will work.
We are using a state channel between 2 users where they exchange encrypted messages that contain the latest balance of the past games combined, the nonce, the dice selected, the bet for the current game, the sequence and the address of the player.
So the way that we create the finish functionality is by taking the 2 latests messages, one per player and uncrypt them with the original data to recognize how much ether each player gets.
The 2 lastest messages contain all the information that we need to recall the final outcome of the game.
Let’s create a verifyPlayerBalance() function which is our finish function. It’s called that way because we will actually verify that the message is valid and then send the corresponding ether to both players. Here’s how it looks like:
And here’s the breakdown. Pay attention to it because it’s quite a complex function and you want to get it right:
First, notice that the function receives 7 arguments:
function verifyPlayerBalance(bytes playerMessage, uint256 playerCall, uint256 playerBet, uint256 playerBalance, uint256 playerNonce, uint256 playerSequence, address addressOfMessage) public {}
We need the signed message, the call, the bet, balance, nonce, sequence and address of the player. Turns out that we have all that information stored on the server so it won’t be a problem.
We require all those parameters because you have to have them to uncrypt the signed message and check if it’s valid.
Then we define some simple require checks with error messages. We make sure that the player 2 is setup, that the signed message is valid with the required length of 65 bytes and that the address of the player used is a valid address set up before.
Next we generate a new encrypted message with the parameters used on the client: the nonce, call, bet, balance and sequence.
After that we use an assembly block to get the values of r, v and s . Those are special variables used for signing a message with your ethereum address. You need them to check if the original signer of the message is the user that you specified. You can copy it as it is since it won’t change soon.
Then we simply retrieve the address used in that message with the global function ecrecover() . We do this to check if the signer is the address that you sent to the function addressOfMessage .
Then, we store in the contract’s state variables the balance, bet and call of the player that signed that message. We need to store that information in the contract because we’ll need it later for distributing the funds.
The thing is that we are using 2 separate calls to the function verifyPlayerBalance() for uncrypting the data of both messages. We can’t uncrypt both messages in 1 single function since the contract would revert because of the size of the function. There would be just too many computations in one execution.
Finally we determine if both balances are setup and if so, we distribute the funds after calculating the outcome of this final game. Remember that each message contained the latest balance and the call of the dice but we didn’t calculate the outcome of that final game so we have to do it in the contract.
Congratulations if you made it all the way here! It’s difficult to understand all those blocks immediately so take your time to understand how this finish function works. The major things that we did inside can be resumed in 3 simple actions or steps:
Check that the parameters are valid.
Generate a new encrypted message to see if the one that you sent is valid.
Distribute the ether of the game balances after both have verified their balance with this function.
Here’s how the final smart contract looks like. Note that we added several state variables at the top so be sure to incorporate them to your code:
When it comes to implementing the finishing functionality in the user interface, we simply have to add a button to allow any user to finish the game at any point and the corresponding javascript code.
After one player has clicked on the “Finish Game” button, the javascript will command the server to “send the 2 latests messages from the game to the smart contract and execute those 2 transactions from the current metamask account”.
Essentially we want to perform the function verifyPlayerBalance() twice with each message to finish the game as soon as possible.
First let’s simply add an html button to be able to end the game. Here’s how the final html game file looks like:
You can see the “Finish Game” button there. I also added some css to that. Here’s the final css used for the game html file. You can copy it for your own game project:
Here’s how the decentralised application looks like after those small changes:
Now here’s the final javascript code, I’ll explain you in a moment the few changes that we made to incorporate the closure functionality:
The sequence is the following:
The user clicks on “Finish Game” which fires the event…
…at line 98 which executes the function finishGame() that we just created at line 101.
2. That function emits the event finish to the socket server. Which, as you’ll see right away in section 3, executes the client event finish-2-messages .
3. The finish-2-messages listener at line 57 gets called with the data of the 2 latest games.
4. Now having all the information in the client, we can execute the contract.verifyPlayerBalance() function of the Smart Contract once for each player’s data. We’ll make 2 transactions that you have to execute with your own gas if you want to end the game. You can see them inside that listener.
5. After both transactions have been mined on the Ethereum network, you’ll see that the escrow and balance played over those games has been correctly distributed to each player’s wallets . So if you won 2 ETH after 3 games and your opponent had 0.3 ETH left after those 3 games, you’ll receive 2 ETH and he’ll receive 0.3 ETH.
Remember that that money is coming from the initial escrow that you both invested in at the beginning of the game.
It’s almost done! Check the final section below to realize how the application wraps up with the server code.
3. Server side code
The server is quite simple. We just added a real-time socket listener that sends the latest game object to the client which as you know, contains the data of the last 2 games.
Let’s put it another way: it contains the balances, signed messages and bets of both players in the last game among other things.
Here’s the small bit of code that we added to the server:
Which takes the games array that contains all the past games and slices the last one to send it to the client. We only need the last one since it has all the data from both players as I explained you before. So it’s 1 object with 2 players’ data.
Then we just emit the event finish-2-messages while sending that object.
Here’s the final server code that you can come back for reference at any point to learn more about how it should look like:
It looks big but almost everything is from the last episode.
Congratulations! You just finished this chapter. If you completed part 1 and part 2 you now know exactly how to create scalable, decentralised applications with State Channels, Smart Contracts and web3.js.
You understand by now how encryption works in Ethereum. The fact that you can bundle several variables in one big chunk of encrypted text that you can sign with your address and verify later on with ecrecover() .
Now put this knowledge to the test and create your own different version of a scalable ethereum application, share it with hundreds of developers just like you in my facebook group https://www.facebook.com/groups/ethereumdevelopers/ and join the exclusive email list of ethereum developers here: http://eepurl.com/dDQ2yX
Thank you for your patience and dedication. Keep improving your skills and remember to share what you learn with others so that the Ethereum ecosystem can grow and develop as the top dApps solution.