[Reach教程翻译] 2.10 石头剪刀布的总结!

石头剪刀布的教程至此完整啦! 这篇文章提供了完整地总结与更多学习资料的整理。欢迎留言感兴趣的话题,点播后续的文章内容!

*[Reach教程翻译] Reach是安全简单的Dapp开发语言 让用户可以像开发传统App一样开发DApp 目前使用Reach开发的智能合约可以部署在以太坊、Conflux、Algorand [Reach官网](https://reach.sh/) [Reach官方文挡](https://docs.reach.sh/)* ## 2.10 石头剪刀布的总结! [原文链接](https://docs.reach.sh/tut-10.html) 让我们回顾一下本教程所做的工作: 在 [2.1 安装与初始化](https://learnblockchain.cn/article/3207) 中,我们展示了在不同系统下如何通过令命行安装 Reach,这个过程对多数开发者而言都非常容易。 在 [2.2 创建DApp脚手架](https://learnblockchain.cn/article/3209) 中,我们展示了一个简单的 Reach 框架,可以发现不管要部署在哪个共识网络,这个框架都不用改动。 在 [2.3 石头剪刀布](https://learnblockchain.cn/article/3216) 中,我们可以发现使用 Reach 的开发只要关注DApp的业务逻辑就好了,不用在乎与不同区块链交互和协议设计的麻烦而琐碎的问题。 在 [2.4 打赌与下注](https://learnblockchain.cn/article/3230) 中,我们展示了使用 Reach 处理代币与交易就像简单的发送数据一样。 在 [2.5 信任与约定](https://learnblockchain.cn/article/3234) 中,我们介绍了[Reach自动验证引擎](https://docs.reach.sh/guide-assert.html),他能替我们的程序发现并避免很大一部份的安全性漏洞。 在 [2.6 超时与不参与](https://learnblockchain.cn/article/3240) 中,我们介绍了该如何使用 Reach 应对参与者迟迟不响应的行为,从而防止资金锁定在合约中。 在 [2.7 平手则继续猜拳直至分出胜负](https://learnblockchain.cn/article/3249)**中,我们展示了如何通过While循环让程序可以不断的交互。 在 [2.8 玩家交互、自主运行游戏](https://learnblockchain.cn/article/3252) 中,我们展示了除了使用 Reach 的测试环境外,我们如何将可交互的 Reach 程序部署到真实共识网络上。 在 [2.9 通过React实现游戏网页交互](https://learnblockchain.cn/article/3264) 中,我们展示了如何将 Reach 程序部署成一个完整的 DApp! 即使已经做了这么多工作,这仅仅只是关于 Reach 功能的一个简单介绍。 上述 9 个小节完成的最终版本其实很简单,让我们看看程序的最终版本。 首先,让我们看看 Reach 程序: [rps-8-interact/index.rsh](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/index.rsh) ``` 'reach 0.1'; const [ isHand, ROCK, PAPER, SCISSORS ] = makeEnum(3); const [ isOutcome, B_WINS, DRAW, A_WINS ] = makeEnum(3); const winner = (handAlice, handBob) => ((handAlice + (4 - handBob)) % 3); assert(winner(ROCK, PAPER) == B_WINS); assert(winner(PAPER, ROCK) == A_WINS); assert(winner(ROCK, ROCK) == DRAW); forall(UInt, handAlice => forall(UInt, handBob => assert(isOutcome(winner(handAlice, handBob))))); forall(UInt, (hand) => assert(winner(hand, hand) == DRAW)); const Player = { ...hasRandom, getHand: Fun([], UInt), seeOutcome: Fun([UInt], Null), informTimeout: Fun([], Null), }; export const main = Reach.App(() => { const Alice = Participant('Alice', { ...Player, wager: UInt, // atomic units of currency deadline: UInt, // time delta (blocks/rounds) }); const Bob = Participant('Bob', { ...Player, acceptWager: Fun([UInt], Null), }); deploy(); const informTimeout = () => { each([Alice, Bob], () => { interact.informTimeout(); }); }; Alice.only(() => { const wager = declassify(interact.wager); const deadline = declassify(interact.deadline); }); Alice.publish(wager, deadline) .pay(wager); commit(); Bob.only(() => { interact.acceptWager(wager); }); Bob.pay(wager) .timeout(relativeTime(deadline), () => closeTo(Alice, informTimeout)); var outcome = DRAW; invariant( balance() == 2 * wager && isOutcome(outcome) ); while ( outcome == DRAW ) { commit(); Alice.only(() => { const _handAlice = interact.getHand(); const [_commitAlice, _saltAlice] = makeCommitment(interact, _handAlice); const commitAlice = declassify(_commitAlice); }); Alice.publish(commitAlice) .timeout(relativeTime(deadline), () => closeTo(Bob, informTimeout)); commit(); unknowable(Bob, Alice(_handAlice, _saltAlice)); Bob.only(() => { const handBob = declassify(interact.getHand()); }); Bob.publish(handBob) .timeout(relativeTime(deadline), () => closeTo(Alice, informTimeout)); commit(); Alice.only(() => { const saltAlice = declassify(_saltAlice); const handAlice = declassify(_handAlice); }); Alice.publish(saltAlice, handAlice) .timeout(relativeTime(deadline), () => closeTo(Bob, informTimeout)); checkCommitment(commitAlice, saltAlice, handAlice); outcome = winner(handAlice, handBob); continue; } assert(outcome == A_WINS || outcome == B_WINS); transfer(2 * wager).to(outcome == A_WINS ? Alice : Bob); commit(); each([Alice, Bob], () => { interact.seeOutcome(outcome); }); }); ``` 还有以 Javascript 编写的命令行版本的前端 [rps-8-interact/index.mjs](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/index.mjs) ```javascript import { loadStdlib } from '@reach-sh/stdlib'; import * as backend from './build/index.main.mjs'; import { ask, yesno, done } from '@reach-sh/stdlib/ask.mjs'; const stdlib = loadStdlib(process.env); (async () => { const isAlice = await ask( `Are you Alice?`, yesno ); const who = isAlice ? 'Alice' : 'Bob'; console.log(`Starting Rock, Paper, Scissors! as ${who}`); let acc = null; const createAcc = await ask( `Would you like to create an account? (only possible on devnet)`, yesno ); if (createAcc) { acc = await stdlib.newTestAccount(stdlib.parseCurrency(1000)); } else { const secret = await ask( `What is your account secret?`, (x => x) ); acc = await stdlib.newAccountFromSecret(secret); } let ctc = null; if (isAlice) { ctc = acc.contract(backend); ctc.getInfo().then((info) => { console.log(`The contract is deployed as = ${JSON.stringify(info)}`); }); } else { const info = await ask( `Please paste the contract information:`, JSON.parse ); ctc = acc.contract(backend, info); } const fmt = (x) => stdlib.formatCurrency(x, 4); const getBalance = async () => fmt(await stdlib.balanceOf(acc)); const before = await getBalance(); console.log(`Your balance is ${before}`); const interact = { ...stdlib.hasRandom }; interact.informTimeout = () => { console.log(`There was a timeout.`); process.exit(1); }; if (isAlice) { const amt = await ask( `How much do you want to wager?`, stdlib.parseCurrency ); interact.wager = amt; interact.deadline = { ETH: 100, ALGO: 100, CFX: 1000 }[stdlib.connector]; } else { interact.acceptWager = async (amt) => { const accepted = await ask( `Do you accept the wager of ${fmt(amt)}?`, yesno ); if (!accepted) { process.exit(0); } }; } const HAND = ['Rock', 'Paper', 'Scissors']; const HANDS = { 'Rock': 0, 'R': 0, 'r': 0, 'Paper': 1, 'P': 1, 'p': 1, 'Scissors': 2, 'S': 2, 's': 2, }; interact.getHand = async () => { const hand = await ask(`What hand will you play?`, (x) => { const hand = HANDS[x]; if ( hand == null ) { throw Error(`Not a valid hand ${hand}`); } return hand; }); console.log(`You played ${HAND[hand]}`); return hand; }; const OUTCOME = ['Bob wins', 'Draw', 'Alice wins']; interact.seeOutcome = async (outcome) => { console.log(`The outcome is: ${OUTCOME[outcome]}`); }; const part = isAlice ? ctc.p.Alice : ctc.p.Bob; await part(interact); const after = await getBalance(); console.log(`Your balance is now ${after}`); done(); })(); ``` 最后是 Web 前端: [rps-9-web/index.js](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-9-web/index.js) ```javascript 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 {loadStdlib} from '@reach-sh/stdlib'; const reach = loadStdlib(process.env); 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}; 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}); if (await reach.canFundFromFaucet()) { this.setState({view: 'FundAccount'}); } else { this.setState({view: 'DeployerOrAttacher'}); } } async fundAccount(fundAmount) { await reach.fundFromFaucet(this.state.acc, reach.parseCurrency(fundAmount)); this.setState({view: 'DeployerOrAttacher'}); } async skipFundAccount() { this.setState({view: 'DeployerOrAttacher'}); } selectAttacher() { this.setState({view: 'Wrapper', ContentView: Attacher}); } selectDeployer() { this.setState({view: 'Wrapper', ContentView: Deployer}); } render() { return renderView(this, AppViews); } } 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); } } 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 this.deadline = {ETH: 10, ALGO: 100, CFX: 1000}[reach.connector]; // UInt backend.Alice(ctc, this); const ctcInfoStr = JSON.stringify(await ctc.getInfo(), null, 2); this.setState({view: 'WaitingForAttacher', ctcInfoStr}); } render() { return renderView(this, DeployerViews); } } class Attacher extends Player { constructor(props) { super(props); this.state = {view: 'Attach'}; } attach(ctcInfoStr) { const ctc = this.props.acc.attach(backend, JSON.parse(ctcInfoStr)); this.setState({view: 'Attaching'}); backend.Bob(ctc, this); } async acceptWager(wagerAtomic) { // Fun([UInt], Null) const wager = reach.formatCurrency(wagerAtomic, 4); return await new Promise(resolveAcceptedP => { this.setState({view: 'AcceptTerms', wager, resolveAcceptedP}); }); } termsAccepted() { this.state.resolveAcceptedP(); this.setState({view: 'WaitingForTurn'}); } render() { return renderView(this, AttacherViews); } } renderDOM(<App />); ``` 我们总共写了约 100 行的 Reach 代码,并且有两个不同的前端,分别是命令行版本和网页版本的,他们各自也都大约只有 100 行。 在这背后,Reach 生成了几百行的 Solidity 代码(可以查看文件:[rps-8-interact/build/index.main.sol](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/build/index.main.sol)),近 2000 行的 TEAL 代码(可以查看文件:[rps-8-interact/build/index.main.appApproval.teal](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/build/index.main.appApproval.teal)),以及超过 1000 行的 JavaScript 代码(可以查看文件:[rps-8-interact/build/index.main.mjs](https://github.com/reach-sh/reach-lang/blob/master/examples/rps-8-interact/build/index.main.mjs))。如果我们不是使用 Reach,那么我们就必须自己编写这几千行代码,并确保它们在每次改动时彼此同步、相容,那将是一个很复杂的任务! 现在我们已经从头到尾的带着你写出一个完整的 Reach 应用程序了,该是你开发自己的应用程序的时候啦! 无论如何,我们非常希望你能加入我们的[ Discord 社区](https://discord.gg/AZsgcXu)。一旦你加入 Discord 社区,发信息`@team, I just completed the tutorial!` 我们就会赋予你`tutorial veteran`的角色,这样你就可以帮助其他人学习本教程! 感谢您和我们一起度过学习这个教程的美好时光! [文挡](https://docs.reach.sh/)内还有很多值得阅读的部份, 比如: [工作坊](https://docs.reach.sh/workshop.html)内有更多的应用案例,并且不断持续更新。 [指南](https://docs.reach.sh/guide.html)部份有关于各种不同主题与概念的详细讨论。 [参考资料](https://docs.reach.sh/ref.html)中则可以找到全部语法与概念仔细的说明。 **我们也会持续与大家分享,也欢迎各位读者加入微信群与更多开发者一起讨论、学习。 有想要了解的主题也欢迎留言点播!!!** 欢迎关注Reach微信公众号 并在公众号目录 -> 加入群聊 选择加入官方开发者微信群与官方Discord群 与更多Reach开发者一起学习交流! ![_.png](https://img.learnblockchain.cn/attachments/2021/11/IslW4w8i6192082848c1a.png)

[Reach教程翻译] Reach是安全简单的Dapp开发语言 让用户可以像开发传统App一样开发DApp 目前使用Reach开发的智能合约可以部署在以太坊、Conflux、Algorand Reach官网 Reach官方文挡

2.10 石头剪刀布的总结!

原文链接

让我们回顾一下本教程所做的工作:

在 2.1 安装与初始化 中,我们展示了在不同系统下如何通过令命行安装 Reach,这个过程对多数开发者而言都非常容易。

在 2.2 创建DApp脚手架 中,我们展示了一个简单的 Reach 框架,可以发现不管要部署在哪个共识网络,这个框架都不用改动。

在 2.3 石头剪刀布 中,我们可以发现使用 Reach 的开发只要关注DApp的业务逻辑就好了,不用在乎与不同区块链交互和协议设计的麻烦而琐碎的问题。

在 2.4 打赌与下注 中,我们展示了使用 Reach 处理代币与交易就像简单的发送数据一样。

在 2.5 信任与约定 中,我们介绍了Reach自动验证引擎,他能替我们的程序发现并避免很大一部份的安全性漏洞。

在 2.6 超时与不参与 中,我们介绍了该如何使用 Reach 应对参与者迟迟不响应的行为,从而防止资金锁定在合约中。

在 2.7 平手则继续猜拳直至分出胜负**中,我们展示了如何通过While循环让程序可以不断的交互。

在 2.8 玩家交互、自主运行游戏 中,我们展示了除了使用 Reach 的测试环境外,我们如何将可交互的 Reach 程序部署到真实共识网络上。

在 2.9 通过React实现游戏网页交互 中,我们展示了如何将 Reach 程序部署成一个完整的 DApp!

即使已经做了这么多工作,这仅仅只是关于 Reach 功能的一个简单介绍。

上述 9 个小节完成的最终版本其实很简单,让我们看看程序的最终版本。

首先,让我们看看 Reach 程序:

rps-8-interact/index.rsh

'reach 0.1';

const [ isHand, ROCK, PAPER, SCISSORS ] = makeEnum(3);
const [ isOutcome, B_WINS, DRAW, A_WINS ] = makeEnum(3);

const winner = (handAlice, handBob) =>
  ((handAlice + (4 - handBob)) % 3);

assert(winner(ROCK, PAPER) == B_WINS);
assert(winner(PAPER, ROCK) == A_WINS);
assert(winner(ROCK, ROCK) == DRAW);

forall(UInt, handAlice =>
  forall(UInt, handBob =>
    assert(isOutcome(winner(handAlice, handBob)))));

forall(UInt, (hand) =>
  assert(winner(hand, hand) == DRAW));

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

export const main = Reach.App(() => {
  const Alice = Participant('Alice', {
    ...Player,
    wager: UInt, // atomic units of currency
    deadline: UInt, // time delta (blocks/rounds)
  });
  const Bob   = Participant('Bob', {
    ...Player,
    acceptWager: Fun([UInt], Null),
  });
  deploy();

  const informTimeout = () => {
    each([Alice, Bob], () => {
      interact.informTimeout();
    });
  };

  Alice.only(() => {
    const wager = declassify(interact.wager);
    const deadline = declassify(interact.deadline);
  });
  Alice.publish(wager, deadline)
    .pay(wager);
  commit();

  Bob.only(() => {
    interact.acceptWager(wager);
  });
  Bob.pay(wager)
    .timeout(relativeTime(deadline), () => closeTo(Alice, informTimeout));

  var outcome = DRAW;
  invariant( balance() == 2 * wager && isOutcome(outcome) );
  while ( outcome == DRAW ) {
    commit();

    Alice.only(() => {
      const _handAlice = interact.getHand();
      const [_commitAlice, _saltAlice] = makeCommitment(interact, _handAlice);
      const commitAlice = declassify(_commitAlice);
    });
    Alice.publish(commitAlice)
      .timeout(relativeTime(deadline), () => closeTo(Bob, informTimeout));
    commit();

    unknowable(Bob, Alice(_handAlice, _saltAlice));
    Bob.only(() => {
      const handBob = declassify(interact.getHand());
    });
    Bob.publish(handBob)
      .timeout(relativeTime(deadline), () => closeTo(Alice, informTimeout));
    commit();

    Alice.only(() => {
      const saltAlice = declassify(_saltAlice);
      const handAlice = declassify(_handAlice);
    });
    Alice.publish(saltAlice, handAlice)
      .timeout(relativeTime(deadline), () => closeTo(Bob, informTimeout));
    checkCommitment(commitAlice, saltAlice, handAlice);

    outcome = winner(handAlice, handBob);
    continue;
  }

  assert(outcome == A_WINS || outcome == B_WINS);
  transfer(2 * wager).to(outcome == A_WINS ? Alice : Bob);
  commit();

  each([Alice, Bob], () => {
    interact.seeOutcome(outcome);
  });
});

还有以 Javascript 编写的命令行版本的前端

rps-8-interact/index.mjs

import { loadStdlib } from '@reach-sh/stdlib';
import * as backend from './build/index.main.mjs';
import { ask, yesno, done } from '@reach-sh/stdlib/ask.mjs';
const stdlib = loadStdlib(process.env);

(async () => {
  const isAlice = await ask(
    `Are you Alice?`,
    yesno
  );
  const who = isAlice ? 'Alice' : 'Bob';

  console.log(`Starting Rock, Paper, Scissors! as ${who}`);

  let acc = null;
  const createAcc = await ask(
    `Would you like to create an account? (only possible on devnet)`,
    yesno
  );
  if (createAcc) {
    acc = await stdlib.newTestAccount(stdlib.parseCurrency(1000));
  } else {
    const secret = await ask(
      `What is your account secret?`,
      (x => x)
    );
    acc = await stdlib.newAccountFromSecret(secret);
  }

  let ctc = null;
  if (isAlice) {
    ctc = acc.contract(backend);
    ctc.getInfo().then((info) => {
      console.log(`The contract is deployed as = ${JSON.stringify(info)}`); });
  } else {
    const info = await ask(
      `Please paste the contract information:`,
      JSON.parse
    );
    ctc = acc.contract(backend, info);
  }

  const fmt = (x) => stdlib.formatCurrency(x, 4);
  const getBalance = async () => fmt(await stdlib.balanceOf(acc));

  const before = await getBalance();
  console.log(`Your balance is ${before}`);

  const interact = { ...stdlib.hasRandom };

  interact.informTimeout = () => {
    console.log(`There was a timeout.`);
    process.exit(1);
  };

  if (isAlice) {
    const amt = await ask(
      `How much do you want to wager?`,
      stdlib.parseCurrency
    );
    interact.wager = amt;
    interact.deadline = { ETH: 100, ALGO: 100, CFX: 1000 }[stdlib.connector];
  } else {
    interact.acceptWager = async (amt) => {
      const accepted = await ask(
        `Do you accept the wager of ${fmt(amt)}?`,
        yesno
      );
      if (!accepted) {
        process.exit(0);
      }
    };
  }

  const HAND = ['Rock', 'Paper', 'Scissors'];
  const HANDS = {
    'Rock': 0, 'R': 0, 'r': 0,
    'Paper': 1, 'P': 1, 'p': 1,
    'Scissors': 2, 'S': 2, 's': 2,
  };

  interact.getHand = async () => {
    const hand = await ask(`What hand will you play?`, (x) => {
      const hand = HANDS[x];
      if ( hand == null ) {
        throw Error(`Not a valid hand ${hand}`);
      }
      return hand;
    });
    console.log(`You played ${HAND[hand]}`);
    return hand;
  };

  const OUTCOME = ['Bob wins', 'Draw', 'Alice wins'];
  interact.seeOutcome = async (outcome) => {
    console.log(`The outcome is: ${OUTCOME[outcome]}`);
  };

  const part = isAlice ? ctc.p.Alice : ctc.p.Bob;
  await part(interact);

  const after = await getBalance();
  console.log(`Your balance is now ${after}`);

  done();
})();

最后是 Web 前端:

rps-9-web/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 {loadStdlib} from '@reach-sh/stdlib';
const reach = loadStdlib(process.env);

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};

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});
    if (await reach.canFundFromFaucet()) {
      this.setState({view: 'FundAccount'});
    } else {
      this.setState({view: 'DeployerOrAttacher'});
    }
  }
  async fundAccount(fundAmount) {
    await reach.fundFromFaucet(this.state.acc, reach.parseCurrency(fundAmount));
    this.setState({view: 'DeployerOrAttacher'});
  }
  async skipFundAccount() { this.setState({view: 'DeployerOrAttacher'}); }
  selectAttacher() { this.setState({view: 'Wrapper', ContentView: Attacher}); }
  selectDeployer() { this.setState({view: 'Wrapper', ContentView: Deployer}); }
  render() { return renderView(this, AppViews); }
}

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); }
}

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
    this.deadline = {ETH: 10, ALGO: 100, CFX: 1000}[reach.connector]; // UInt
    backend.Alice(ctc, this);
    const ctcInfoStr = JSON.stringify(await ctc.getInfo(), null, 2);
    this.setState({view: 'WaitingForAttacher', ctcInfoStr});
  }
  render() { return renderView(this, DeployerViews); }
}
class Attacher extends Player {
  constructor(props) {
    super(props);
    this.state = {view: 'Attach'};
  }
  attach(ctcInfoStr) {
    const ctc = this.props.acc.attach(backend, JSON.parse(ctcInfoStr));
    this.setState({view: 'Attaching'});
    backend.Bob(ctc, this);
  }
  async acceptWager(wagerAtomic) { // Fun([UInt], Null)
    const wager = reach.formatCurrency(wagerAtomic, 4);
    return await new Promise(resolveAcceptedP => {
      this.setState({view: 'AcceptTerms', wager, resolveAcceptedP});
    });
  }
  termsAccepted() {
    this.state.resolveAcceptedP();
    this.setState({view: 'WaitingForTurn'});
  }
  render() { return renderView(this, AttacherViews); }
}

