[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(<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开发者一起学习交流!
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。
- 发表于 2021-12-10 20:40
- 阅读 ( 130 )
- 学分 ( 3 )
- 分类:其他
- 专栏:Reach开发资料
评论