在本教程中,我们替石头剪刀布游戏加上了网络代币的转移~
[Reach教程翻译] Reach是安全简单的Dapp开发语言 让用户可以像开发传统App一样开发DApp 目前使用Reach开发的智能合约可以部署在以太坊、Conflux、Algorand Reach官网 Reach官方文挡
玩“石头剪刀布”很有趣,但是投入你的小金库会更有意思。我们修改一下代码,让 Alice 可以跟 Bob 打赌下注,最终猜拳的赢家赢得奖金。 这一次,我们从 JavaScript 前端开始,然后再回到 Reach 的代码,加入新的方法。
既然要转移资金,我们就要在游戏开始之前记录每个参与者的余额,以便我们更清楚地显示他们最终赢了多少钱。我们将在帐户创建与合约部署之间添加代码。
.. // ...
6 const startingBalance = stdlib.parseCurrency(100);
7 const accAlice = await stdlib.newTestAccount(startingBalance);
8 const accBob = await stdlib.newTestAccount(startingBalance);
9
10 const fmt = (x) => stdlib.formatCurrency(x, 4);
11 const getBalance = async (who) => fmt(await stdlib.balanceOf(who));
12 const beforeAlice = await getBalance(accAlice);
13 const beforeBob = await getBalance(accBob);
.. // ...
接下来我们更新 Alice 的接口来让她下注。
.. // ...
32 backend.Alice(ctcAlice, {
33 ...Player('Alice'),
34 wager: stdlib.parseCurrency(5),
35 }),
.. // ...
至于 Bob ,我们修改他的接口以显示赌注,并通过返回值立即接受赌注。
.. // ...
36 backend.Bob(ctcBob, {
37 ...Player('Bob'),
38 acceptWager: (amt) => {
39 console.log(`Bob accepts the wager of ${fmt(amt)}.`);
40 },
41 }),
.. // ...
acceptWager
。最后,在计算完了之后,我们将会再次获取余额并展示一条总结信息。
.. // ...
44 const afterAlice = await getBalance(accAlice);
45 const afterBob = await getBalance(accBob);
46
47 console.log(`Alice went from ${beforeAlice} to ${afterAlice}.`);
48 console.log(`Bob went from ${beforeBob} to ${afterBob}.`);
.. // ...
对前端的这些更改只处理输出和接口的问题,下注和转移代币的实际操作会在 Reach 的代码中实现。
让我们接着看看后端代码。
首先,我们需要更新参与者交互接口。
1 'reach 0.1';
2
3 const Player = {
4 getHand: Fun([], UInt),
5 seeOutcome: Fun([UInt], Null),
6 };
7
8 export const main = Reach.App(() => {
9 const Alice = Participant('Alice', {
10 ...Player,
11 wager: UInt,
12 });
13 const Bob = Participant('Bob', {
14 ...Player,
15 acceptWager: Fun([UInt], Null),
16 });
17 deploy();
18
19 Alice.only(() => {
.. // ...
Player
接口,加上一个名为 wager
(赌注)的整数值。acceptWager
(接受赌注)的方法,用来查看 wager 的值。为了实现赌注的功能,这个应用程序的三个部分中的每一个都必须稍微修改一下。让我们先看看 Alice 的第一步。
.. // ...
19 Alice.only(() => {
20 const wager = declassify(interact.wager);
21 const handAlice = declassify(interact.getHand());
22 });
23 Alice.publish(wager, handAlice)
24 .pay(wager);
25 commit();
.. // ...
您可以试试看,如果 wager
只出现在第 24 行而没有被写在 23 行,则会引发 Reach 编译器异常。这是因为共识网络必须能够验证 Alice 的发布中包含的网络代币的数量是否与共识网络可获取的计算相匹配。
接下来,需要让 Bob 知道赌注并给他接受赌注的机会、与转移代币的时机。
.. // ...
27 Bob.only(() => {
28 interact.acceptWager(wager);
29 const handBob = declassify(interact.getHand());
30 });
31 Bob.publish(handBob)
32 .pay(wager);
.. // ...
此时 DApp 正在共识步骤中运行,而现在合约内拥有下注金额两倍的网络代币。在之前的例子里,它只要计算结果后提交状态。但是现在,它还需要根据结果并处理赌注资金。
.. // ...
34 const outcome = (handAlice + (4 - handBob)) % 3;
35 const [forAlice, forBob] =
36 outcome == 2 ? [ 2, 0] :
37 outcome == 0 ? [ 0, 2] :
38 /* tie */ [ 1, 1];
39 transfer(forAlice * wager).to(Alice);
40 transfer(forBob * wager).to(Bob);
41 commit();
.. // ...
现在,我们可以运行这个程序,并查看它的输出结果。
$ ./reach run
因为两个玩家的出拳是随机的,所以你每次运行时看到的结果可能会不一样,以下是我们运行了三次的结果
Alice played Paper
Bob accepts the wager of 5.
Bob played Rock
Alice saw outcome Alice wins
Bob saw outcome Alice wins
Alice went from 100 to 104.9999.
Bob went from 100 to 94.9999.
$ ./reach run
Alice played Paper
Bob accepts the wager of 5.
Bob played Scissors
Alice saw outcome Bob wins
Bob saw outcome Bob wins
Alice went from 100 to 94.9999.
Bob went from 100 to 104.9999.
$ ./reach run
Alice played Rock
Bob accepts the wager of 5.
Bob played Scissors
Alice saw outcome Alice wins
Bob saw outcome Alice wins
Alice went from 100 to 104.9999.
Bob went from 100 to 94.9999.
Alice和Bob的余额为什么每次都变回 100 呢?这是因为每次我们运行./reach run
时,Reach会启动一个测试网络的全新实例并为每个玩家注册账号。
余额为什么不是 100,105 和 95 呢?这是因为在共识网络交易会消耗燃气费。
如果显示所有的小数点,看起来就会像这样:
Alice went from 100 to 104.999999999999687163.
Bob went from 100 to 94.999999999999978229.
Alice went from 10 to 4.999999999999687163.
Bob went from 10 to 14.999999999999978246.
为什么当 Alice 获胜时,赢得的钱比 Bob 获胜时少呢?因为她得付钱部署这份合约,因为她在前端发布了第一个信息 。部署的指南部分讨论了如何避免这种差值。
Alice的前景一片光明,如果她一直赌下去,她似乎能在“石头剪刀布”中赚上一笔!
如果你的版本不能正确运行,请查看完整版本的tut-4/index.rsh和tut-4/index.mjs,并确保你正确地复制了所有内容
然而这个应用程序还存在一个重大的安全隐患。我们将会在下一步修复它;千万别用这个版本部署,不然 Alice 就要破产了!
您知道了吗:
Reach 程序如何管理代币?
1.他们不管理,你需要在 Reach 程序显式地管理代币;
2.pay的原始语句可以被添加到 publish原始语句中来发送资金给 Reach 程序,这样到时候就可以用 transfer 原始语句来把资金发送回参与者和其他地址。
答案:
2; pay和 transfer 原始语句会帮你搞定这一切。
欢迎关注Reach微信公众号 并在公众号目录 -> 加入群聊 选择加入官方开发者微信群与官方Discord群 与更多Reach开发者一起学习交流!
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!