On this page:
1.1 Decentralized applications
1.2 A minimal Reach program
1.3 Compile
1.4 Verify
1.5 Interface
1.6 Execute
1.7 Web app
1.8 Next steps

1 Overview

This is an informal overview of Reach and the structure of a Reach program. The goal of this document is to give enough technical specifics to help you understand what Reach does, but it isn’t intended as either a tutorial or a reference. When you’re ready to really begin a project, you can start with one of those, or the workshop.

If you have experience with blockchain development using existing tools, we recommend reading this article and a comparison with other development platforms.

A recording of a live workshop that goes over this material is available on YouTube.

1.1 Decentralized applications

DApps are made of multiple agents interacting with each other through some backend consensus network, like Ethereum or Algorand. These agents act on behalf of principals that provide direction and authority through information. These principals might be humans or other autonomous agents or even committees and organizations with their own structure. The consensus network allows these agents to transfer and receive value in the form of network-specific tokens, like ETH or ALGO. The network also allows the creation of "contracts" that ensure that all agents follow the same rules as they take turns computing and publishing values and information. The details of these "contracts" are specific to each consensus network, but they are implicitly trusted by all agents and principals because their operation can be independently verified to match the previously agreed-upon rules.

A single Reach program incorporates all aspects of a DApp:
  • Participant backends are the agents acting on behalf of the principals.

  • Frontends are the technical representation of the interface between the participants and the principals.

  • A contract enforces the rules of the program, including the order of operation.

In Reach, a programmer only needs to specify the actions of participantswhat they do individually and what they do in unison. The Reach compiler automatically derives a contract for the consensus network via a connector that enforces these rules.

1.2 A minimal Reach program

Let’s look at a simple Reach program where two principals, Alice and Bob, interact. In this DApp, Alice has some information that Bob might want and she has an amount of network tokens in mind that she’d like to trade for it.

You can look at the entire example program by visiting overview/index.rsh.

Get language support for Reach in your editor by visiting the guide on editor support.

