跳到主要内容

CC 充值流程

CC 是 Rocky 的原生代币,作为 Daml 合约在 Canton 网络上发行。本文介绍用户如何把 CC 充值到 Rocky 交易账户,分为 DevNet(当前实现)与 MainNet(规划)两套流程。

涉及概念:Canton Party、Daml Holding 合约、Canton Bridge。如果不熟悉,请先阅读 Canton 网络介绍


一、共同的核心:DepositIntent 合约

无论 DevNet 还是 MainNet,最终的"上链 → 入账"环节都走同一条管道:

DepositIntent (Daml 合约)

▼ canton-bridge consumer 监听到 Created event
NATS ledger.events.deposit_intent

▼ internal-ledger 消费
UPDATE ledger.accounts SET available = available + amount
WHERE user_id = $1 AND asset = 'CC'

DepositIntent 是 Rocky 自定义的 Daml 模板,由用户(或其代理)作为 signatory 创建:

template DepositIntent
with
user_party : Party -- signatory
exchange : Party -- observer (Rocky's app-provider)
asset : Text -- "CC", "USDC", ...
amount : Decimal
intent_id : Text -- UUIDv7 for idempotency
where
signatory user_party
observer exchange
...

两种环境的差异**只在"DepositIntent 怎么被创建"**这一步:

环节DevNetMainNet(规划)
触发方前端 TopNav 一键充值按钮用户钱包转账操作
实际转账无(dev tool 模拟)真实的 Splice Holding 转账
Signatory 权限shadow-user-party-id(共享)用户自己的 Party
余额校验不校验必须有足够的 CC Holding

二、DevNet 流程(当前实现)

2.1 用户视角

  1. 登录 Rocky → 点击右上角用户头像打开账户 Modal
  2. "CC 充值"区域输入金额(如 1000),点击"充值 CC"
  3. ~1-3 秒后 modal 中"CC 余额"自动更新

2.2 API 调用链

Browser
POST /api/perp/deposits/cc {user_id, amount}


mtc-exchange (Next.js BFF)
/api/perp/deposits/cc/route.ts
└─ 注入 asset: "CC",转发:
POST http://api-gateway/v1/deposits/seed {user_id, asset: "CC", amount}


api-gateway (Rust, port 8080)
dev_tools::seed_deposit
└─ 调用 gRPC:
canton_bridge.SubmitDepositIntent({user_id_hex, asset, amount})


canton-bridge (Rust, port 50071)
service::submit_deposit_intent
└─ 用 shadow_user_party_id 作为 signatory 创建 DepositIntent 合约:
Ledger API: CreateCommand(DepositIntent, {user_party, exchange, asset, amount})


Canton Participant (DevNet)
把合约写入 Synchronizer,分发给 observer (Rocky app-provider)


canton-bridge consumer (订阅 active contracts stream)
发现 DepositIntent Created event
└─ 发布 NATS: ledger.events.deposit_intent {user_id, asset, amount, intent_id}


internal-ledger (Rust, port 50051)
chain_events::handle_deposit_intent
└─ INSERT idempotent + UPDATE ledger.accounts
SET available = available + amount WHERE user_id = ? AND asset = ?

2.3 关键代码位置

文件作用
mtc-exchange/src/app/api/perp/deposits/cc/route.ts前端 BFF,注入 asset: "CC"
rocky-backend/services/api-gateway/src/routes/dev_tools.rsseed_deposit handler(devToolsOnly 在 BFF 侧)
rocky-backend/services/canton-bridge/src/service.rssubmit_deposit_intent gRPC handler
rocky-backend/services/canton-bridge/src/ledger/marshal.rsbuild_deposit_intent_create Daml 命令构造
rocky-backend/services/canton-bridge/src/consumer.rsactive contracts 流消费 + NATS 发布
rocky-backend/services/internal-ledger/src/chain_events.rsNATS 消费 + 更新 ledger.accounts

2.4 DevNet 的简化点 / 不真实之处

  • ⚠️ 不校验余额:用户可以无限"充值"。因为 shadow-user-party-id 没有任何 CC Holding 上限。
  • ⚠️ 不真实转账:DepositIntent 直接由 backend 代用户创建,用户本身不持有任何 CC token。
  • ⚠️ 不需要钱包:用户没有自己的 Splice wallet 实例。
  • 路径真实:从 DepositIntent 创建到 ledger.accounts 入账这段管道与 MainNet 流程一致。

2.5 验证 DevNet 充值是否成功

# 直接看 ledger.accounts 的 CC 行
docker exec rocky-backend-stack-postgres-1 psql -U rocky -d rocky -c \
"SELECT user_id, available FROM ledger.accounts WHERE asset = 'CC' ORDER BY available DESC"

或者前端 TopNav modal 实时显示。


三、MainNet 流程(规划,未实现)

3.1 用户视角

  1. 用户已通过 Splice / Canton wallet 持有 CC tokens(来自空投、OTC、二级市场购买)
  2. 在 Rocky 网页点击"充值 CC"
  3. Rocky 弹出指引:"请把 X CC 从你的钱包转入以下托管地址 / Party"
  4. 用户在自己的 Splice wallet 里执行转账(签名)
  5. canton-bridge 监听到转账事件 → 1-2 个区块确认后入账
  6. Rocky modal 显示"已入账 X CC"

3.2 关键差异

项目DevNetMainNet
真实持有方用户自己的 Party
触发方式后端代用户创建 DepositIntent用户钱包发起转账
资产来源凭空(dev tool)用户自己持有的 CC Holding 合约
余额校验Daml 合约确保 from.amount >= transfer.amount
入账延迟~1-3s取决于 Canton Synchronizer 出块(通常 2-5s)
失败回滚不存在失败用户钱包不足 / 签名失败时 transfer 不上链

3.3 MainNet 数据流(规划)

用户的 Splice Wallet
├─ 持有合约 1: Holding(party=alice, asset="CC", amount=1000)

▼ alice 在 wallet 里选择 "Transfer to Rocky"
Transfer Choice
├─ 输入金额 + Rocky 平台 party id(known constant)
├─ alice 用自己的 key 签名

Canton Synchronizer
├─ 把 Holding 合约 archive
├─ 创建新 Holding(party=rocky_custody, asset="CC", amount=1000)
├─ 创建 DepositIntent(user_party=alice, exchange=rocky, asset="CC", amount=1000)

canton-bridge consumer
└─ 同 DevNet:发布 NATS → internal-ledger 入账

⚠️ MainNet 流程涉及 Rocky 平台的 custody model(资金托管模型),还在设计中。详细参考仓库根目录的 Canton_Custody_Wallet_Architecture.md

3.4 MainNet 必须解决的开放问题

  • 钱包指引 UX:用户怎么知道往哪个 party 转?是 deeplink 还是手动复制?
  • 金额精度:CC 的最小单位(Daml Decimal 默认 10 位精度),UI 上显示几位?
  • 手续费:链上 transfer 的 gas / synchronizer fee 谁出?平台 absorb 还是用户付?
  • 失败处理:转账上链了但 canton-bridge 故障没入账 → 重试 / 客服路径?
  • 限额 / KYC:单笔 / 单日上限?是否需要 KYC 通过?
  • 取消 / 退款:DepositIntent 创建后但还没入账时,能不能撤销?

这些问题会在迁移到 MainNet 前以单独 spec 解决。


四、与其它资产(USDC、MTC)的关系

Rocky 当前支持三种资产:

资产类型充值方式
CCRocky 原生代币本文档
USDCSplice 标准稳定币DevNet 用同一 seed 接口,MainNet 走 Splice 跨链桥
MTCMining Token Credit(挖矿奖励)不充值,只能通过交易触发 MiningReward.Claim 获得

三者的入账管道都走 ledger.accounts (user_id, asset),但发行机制不同:

  • CC: 平台铸造(MainNet 后由 Rocky app-provider party mint)
  • USDC: 跨链锚定
  • MTC: 链上奖励合约(参见 MiningReward 模板,详见后续 [挖矿奖励] 文档)

五、相关文档

  • Canton 网络介绍
  • 仓库根目录技术决策:
    • Canton_Custody_Wallet_Architecture.md — 托管钱包架构
    • party-model-decision.md — Party 模型选型(影响 MainNet shadow vs real party)
    • canton-privacy-arbitration-design.md — 隐私仲裁设计
  • TODO: USDC 充值文档(DevNet seed + MainNet 跨链桥)
  • TODO: MTC 挖矿奖励合约文档