Web applications that use ethereum can take advantage of state channels for an improved user experience. Those which connect fixed groups of users for intra-group interactions are the best candidates for scaling with state channels.

In this post I’m going to explain how a state channel wallet can be used to build a decentralized rock paper scissors application. This application uses state channels on ethereum to make the user experience snappy, like a centralized application might be.

🧰 Channels

We have designed State Channels around a primitive called a Channel .

A Channel is an object which contains information on the fixed list of participants involved, the ruleset that defines valid state transitions, the outcome that would appear on-chain if the channel were finalized at its current state, and of course information related to the current state.

When designing an application, you want to understand what kinds of interactions exist between your users and try to model them as Channels.

For example, in a game of Tic Tac Toe, a Channel might be between two players, one taking X and the other O, applying a state transition function which models the logic of Tic Tac Toe. In another example between a livestream user and a livestreamer, a Channel might be a connection between the user and the streamer, where the state transition function simply allows the user to send small micropayments to the livestreamer.

📟 Wallets provide an API for Apps to interact with Channels

When designing an application that uses channels, the application communicates over an API to a state channels wallet to perform basic operations like creating a new channel, updating the state of an existing channel, closing a channel, or even challenging an opponent to respond on-chain for a channel.

Importantly, the wallet is the source-of-truth for all Channel data.

In our current implementation, our example applications (e.g., rps ) communicate to a wallet over Window.postMessage . It is able to do this as, for demo purposes, we run the wallet inside an <iframe> element embedded within the application.

The API could also exist over other communication channels, such as between an application and a Chrome Extension as MetaMask and others have done with window.ethereum .

Without state channels, a web application might rely on data stored in a smart contract by querying a client (e.g., MetaMask) to fetch data in storage of an on-chain contract. It might use this data to render some UI for the app.

With state channels, the application can make a request to a wallet and fetch a Channel object. This object has a property which includes its current state which can be used in a similar way to any on-chain state to render any UI.

The API between an App and a Wallet currently looks like this:

createChannel — to create a new Channel with certain parameters

— to create a new Channel with certain parameters joinChannel — to join a Channel that someone has created

— to join a Channel that someone has created updateState — to update the state of a Channel

— to update the state of a Channel closeChannel — to close a Channel, effectively de-funding it

— to close a Channel, effectively de-funding it challengeChannel — to challenge an unresponsive counterparty on-chain

Additionally, a Wallet might emit an Event that an App would subscribe to:

ChannelProposed — when a created Channel includes you

— when a created Channel includes you ChannelUpdated — when a Channel you’re in has been updated

You can see that there are similarities between the App to Wallet communication protocol and an App to Server protocol in other, centralized contexts.

Note that wallet software is responsible for handling private keys, storing and persisting state, funding channels, determining whether state updates are valid, generating messages to be sent to counterparties, monitoring the blockchain for challenges, and securely executing the state channels protocols. We’ll be publishing more on this soon, but for this blog post we’ll focus on Apps.

✂️ Rock Paper Scissors, an example state channels application

We’ve built an example application implementing a game of decentralized rock paper scissors that uses this Channels API in a web browser. You can play it now on Ropsten, if you’d like, at rps.statechannels.org (or watch this explainer video).

The application does a few interesting things with Channels:

🎨 It renders application UI based on Channel state

The state of our rock paper scissors application is fundamentally dependant on the state of the underlying Channel objects it manipulates. So, depending on the state of the Channel , the UI will prompt the user to select a move to be made, wait for their counterparty to make their move, or play a new game.

Additionally, the application dispatches calls to the Channel API for actions taken by the user. The mapping is quite intuitive:

When a user joins a game advertised in the game lobby, it calls createChannel which produces a Channel for the app to use from then on.

which produces a Channel for the app to use from then on. When a user clicks rock, paper, or scissors, a call is made to updateState .

. When a user launches a challenge a call is made to challengeChannel .

. When a user wishes to end the game a call is made to closeChannel .

Similarly, when the Channel is updated (i.e., your counterparty updated the Channel by making their move in the game), the ChannelUpdated event is emitted and new state is then rendered on the UI.

We’ve visualized all of the usages of the API in the application’s state machine on a nice diagram which you can have a look at here.

🤖 It refers to a game-specific state machine (i.e., AppDefinition )

In this demonstration we’ve written our own custom implementation of rock paper scissors using Solidity. We’ve deployed this contract on Ropsten so the validTransition function is publicly accessible.

The actual implementation uses a commit-reveal scheme to simulate two players revealing their “move” (e.g., “rock”, “paper”, or “scissors”). If you’re unfamiliar with this scheme, you can learn how it works by reading this excellent post on the topic.

👨‍⚖️ It understands challenges

In some applications it might be fine to not receive a state update for a long time. Say, for example, a channel you have open with a friend where you periodically pay each other for picking up the cheque at dinner. It’s fine if you don’t receive a new state update immediately.

In this App, however, you expect your counterparty to make a move within a few seconds. You don’t want your game to last days. So, the application has taken two steps in this direction: