合约:它如何运作,为什么这样运作
FAIRDRUM 的全部资金和全部规则都在 Solana 网络上的一个程序里——智能合约。网站只是向它发送命令并显示回应。下面介绍:合约保存什么,能理解哪些命令,结果如何由随机数得出,以及管理员能做什么、不能做什么。
什么是合约,它在哪里
智能合约是由 Solana 网络节点执行的程序,而不是运行在项目的服务器上。它的代码对所有人都一样,每个操作都是一笔公开交易。FAIRDRUM 合约用 Rust 和 Anchor 库编写;源代码公开。
网站接入合约后,程序地址会显示在这里。
一个重要的推论:即使 fairdrum.to 网站消失,玩家的资金仍留在合约中,任何能够发送带有你钱包签名的提现命令的程序都可以把它们提取出来。
合约保存什么
- 金库。一个以包装 SOL 计的账户,只有程序本身可以支配。所有资金都在其中:玩家余额、奖金池、头奖、储备金以及管理员尚未领取的份额。
- 游戏总账户。记录金库中的金额如何划分:奖金池、头奖、迷你头奖、超级头奖、种子储备金、管理员份额、头奖起始金额、各游戏的回合计数。
- 玩家账户。由 register 命令创建,网站会在首次充值时一并发送该命令;账户与钱包地址绑定:游戏余额、最后一次充值的时间、未完成的回合、头奖中尚未支付的部分。
- 会话账户(启用“免签名游戏”时):临时密钥、有效期和投注限额。
账目的设计保证所有余额与奖池之和等于金库中的金额。测试会在数万次随机操作的每一次之后检查这一等式。
合约能执行哪些命令
| 命令 | 谁可以调用 | 作用 |
|---|---|---|
register | 玩家 | 创建玩家账户。网站会在首次充值时一并发送。 |
deposit | 玩家 | 充值游戏余额。记录时间:提现前的 12 小时从这一刻起算。 |
withdraw | 玩家 | 提现到自己的钱包。距最后一次充值未满 12 小时会被拒绝。 |
spin_commit | 玩家或其会话密钥 | 投注:从余额中扣除回合金额,记录游戏和当时有效的概率,向预言机请求随机数。 |
spin_settle | 任何人 | 结算:读取预言机的数字,计算赔付并记入玩家余额。任何人都可以调用——结果不受此影响。 |
spin_expire | 任何人 | 关闭 20 分钟内未结算的回合。预言机未响应——投注全额退还给玩家。预言机已响应且回合已锁定——支付普通奖金,不进行头奖开奖。 |
spin_seal | 任何人 | 锁定回合:在投注所在区块的哈希仍可读取时(约 3.5 分钟)把它记录下来。只有预言机响应得更慢时才需要;通常由值守服务调用。未及时结算也未及时锁定的回合将无结果关闭。 |
skip_nonce | 玩家 | 当请求地址已被占用时,将下一次随机数请求的编号后移。 |
session_open / session_close | 玩家 | 允许临时密钥在有效期和限额内投注;该密钥既不能提现,也不能充值。 |
donate | 任何人 | 向头奖种子储备金注入 SOL。没有反向命令。 |
set_jackpot_starts | 管理员 | 设定头奖和超级头奖的新起始金额;24 小时后生效。 |
rescale_pools | 管理员 | 将部分种子储备金转入游戏奖金池。只能朝这个方向;不能转入头奖——头奖在被赢走后会自行从储备金中补充。 |
set_paused | 管理员 | 暂停或恢复接受新投注——用于预言机发生故障的情况。已经下的投注照常结算,派奖和提现照常进行。 |
set_guardian / guard_pause | 管理员 | 管理员指定守卫——这是值守服务的密钥,只能做一件事:在预言机停止响应时暂停接受新投注。只有管理员可以解除暂停。 |
propose_dev_authority / accept_dev_authority | 管理员 | 分两步把管理员权限移交给另一个密钥:当前密钥指定新密钥,新密钥自行确认。 |
roll_epoch / checkin | 任何人 | 服务性命令:更新每周统计和日期标记。不移动资金。 |
claim_dev | 管理员 | 领取管理员已累积的份额——仅此一项。 |
initialize / fund_pools | 管理员 | 一次性启动。initialize 创建各账户(由程序升级权限的持有人签名);fund_pools 一次性把注入的启动资金记入各奖池,超出的部分记入单独一行,管理员可以用 promo_pay 命令取走。 |
代码中不存在把奖金池、头奖或玩家余额转给管理员的命令。
回合步骤
- 投注。spin_commit 命令从你的余额中扣除回合金额,并在同一笔交易中向 ORAO 预言机创建随机数请求。请求地址由你的钱包和回合编号算出,因此无法调换成别人的请求。
- 数字。ORAO 的多个独立节点对随机数签名并将其写入网络。在写入之前没有人知道它:你不知道,项目不知道,任何单个节点也不知道。
- 结算。spin_settle 命令从预言机的记录中读取数字,确认这正是你这一回合的请求,然后计算赔付。网站会自行发送该命令;如果页面已关闭,则由项目的值守程序发送,或在你下次访问时由你发送。
- 展示。纸牌、地雷、火箭、宝石——都是展示已算出赔付的方式。游戏中的点击不会影响赔付。
为什么结果无法提前得知,也无法伪造。回合的最终数字,是预言机的数字与记录该投注的区块的哈希混合而成。投注时这个哈希还不存在,而预言机的数字之后才由第三方给出:没有人能提前算出结果——玩家不能,项目不能,预言机自己也不能。游戏、头奖起始金额和奖金上限在投注时就已记录,计算公式在公开的代码中。迟到的结算绝不会比按时结算更有利。要改变规则,只能替换程序代码,而这所有人都看得见(见关于管理员的章节)。
随机数如何变成结果
一个回合是 1 到 100 笔 0.01 SOL 的投注(转盘最多 1000 笔)。预言机的数字会展开为每笔投注各自独立的随机值,合约对每笔投注依次回答以下问题:
- 超级头奖、头奖、迷你头奖?每个奖池的中奖概率等于该笔投注注入它的金额除以该奖池的起始金额。只有当奖池中的金额不少于起始金额的 25% 时,才会开奖。
- 游戏奖金?每个游戏都有自己的中奖概率和一张带权重的六级奖金表。例如,转盘有 10% 的投注会中奖。所有游戏的表都显示在各自的页面上。
- “再来一次”?有 11.6% 的概率由奖金池出资免费重复这笔投注。
- 否则这笔投注没有奖金。它的分配方式与其他任何投注相同:奖金池、头奖和管理员份额。
各游戏的表不同,但按表计算的期望赔付相同(误差在百分之零点零几以内;前提是奖金池不少于 40 SOL,奖金不触及上限):约为投注的 77.5% 以普通奖金形式支付,加上“再来一次”的重复,约为已付费投注的 88%。这一相等关系由编译器检查:如果某个游戏的期望值高于转盘,或低出超过半个百分点,程序就无法编译。
合约把每笔投注放到哪里
| 去向 | 占已付费投注的比例 | 用途 |
|---|---|---|
| 奖金池 | ≈88% | 所有游戏的普通奖金 |
| 头奖、迷你头奖、超级头奖和种子资金 | 2% | 三个公共奖池 |
| 管理员 | 10% | 项目的收入;没有其他收入 |
代码中的比例是按每一笔已执行的投注(包括免费重复)来写的,所以那里写的是 8.84% 和 1.77%:折算到已付费投注,平均为 10% 和 2%。
限制规则及其用途
- 单笔奖金不超过奖金池的四分之一。合约不能承诺超出自己所有的金额,一次大额中奖也不会把奖金池掏空、让其他人无奖可拿。未付足的部分留在奖金池中。
- 最后一次充值 12 小时后才能提现。这条规则对所有人相同,并以常量写入:只有程序的新版本才能改变它。
- 头奖起始金额的更改延迟 24 小时生效。回合的概率在投注时锁定,因此无法“针对已下的投注”更改。
- 20 分钟结算时限。如果预言机没有响应,投注全额退还给玩家:数字出现之前没有人知道结果,因此无法用这种方式逃避输掉的投注。如果预言机已响应但结算迟了,普通奖金照常支付,但本回合不进行头奖开奖。金库保护。每次支付之前和每次结算之后,合约都会检查金库中的金额不少于它欠玩家和奖池的金额;否则操作会被拒绝。守卫。如果预言机停止响应,会自动暂停接受新投注;结算和提现继续正常运行。
- 头奖分期支付:赢家应得奖池的 95%,其中 20% 立即支付,之后每周 8%。未支付的部分记在赢家的账户上。
- 每位玩家只能有一个未完成的回合。回合结算之前,不接受新的投注。
管理员能做什么、不能做什么
能
- 领取自己的份额——已付费投注的 10%;
- 向储备金注入 SOL,并把它们从储备金转入奖金池;
- 取走启动时金库中超出启动资金的多余部分;
- 更改头奖的起始金额——有延迟,并且在规定的范围内;
- 在预言机没有响应时暂停接受新投注,以及把自己的权限移交给另一个密钥。
不能
- 提走奖金池、头奖、储备金或玩家余额(作为玩家,他按与所有人相同的条件赢奖);
- 更改奖金表、游戏的中奖概率和投注分配比例:这些都是代码中的常量;
- 在投注前得知回合的随机数,或选定它;
- 停止已下投注的结算或提现:暂停只涉及新投注。
坦率的说明。程序带有升级权限:在该权限被撤销之前,其持有人可以用新版本替换代码,届时上述一切针对的将是新代码。该权限的状态和每次升级都可在 Solana 区块链浏览器中看到。合约没有经过独立审计。
这些是如何验证的
- 合约测试:每个游戏的管理员份额均为 10%,奖金池为 50 SOL 时所有游戏的返还接近,四分之一奖金池规则和投注时的奖金上限,提现锁,延迟更改起始金额,迟到的结算不比按时结算更有利。
- 资金守恒检查:数万次随机操作,每次之后都把金库中的金额与余额和奖池之和核对。
- 长时间运行:每个游戏两百万笔投注——向玩家支付了多少,管理员得到多少。
- 本地 Solana 节点上的真实交易:充值、未到期时拒绝提现、向储备金注资、更改起始金额。
- 网站测试:网站用与合约相同的公式计算概率,并与合约记录的样本核对。
哪些没有验证、哪些没有做,都不加粉饰地列在“为什么选 FAIRDRUM”页面上。如何自己验证任何一个回合,见“验证回合”页面。
合约参数一览表
- 单注金额
- 0.01 SOL
- 回合
- 游戏中最多 100 笔投注,转盘最多 1,000 笔
- 游戏(模式)数量
- 16
- “再来一次”的概率
- 11.6%
- 单笔奖金上限
- 奖金池的 1/4
- 最低起始金额
- 头奖 0.2 SOL,超级头奖 2 SOL
- 迷你头奖
- 从头奖的 1/10 起步
- 头奖赢家所得
- 95% · 5% 进入超级头奖
- 超级头奖赢家所得
- 90%
- 提现锁
- 最后一次充值后 12 小时
- 回合结算期限
- 3,000 个网络时隙(slot),约 20 分钟
- 网络和预言机费用
- 每回合 0.005 SOL,不是项目收的