Tutorials
No Results
Tutorial

Intermediate · 1 hour +

Algorand Blockchain Development using Reach - Part 7: React.js Front End and AlgoSigner Integration

In this tutorial, we will add a React User Interface to our Rock Paper Scissors game. By the end you will have a fully functioning dApp to show the world. As of this writing. This dApp has been compiled to. Binance / Matic / Ethereum Core and Algorand blockchains.

Requirements

Background

This tutorial is geared towards front end dApp developers. It will also be of interest to backend developers looking to integrate front ends skills to their bag of tricks.

Steps

Web Interaction

Reach Web applications rely on the Web browser to provide access to a consensus network account and its associated wallet. On Algorand, the standard wallet is AlgoSigner. If you want to test this code, you’ll need to install it and set it up. Furthermore, AlgoSigner does not support multiple active accounts, so if you want to test Rock, Paper, Scissors! locally, you’ll need to have two separate browser instances: one to act as Alice and another to act as Bob.

To get started follow along with developer docs and YouTube videos for reference

We will focus on tut-8/index.js, because tut-8/index.rsh.

The code in this section does not use the scaffolding from the previous section. Reach comes with a convenience command for deleting scaffolded files:

$ ./reach unscaffold

Similarly, you do not need the previous index.mjs file, because we’ll be writing it completely from scratch to use React. You can run the following command to delete it:

$ rm index.mjs

Or, you can copy the index.rsh file into a new directory and work from there.

This code is supplemented with index.css and some views. These details are not specific to Reach, and are fairly trivial, so we will not explain the specifics of those files. If you run this locally, you’ll want to download those files. Your directory should look like:

.

├── index.css

├── index.js

├── index.rsh

└── views

    ├── AppViews.js

    ├── AttacherViews.js

    ├── DeployerViews.js

    ├── PlayerViews.js

    └── render.js


Implementing the Front End with React.js

index.js

import React from 'react';
import AppViews from './views/AppViews';
import DeployerViews from './views/DeployerViews';
import AttacherViews from './views/AttacherViews';
import {renderDOM, renderView} from './views/render';
import './index.css';
import * as backend from './build/index.main.mjs';
import * as reach from '@reach-sh/stdlib/ALGO';

In the above code we import our view code and CSS. We also import the compiled backend. We import the stdlib as reach.

index.js

const handToInt = {'ROCK': 0, 'PAPER': 1, 'SCISSORS': 2};
const intToOutcome = ['Bob wins!', 'Draw!', 'Alice wins!'];
const {standardUnit} = reach;
const defaults = {defaultFundAmt: '10', defaultWager: '3', standardUnit};

In the above code we define a few helpful constants and defaults for later, some corresponding to the enumerations we defined in Reach.

EditorImages/2021/03/12 13:07/ConnectAccount.png


Defining the React Components

We start defining App as a React component, and tell it what to do once it mounts, which is the React term for starting.

index.js

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {view: 'ConnectAccount', ...defaults};
  }
  async componentDidMount() {
    const acc = await reach.getDefaultAccount();
    const balAtomic = await reach.balanceOf(acc);
    const bal = reach.formatCurrency(balAtomic, 4);
    this.setState({acc, bal});
    try {
      const faucet = await reach.getFaucet();
      this.setState({view: 'FundAccount', faucet});
    } catch (e) {
      this.setState({view: 'DeployerOrAttacher'});
    }
  }

index.js

  render() { return renderView(this, AppViews); }
}

In the above code we initialize the component state to display the ConnectAccount view. We then hook into React’s componentDidMount lifecycle event, which is called when the component starts. we use getDefaultAccount, which accesses the default browser account. For example, when used with Algorand, it can discover the currently-selected AlgoSigner account. We use getFaucet to try and access the Reach developer testing network faucet. if getFaucet was successful, we set the component state to display the FundAccount if getFaucet was unsuccessful, we set the component state to skip to the DeployerOrAttacher view. We render the appropriate view

The on the next screen we will need to add our public key or mnemonic to continue with our dApp. Currently, Reach still needs delivered in TEAL 3 for a better user experience.

EditorImages/2021/03/12 13:23/Add_Key.png

Then we will allow our frontend to connect to the Algorand blockchain.

EditorImages/2021/03/12 13:14/Attacher.png

  async fundAccount(fundAmount) {
    await reach.transfer(this.state.faucet, this.state.acc, reach.parseCurrency(fundAmount));
    this.setState({view: 'DeployerOrAttacher'});
  }
  async skipFundAccount() { this.setState({view: 'DeployerOrAttacher'}); }

