MTC 挖矿奖励
MTC(Mining Token Credit)是 Rocky 在 Canton 链上发行的交易挖矿奖励代币。每完成一笔 perp 交易,Rocky 平台铸造一份 MiningReward Daml 合约;用户可以随时主动 claim,把奖励变成可转移的 TokenHolding。本文介绍整套合约设计、生命周期、以及为什么采用"先铸后领"的两段式。
涉及概念:Canton Party、Daml signatory / controller / choice。如果不熟悉,请先阅读 Canton 网络介绍。
一、核心概念
1.1 为什么需要"挖矿奖励"
Rocky 的撮合 + 资金账户都在链下数据库 (ledger.accounts),速度快但不可被第三方审计。为了让交易激励有不可抵赖的链上证据,Rocky 把"每笔交易都触发一份链上奖励合约"作为机制设计:
- 链下:高频订单簿、撮合、margin
- 链上:每笔成交后的奖励铸造(低频、可审计)
这也是 Rocky 与 Canton 网络结合的核心价值演示。
1.2 MTC 和 CC 的区别
| 维度 | CC | MTC |
|---|---|---|
| 来源 | 用户充值(DevNet 模拟 / MainNet 钱包转账,见 CC 充值流程) | 平台铸造,每笔交易自动触发 |
| 持有方式 | ledger.accounts.asset = 'CC'(链下记账) | Canton 上的 TokenHolding Daml 合约 |
| 转账 | 用户充进 / 平台代管 | (未来)用户之间 P2P,Daml 合约级转账 |
| 余额查询 | 单一 SQL row | 遍历 ledger updates 聚合活跃合约 |
CC 是"可花的余额",MTC 是"激励代币"。两者并行存在,互不影响。
二、Daml 合约设计
2.1 两个核心模板
文件:exchange-app/daml/ExchangeApp.daml
template MiningReward
with
exchange : Party -- signatory(Rocky's app-provider)
miner : Party -- observer(用户的 Canton party)
rewardAmount : Decimal -- 本笔奖励的 MTC 数量
orderHash : Text -- 来源 trade UUID,幂等键
tradeValueUsdt : Decimal -- 触发本次奖励的成交名义价值
rewardTokenSymbol : Text -- 固定为 "MTC"
where
signatory exchange
observer miner
ensure rewardAmount > 0.0
-- 用户主动领取 -> 生成可转移的 TokenHolding
choice ClaimReward : ContractId TokenHolding
controller miner
do
create TokenHolding with
owner = miner
issuer = exchange
amount = rewardAmount
tokenName = "MyTestCoin"
tokenSymbol = rewardTokenSymbol
sourceOrderHash = Some orderHash
template TokenHolding
with
owner : Party -- 持有人
issuer : Party -- 发行方(Rocky)
amount : Decimal
tokenName : Text -- 当前为 "MyTestCoin"(历史名,对应 Rocky 时代的 MTC 简称)
tokenSymbol : Text -- "MTC"
sourceOrderHash : Optional Text
where
signatory issuer
observer owner
-- 未来扩展:transfer / split / merge choices
2.2 为什么是两段式(Mint + Claim)
可能有人会问:为什么不让 Rocky 直接创建 TokenHolding,跳过 MiningReward 中间态?
| 单段式(直接 mint TokenHolding) | 两段式(MiningReward → ClaimReward → TokenHolding) |
|---|---|
| ❌ 用户无须签名就被强塞代币(违反 stakeholder consent) | ✅ 用户主动 exercise choice,等于明确接受 |
| ❌ 用户钱包/UI 必须实时跟踪所有合约 | ✅ "待领取奖励"在 UI 中是独立状态,容易展示 |
| ❌ 没有显式的"trade → reward"溯源 | ✅ MiningReward 自带 orderHash + tradeValueUsdt,链上即可审计 |
| ❌ 不易批量退还/撤销 | ✅ 若发现作弊交易,可在 Claim 前 archive MiningReward |
代价是用户多了一步"Claim"操作,但 UI 可以做成"一键全部领取",UX 损失可控。
2.3 关键签名/控制者设计
MiningReward.signatory = exchange→ Rocky 单方铸造,用户无须签名MiningReward.observer = miner→ 用户可在自己 ledger 视图中看到这份合约(隐私模型保证只有 exchange + miner 可见)ClaimReward.controller = miner→ 只有用户本人可以触发 claimTokenHolding.signatory = issuer (= exchange)→ 由 Rocky 维持发行人责任;后续转账 choice 会需要 owner 共同签名
三、生命周期 (Lifecycle)
1. 用户在 perp 页下单 → 撮合成交
│
▼
2. Frontend 拿到 fill 事件后调用:
POST /api/perp/rewards/mint
body: { traderParty, tradeId, tradeValueUsdt }
│
▼
3. Backend 用 app-provider party 签名创建 MiningReward 合约
rewardAmount = tradeValueUsdt × REWARD_RATE
orderHash = tradeId
│
▼
4. 合约写入 Canton Synchronizer(只对 exchange + miner 可见)
│
▼
5. Dashboard 显示"待领取奖励 X MTC"
(/api/history 聚合所有未 archive 的 MiningReward)
│
▼ 用户点击 Claim
6. Frontend → POST /api/claim
body: { token, party, rewardContractId }
│
▼
7. Backend 用用户 token + party 触发:
ExerciseCommand(MiningReward, ClaimReward, {})
│
▼
8. Canton 同时 archive 旧 MiningReward + create 新 TokenHolding
│
▼
9. Dashboard 显示"已持有 X MTC"
(TokenHolding owner = party)
四、代码导航
| 文件 | 作用 |
|---|---|
daml-contracts/exchange-app/daml/ExchangeApp.daml | MiningReward + TokenHolding 模板定义 |
mtc-exchange/src/lib/canton.ts | EXCHANGE_PKG_ID、REWARD_RATE = 1.0、submitCommand 辅助 |
mtc-exchange/src/app/api/perp/rewards/mint/route.ts | Mint:每笔交易后调用,CreateCommand(MiningReward) |
mtc-exchange/src/app/api/claim/route.ts | Claim:用户主动领取,ExerciseCommand(ClaimReward) |
mtc-exchange/src/app/api/history/route.ts | 聚合:遍历 app-provider party 视角的 ledger updates,统计 pending / claimed / holdings |
mtc-exchange/src/components/TopNav.tsx | TopNav modal 中显示 MTC 余额(来自 /api/history 的 totals.mtcBalance) |
五、配置项
5.1 REWARD_RATE
mtc-exchange/src/lib/canton.ts:
export const REWARD_RATE = 1.0;
含义:每 $1 名义成交价值,铸造 1 MTC。
当前为固定值;未来计划改为分层(VIP / 交易量阶梯)或随时间衰减("halving")。修改要点:
- 改变这个常量后,所有新交易立即按新费率铸造,已经创建但未 claim 的 MiningReward 不受影响
- 若需要"按时间段切换费率",应在后端而非前端配置,并将费率写入 MiningReward 合约本身(增加
rewardRate字段)以便链上审计
5.2 EXCHANGE_PKG_ID
Daml package id,定位 ExchangeApp 模块。每次 Daml 模板修改后重新部署都会变。当前从环境变量读取:
export const EXCHANGE_PKG_ID =
process.env.EXCHANGE_PKG_ID ?? "<fallback>";
部署时通过 .env.local 设置。
5.3 reward token symbol
写死在 mint 路由调用里:rewardTokenSymbol: "MTC"。未来若 Rocky 发行多种奖励代币(如 "RKY"),可以让前端按用户/活动传入。
六、幂等性
orderHash = String(tradeId)(trade UUIDv7)。
- 链上:MiningReward 模板本身没有 unique key on orderHash。理论上 Rocky 后端如果对同一 tradeId 调用两次
/api/perp/rewards/mint,会创建两份合约。 - 链下:Dashboard 的
/api/history在聚合时按orderHash去重显示(取最早一份),实际 claim 时只会 claim 用户选中的那份合约 id。 - 建议:后端在 mint 前先查
MiningReward是否存在 same orderHash 的活跃合约。或在 commandId 里加 trade UUID 作为 idempotency token(perp-reward-${orderHash.slice(0,12)}-${Date.now()}— 当前实现已部分采用,但 Date.now() 让 commandId 不严格幂等)。
正式上线前需要修正这一处。
七、隐私 (Privacy)
回顾 Canton 隐私模型:
| 合约 | Signatory | Observer | 谁能看见 |
|---|---|---|---|
| MiningReward | exchange | miner | Rocky + 用户本人 |
| TokenHolding | exchange (issuer) | miner (owner) | Rocky + 用户本人 |
其他用户、其他 validator、Synchronizer 节点都看不到具体奖励数额或 orderHash。Rocky 内部因为 signatory 是 exchange,能聚合 dashboard 的全平台统计。
八、未来扩展 (Roadmap)
8.1 一键全部领取
当前 UI 是逐份 claim。批量 claim choice:
template MiningRewardBatch
with
miner : Party
rewards : [ContractId MiningReward]
...
或在 frontend 做"批量 ExerciseCommand"。
8.2 自动领取
后端定时扫描 unarchived MiningReward 并代用户 claim?目前不行,因为 ClaimReward.controller = miner,只有 miner 能签名。如果后端要代领,需要新增 AutoClaimToken 合约由用户预先签名授权。
8.3 转账 / 拆分
给 TokenHolding 加 Transfer / Split / Merge choices,让 MTC 真正成为可流通代币。需要解决:
- 同 issuer 的 holding 是否可以合并?
- 不同 owner 之间转账时新的合约 signatory 是谁?
- 与 Splice 标准 token interface 的兼容?
8.4 取消错误奖励
发现交易作弊 / 撤销时,需要在 user claim 之前 archive MiningReward:
choice CancelReward : ()
controller exchange
do return ()
仅 controller=exchange 即可单方撤销,但前提是合约还没被 claim。
8.5 升级到 Splice 标准 token interface
TokenHolding 目前是 Rocky 私有模板。Splice 项目提供了标准化的 token interface(类似以太坊 ERC-20),让 Rocky MTC 能与 Canton 生态其他 wallet 兼容。是 MainNet 上线的硬要求。
九、相关文档
- Canton 网络介绍
- CC 充值流程
- 仓库技术决策:
daml-contracts/exchange-app/daml/ExchangeApp.daml— Daml 源码Canton_Custody_Wallet_Architecture.md— 托管模型(影响 TokenHolding transfer 设计)