renderDOM(&lt;App />);

我们总共写了约 100 行的 Reach 代码,并且有两个不同的前端,分别是命令行版本和网页版本的,他们各自也都大约只有 100 行。

在这背后,Reach 生成了几百行的 Solidity 代码(可以查看文件:rps-8-interact/build/index.main.sol),近 2000 行的 TEAL 代码(可以查看文件:rps-8-interact/build/index.main.appApproval.teal),以及超过 1000 行的 JavaScript 代码(可以查看文件:rps-8-interact/build/index.main.mjs)。如果我们不是使用 Reach,那么我们就必须自己编写这几千行代码,并确保它们在每次改动时彼此同步、相容,那将是一个很复杂的任务!

现在我们已经从头到尾的带着你写出一个完整的 Reach 应用程序了,该是你开发自己的应用程序的时候啦!

无论如何,我们非常希望你能加入我们的 Discord 社区。一旦你加入 Discord 社区,发信息@team, I just completed the tutorial! 我们就会赋予你tutorial veteran的角色,这样你就可以帮助其他人学习本教程!

感谢您和我们一起度过学习这个教程的美好时光!

文挡内还有很多值得阅读的部份, 比如: 工作坊内有更多的应用案例,并且不断持续更新。 指南部份有关于各种不同主题与概念的详细讨论。 参考资料中则可以找到全部语法与概念仔细的说明。

我们也会持续与大家分享,也欢迎各位读者加入微信群与更多开发者一起讨论、学习。 有想要了解的主题也欢迎留言点播!!!

欢迎关注Reach微信公众号 并在公众号目录 -> 加入群聊 选择加入官方开发者微信群与官方Discord群 与更多Reach开发者一起学习交流!

[Reach教程翻译] 2.10 石头剪刀布的总结!插图

本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

  • 发表于 2021-12-10 20:40
  • 阅读 ( 130 )
  • 学分 ( 3 )
  • 分类:其他
  • 专栏:Reach开发资料

评论