In the above code, we define what happens when the user clicks the Fund Account button. We transfer funds from the faucet to the user’s account then we set the component state to display the DeployerOrAttacher view. Lastly, we define what to do when the user clicks the Skip button, which is to set the component state to display the DeployerOrAttacher.

index.js

  selectAttacher() { this.setState({view: 'Wrapper', ContentView: Attacher}); }
  selectDeployer() { this.setState({view: 'Wrapper', ContentView: Deployer}); }

In the above code we set a sub-component based on whether the user clicks Deployer or Attacher.

EditorImages/2021/03/12 13:16/DeployerAttacher.png

Next, we will define Player as a React component, which will be extended by the specialized components for Alice and Bob.

Our Web frontend needs to implement the participant interact interface for players, which we defined as:

index.rsh

const Player =
      { ...hasRandom,
        getHand: Fun([], UInt),
        seeOutcome: Fun([UInt], Null),
        informTimeout: Fun([], Null) };

We will provide these callbacks via the React component directly.

class Player extends React.Component {
  random() { return reach.hasRandom.random(); }
  async getHand() { // Fun([], UInt)
    const hand = await new Promise(resolveHandP => {
      this.setState({view: 'GetHand', playable: true, resolveHandP});
    });
    this.setState({view: 'WaitingForResults', hand});
    return handToInt[hand];
  }
  seeOutcome(i) { this.setState({view: 'Done', outcome: intToOutcome[i]}); }
  informTimeout() { this.setState({view: 'Timeout'}); }
  playHand(hand) { this.state.resolveHandP(hand); }
}

Implement the Participant Interfaces

In the above code, we provide the random callback. We then we provide the getHand callback. we set the component state to display the GetHand view, and wait for a Promise which can be resolved via user interaction which occurs after the Promise is resolved, we set the component state to display the WaitingForResults view. we provide the seeOutcome and informTimeout callbacks, which set the component state to display the Done view and the Timeout view. We then define what happens when the user clicks Rock, Paper, or Scissors: The Promise is resolved.

Our Web frontend needs to implement the participant interact interface for Alice, which we defined as:

index.rsh

const Alice =
      { ...Player,
        wager: UInt };

We will provide the wager value, and define some button handlers in order to trigger the deployment of the contract.
EditorImages/2021/03/12 13:19/LaunchContract1.png

Implement the Deployer and Attacher Components

class Deployer extends Player {
  constructor(props) {
    super(props);
    this.state = {view: 'SetWager'};
  }
  setWager(wager) { this.setState({view: 'Deploy', wager}); }
  async deploy() {
    const ctc = this.props.acc.deploy(backend);
    this.setState({view: 'Deploying', ctc});
    this.wager = reach.parseCurrency(this.state.wager); // UInt
    backend.Alice(ctc, this);
    const ctcInfoStr = JSON.stringify(await ctc.getInfo(), null, 2);
    this.setState({view: 'WaitingForAttacher', ctcInfoStr});
  }
  render() { return renderView(this, DeployerViews); }
}

EditorImages/2021/03/12 13:19/LaunchContract2.png

Finishing the React React dApp

We initialize the component state to display the Attach view. We then define what happens when the user clicks the Attach button. we call acc.attach.Then we set the component state to display the Attaching view. We start running the Reach program as Bob, using the this React component as the participant interact interface object. we define the acceptWager callback.
We set the component state to display the AcceptTerms view, and wait for a Promise which can be resolved via user interaction. We define what happens when the user clicks the Accept Terms and Pay Wager button: the Promise is resolved, and we set the component state to display the WaitingForTurn view. We finally render the appropriate view

index.js

    renderDOM(<App />);

Finally, we call a small helper function from render.js to render our App component.

As a convenience for running the React development server, you can call:

REACH_CONNECTOR_MODE=ALGO ../algo_react/reach react

You can also use Reach in your own JavaScript project.

As usual, you can compile your Reach program index.rsh to the backend build artifact build/index.main.mjs with:

../algo_react/reach compile

Now our implementation of Rock, Paper, Scissors! is live in the browser! We can leverage callbacks in the participant interact interface to display to and gather information from the user, through any Web UI framework of our choice.

If we wanted to deploy this application to the world, then we would take the static files that React produces and host them on a Web server. These files embed your compiled Reach program, so there’s nothing more to do than provide them to the world.

This has been an exciting journey. I have enjoyed learning about Algorand and have gained some new friends along the way. I hope you enjoyed learning Reach with me. We are excited to see what you can IMAGINE

Follow this link for the completed source

Reach

dapp

March 12, 2021