The main part of the program looks like this:

 1    'reach 0.1';
 2    'use strict';
 4    export const main = Reach.App(() => {
 5     const A = Participant('Alice', {
 6       request: UInt,
 7       info: Bytes(128),
 8     });
 9     const B = Participant('Bob', {
10       want: Fun([UInt], Null),
11       got: Fun([Bytes(128)], Null),
12     });
13     deploy();
15     A.only(() => {
..    // ...body...

The elided lines, 14 through 34, contain the body of the application, which we can divide into four parts.

..    // ...
15    A.only(() => {
16      const request = declassify(interact.request); });
17    A.publish(request);
18    commit();
..    // ...

At this point, Bob’s backend has learned the value of request and can deliver it to Bob’s frontend for his approval. This happens next.

..    // ...
20    B.only(() => {
21      interact.want(request); });
22    B.pay(request);
23    commit();
..    // ...

It’s now Alice’s turn again:

..    // ...
25    A.only(() => {
26      const info = declassify(interact.info); });
27    A.publish(info);
28    transfer(request).to(A);
29    commit();
..    // ...

The only thing left is for Bob’s backend to deliver the information to his frontend.

..    // ...
31      B.only(() => {
32        interact.got(info); });
33      exit();
..    // ...

Reach programmers don’t need to think about details like contract storage, protocol diagrams, state validation, or network details; instead, they can focus exclusively on the business logic of their application.

1.3 Compile

After a Reach programmer writes this application in a file like overview/index.rsh, they could run

  $ reach compile overview/index.rsh

and the build directory will contain a new file named index.main.mjs, which contains a JavaScript implementation of a backend for each participant, as well as the Ethereum bytecode for the contract.

If you are curious, you can take a look at this file by going to overview/build/index.main.mjs. The Ethereum bytecode is not readable, but if you understand Solidity, you may want to look at overview/build/index.main.sol to see the original Solidity source that it is compiled from. Reach can leave files like these in place when run with --intermediate-files.

For this thirty line application, the Reach compiler generated hundreds of lines of JavaScript code in two functions, one for Alice and one for Bob. Separately, it generated hundreds more lines of Solidity code to implement the contract. If a programmer wasn’t using Reach, they would have to write all this code in these three modules individually and keep them synchronized at every step of the development process.

Moreover, Reach doesn’t only work for Ethereum: it is blockchain agnostic and can be easily configured to use a different connector to target a different consensus network, like Algorand. Nor is Reach tied to JavaScript: it can be configured to target other backend languages, like Go.

1.4 Verify

Reach doesn’t just compile your program: it also verifies it and ensures that entire categories of errors don’t occur. For example, it always guarantees that the balance in the contract at the end of the program is zero. This is important because if it were not true, then tokens would be locked away by the contract and inaccessible.

For this example program, it is obvious that when a single transfer of request goes in at line 22 and a single transfer of request goes out at line 28, then the balance is zero at the end of the program. We could make a small tweak, however, to demonstrate things going wrong.

Let’s change the third step to leave a single unit in the balance:

..    // ...
25    A.only(() => {
26      const info = declassify(interact.info); });
27    A.publish(info);
28    transfer(request-1).to(A); // <--- Oops!
29    commit();
..    // ...

And then run the compiler:

  $ reach compile overview/index-error.rsh

It will print out a detailed error message showing the violation.

..    // ...

 2    Verifying for generic connector

 3      Verifying when ALL participants are honest

 4    Verification failed:

 5      when ALL participants are honest

 6      of theorem: assert

 7      msg: "balance zero at application exit"

 8      at ./index-error.rsh:33:7:application


10      // Violation Witness


12      const v58 = "Alice".interact.request;

13      //    ^ could = 1

14      //      from: ./index-error.rsh:5:24:application


16      // Theorem Formalization


18      const v87 = 0 == (v58 - (v58 - 1));

19      //    ^ would be false

20      assert(v87);


22      Verifying when NO participants are honest

23      Verifying when ONLY "Alice" is honest

24      Verifying when ONLY "Bob" is honest

25    Checked 17 theorems; 4 failures (and 3 omitted repeats) :'(

Verification failures include a lot of information, such as a concrete counter-example showing values that could have been provided by frontends that would lead to the property failing to hold. In this case, it reports that if Alice were to pass an interact.request over 1 at the start of the program on line 5, then the balance of the contract would not be provably 0 at the end of the program.

Reach programmers don’t need to worry about entire categories of errors because the compiler automatically checks their code and ensures that those errors aren’t present. Of course, there’s a lot more to say about the details of automatic verification; indeed, it is one of the most powerful features of Reach, but we’ll leave it at that for now.

1.5 Interface

The backend produced by the Reach compiler isn’t an application on its own. In particular, each participant needs a frontend to interact with. In a real deployment, this interfacing code would be tied to a GUI, like a Web or smartphone app. Let’s look at a simple command-line version that demonstrates how it would work for testing on a private devnet.

You can look at the entire example interface program by visiting overview/index.mjs.

The program is just a few dozen lines long and the shell of it is quite simple:

 1    import { loadStdlib } from '@reach-sh/stdlib';
 2    import * as backend from './build/index.main.mjs';
 4    (async () => {
 5      const stdlib = await loadStdlib(process.env);
 7      const accAlice = await stdlib.newTestAccount(stdlib.parseCurrency(5));
 8      const accBob = await stdlib.newTestAccount(stdlib.parseCurrency(10));
10      const ctcAlice = accAlice.deploy(backend);
11      const ctcBob = accBob.attach(backend, ctcAlice.getInfo());
13      await Promise.all([
14        backend.Alice(ctcAlice, {
15          request: stdlib.parseCurrency(5),
16          info: 'If you wear these, you can see beyond evil illusions.'
17        }),
18        backend.Bob(ctcBob, {
19          want: (amt) => console.log(`Alice asked Bob for ${stdlib.formatCurrency(amt)}`),
20          got: (secret) => console.log(`Alice's secret is: ${secret}`),
21        }),
22      ]);
23    })();

This code, similar for all test programs, demonstrates how straightforward it is to scaffold a Reach application for testing.

Let’s look at initializing and interfacing each participant, starting with Alice.

..    // ...
14    backend.Alice(ctcAlice, {
15      request: stdlib.parseCurrency(5),
16      info: 'If you wear these, you can see beyond evil illusions.'
17    }),
..    // ...

Let’s look at Bob next.

..    // ...
18    backend.Bob(ctcBob, {
19      want: (amt) => console.log(`Alice asked Bob for ${stdlib.formatCurrency(amt)}`),
20      got: (secret) => console.log(`Alice's secret is: ${secret}`),
21    }),
..    // ...

Reach completely abstracts all the details of the chosen consensus network from the programmer, except for those directly impinging on business decisions, like the amounts of currency transacted. Reach allows programmers to focus on the business logic of their application at every stage, from the core application to the interfacing elements.

1.6 Execute

It’s now time to execute this test program and ensure that everything is working correctly. In this case, we’ve set up our application simply: there’s one Reach file for the application and one JavaScript file for the interface. This is a common practice, so Reach comes with a simple wrapper script to build and execute such applications. We just run:

  $ reach run

And then Reach

On typical developer laptops, this entire process takes seconds and can be completely integrated into existing development IDEs, like VSCode, so Reach developers can compile, verify, build, launch, and test their Reach app with a single command.

Reach completely abstracts all the details of building and maintaining consensus network test environments and build scripts from the programmer, so they can focus exclusively on the business logic of their application. In fact, Reach works for multiple networks, so if we instead run


then Reach will start up a private Algorand devnet and use the Algorand connector. The developer does not need to change anything about their program because Reach is entirely agnostic to the consensus network choice during deployment.

The same goes for Conflux:


1.7 Web app

You can watch a 7-minute video on YouTube which demonstrates this section’s code in action and provides a brief explanation of how it works.

The previous section uses Node.js to perform a test run at the command line. However, most Reach developers deploy their DApps via a Web application, as we describe below.

A Web deployment uses the exact same index.rsh file connected, this time, to a React-based index.js file. (It also uses some simple React views and css to go with it.) Let’s take a look at some snippets from the React index.js and compare with the Node.js index.mjs from before:

...    // ...
  7    import * as backend from './build/index.main.mjs';
  8    import {loadStdlib} from '@reach-sh/stdlib';
  9    const reach = loadStdlib(process.env);
...    // ...

At the top of the file, we import the Reach-generated backend as backend and we load the standard library as reach.

...    // ...
 27    async componentDidMount() { // from mode: ConnectAccount
 28      const acc = await reach.getDefaultAccount();
...    // ...

We hook into the App component’s lifecycle event componentDidMount in order to fetch the user’s account. getDefaultAccount automatically interacts with browser extensions, like MetaMask, to get the user’s currently-selected account. Reach is able to deploy contracts and send transactions to the consensus network by prompting the user directly through the extension’s API, without additional assistance from the React frontend. This is just like how in the Node.js deployment, the Reach programmer does not need to decode the details of the underlying consensus network’s interaction API.

...    // ...
 71    async deploy() { // from mode: Deploy
 72      const ctc = this.props.acc.deploy(backend);
 73      this.setState({mode: 'EnterInfo', ctc});
 74      const ctcInfoStr = JSON.stringify(await ctc.getInfo(), null, 2);
 75      this.setState({ctcInfoStr});
 76    }
...    // ...

Our React component has a method called deploy that actually deploys the contract on the network, using the same calls as in the test deployment: on line 72 we call the acc.deploy function, and on line 74, we call the ctc.getInfo function; exactly as we did for the Node.js program.

...    // ...
 79    async runBackend() { // from mode: RunBackend
 80      const {ctc, requestStandard, info} = this.state;
 81      this.setState({mode: 'BackendRunning'});
 82      const request = reach.parseCurrency(requestStandard);
 83      await backend.Alice(ctc, {request, info});
 84      this.setState({mode: 'BackendRan'});
 85    }
...    // ...

Similarly, we implement a runBackend method that executes the Reach program as Alice using information gathered from the React UI.

...    // ...
112    async runBackend(ctcInfoStr) { // from mode: RunBackend
113      const ctcInfo = JSON.parse(ctcInfoStr);
114      const ctc = this.props.acc.attach(backend, ctcInfo);
115      this.setState({mode: 'ApproveRequest'});
116      const interact = {
117        want: (request) => this.setState({mode: 'DisplayInfo', requestStandard: reach.formatCurrency(request, 4)}),
118        got: (info) => this.setState({info}),
119      };
120      await backend.Bob(ctc, interact);
121    }
...    // ...

We implement a similar method in the Bob component that runs the backend as Bob.

We specify Alice’s and Bob’s respective participant interact interfaces just as we would in Node.js. In the React program, we have the ability to leverage Bob’s interact functions as callbacks that can update the React state in order to display to, or harvest information from, the React user interface.

You can install the @reachsh/stdlib JavaScript library into your React project, or for convenience, instead of setting up a React project, you can simply use the command

  $ reach react

This command runs your DApp with the React development server in a Docker container which has Reach and React JavaScript dependencies pre-installed, so it starts up much faster than building them yourself.

As before, you can use REACH_CONNECTOR_MODE to choose your desired connector.


1.8 Next steps

In this overview, we’ve briefly described the structure and fundamental concepts of a Reach application. We’ve shown how to construct a simple program, compile it, connect an interface, test at the command-line, and deploy it using a React Web application. Since this is only a brief overview of what Reach can do, we left a lot out. But even so, it should be clear why Reach is the easiest and safest programming language for decentralized application development.

Furthermore, this example program has many flaws and should not be used in practice. For example, it provides no protection to Bob in the event that Alice fails to deliver the information, and makes no attempt to ensure that the information is what he wants. Reach allows you to abstract away the low-level details of your decentralized program and focus on these sorts of bigger picture issues. In the rest of the guide, we discuss design issues like this. For example,

However, unless you’re ready to dive deep now, the next steps for you are to:

Thanks for being part of Reach!