2.9 Onward and Further

Let’s review what we’ve done through this tutorial:

Despite having done so much, this is really just a brief introduction to what is possible with Reach.

How difficult was all this? Let’s look at the final versions of our programs.

First, let’s look at the Reach program:

 1    'reach 0.1';
 2    
 3    const [ isHand, ROCK, PAPER, SCISSORS ] = makeEnum(3);
 4    const [ isOutcome, B_WINS, DRAW, A_WINS ] = makeEnum(3);
 5    
 6    const winner = (handA, handB) =>
 7          ((handA + (4 - handB)) % 3);
 8    
 9    assert(winner(ROCK, PAPER) == B_WINS);
10    assert(winner(PAPER, ROCK) == A_WINS);
11    assert(winner(ROCK, ROCK) == DRAW);
12    
13    forall(UInt256, handA =>
14      forall(UInt256, handB =>
15        assert(isOutcome(winner(handA, handB)))));
16    
17    forall(UInt256, (hand) =>
18      assert(winner(hand, hand) == DRAW));
19    
20    const Player =
21          { ...hasRandom,
22            getHand: Fun([], UInt256),
23            seeOutcome: Fun([UInt256], Null),
24            informTimeout: Fun([], Null) };
25    const Alice =
26          { ...Player,
27            wager: UInt256 };
28    const Bob =
29          { ...Player,
30            acceptWager: Fun([UInt256], Null) };
31    
32    const DEADLINE = 10;
33    export const main =
34      Reach.App(
35        {},
36        [['Alice', Alice], ['Bob', Bob]],
37        (A, B) => {
38          const informTimeout = () => {
39            each([A, B], () => {
40              interact.informTimeout(); }); };
41    
42          A.only(() => {
43            const wager = declassify(interact.wager); });
44          A.publish(wager)
45            .pay(wager);
46          commit();
47    
48          B.only(() => {
49            interact.acceptWager(wager); });
50          B.pay(wager)
51            .timeout(DEADLINE, () => closeTo(A, informTimeout));
52    
53          var outcome = DRAW;
54          invariant(balance() == 2 * wager && isOutcome(outcome) );
55          while ( outcome == DRAW ) {
56            commit();
57    
58            A.only(() => {
59              const _handA = interact.getHand();
60              const [_commitA, _saltA] = makeCommitment(interact, _handA);
61              const commitA = declassify(_commitA); });
62            A.publish(commitA)
63              .timeout(DEADLINE, () => closeTo(B, informTimeout));
64            commit();
65    
66            unknowable(B, A(_handA, _saltA));
67            B.only(() => {
68              const handB = declassify(interact.getHand()); });
69            B.publish(handB)
70              .timeout(DEADLINE, () => closeTo(A, informTimeout));
71            commit();
72    
73            A.only(() => {
74              const [saltA, handA] = declassify([_saltA, _handA]); });
75            A.publish(saltA, handA)
76              .timeout(DEADLINE, () => closeTo(B, informTimeout));
77            checkCommitment(commitA, saltA, handA);
78    
79            outcome = winner(handA, handB);
80            continue; }
81    
82          assert(outcome == A_WINS || outcome == B_WINS);
83          transfer(2 * wager).to(outcome == A_WINS ? A : B);
84          commit();
85    
86          each([A, B], () => {
87            interact.seeOutcome(outcome); });
88          exit(); });

Next, the JavaScript frontend:

 1    import * as stdlib from '@reach-sh/stdlib/ETH.mjs';
 2    import * as backend from './build/index.main.mjs';
 3    import { ask, yesno, done } from '@reach-sh/stdlib/ask.mjs';
 4    
 5    ( async () => {
 6      const toNetworkFormat = (n) => stdlib.toWeiBigNumber(n, 'ether');
 7    
 8      const isAlice = await ask(
 9        `Are you Alice?`, yesno);
10      const who = isAlice ? 'Alice' : 'Bob';
11    
12      console.log(`Starting Rock, Paper, Scissors! as ${who}`);
13    
14      let acc = null;
15      if ( await ask(
16        `Would you like to create an account? (only possible on devnet)`, yesno) ) {
17        acc = await stdlib.newTestAccount( toNetworkFormat('1000') ); }
18      else {
19        const phrase = await ask(
20          `What is your account mnemonic?`, (x => x));
21        acc = await stdlib.newAccountFromMnemonic(phrase); }
22    
23      let ctc = null;
24      if ( await ask(
25        `Do you want to deploy the contract? (y/n)`, yesno) ) {
26        ctc = await acc.deploy(backend);
27        const info = await ctc.getInfo();
28        console.log(`The contract is deployed as = ${JSON.stringify(info)}`); }
29      else {
30        const info = await ask(
31          `Please paste the contract information:`,
32          JSON.parse );
33        ctc = await acc.attach(backend, info); }
34    
35      const getBalance = async () =>
36            stdlib.fromWei ( await stdlib.balanceOf(acc) );
37    
38      const before = await getBalance();
39      console.log(`Your balance is ${before}`);
40    
41      const interact = { ...stdlib.hasRandom };
42    
43      interact.informTimeout = () => {
44        console.log(`There was a timeout.`);
45        process.exit(1); };
46    
47      if ( isAlice ) {
48        const amt = await ask(
49          `How much do you want to wager?`,
50          toNetworkFormat );
51        interact.wager = amt; }
52      else {
53        interact.acceptWager =
54          async (amt) => {
55            if ( await ask(
56              `Do you accept the wager of ${stdlib.fromWei(amt)}?`,
57              yesno) ) {
58              return; }
59            else {
60              process.exit(0); } } }
61    
62      const HAND = ['Rock', 'Paper', 'Scissors'];
63      const HANDS = {'Rock': 0, 'R': 0, 'r': 0,
64                     'Paper': 1, 'P': 1, 'p': 1,
65                     'Scissors': 2, 'S': 2, 's': 2 };
66      interact.getHand =
67        async () => {
68          const hand = await ask(
69            `What hand will you play?`,
70            (x) => {
71              const hand = HANDS[x];
72              if ( hand == null ) {
73                throw Error(`Not a valid hand ${hand}`); }
74              return hand; } );
75          console.log(`You played ${HAND[hand]}`);
76          return hand; };
77    
78      const OUTCOME = ['Bob wins', 'Draw', 'Alice wins'];
79      interact.seeOutcome =
80        async (outcome) => {
81          console.log(`The outcome is: ${OUTCOME[outcome]}`); };
82    
83      const part = isAlice ? backend.Alice : backend.Bob;
84      await part(stdlib, ctc, interact);
85    
86      const after = await getBalance();
87      console.log(`Your balance is now ${after}`);
88    
89      done();
90    })();

We wrote 88 lines of Reach and 90 lines of JavaScript, or 178 lines together.

Behind the scenes, Reach generated 197 lines of Solidity (which you can look at here: tut-7/build/index.main.sol), as well as 321 lines of JavaScript (which you can look at here: tut-7/build/index.main.mjs). If we weren’t using Reach, then we’d have to write these 518 lines ourselves and ensure that they are consistent and updated at every change to the application.

Now that you’ve seen an entire Reach application from beginning to end, it’s time for you to start working on your own application!

No matter what you decide to read or work on next, we hope you’ll join us on the Discord community. Once you join, message @team, I just completed the tutorial! and we’ll give you the tutorial veteran role, so you can more easily help others work through it!

Thanks for spending your afternoon with us!