Liquidable Dao Demo:示范开发一个全栈的 dApp,让锁在 Nervos Dao 的 CKB 用起来 !
NervosDao 是 Nervos 第一个 CKB 链上的 dApp,也是一款在可以让用户可以对抗 Nervos 二级补贴的通胀率的工具,然而,在 Nervos Dao 中的 CKB 必须要在存入 Nervos Dao 后的第 180 epoch ,才能取出进行使用,这也让在 NervosDao 中的 CKB 失去了流动性,因此之前也看到如 Nervos 的开发者 JJY 也提出了 dCKB 的概念,而这次开发者 Retric 的视频,也展示了名为 Liquidable Dao 的 dApp ,以及这样的逻辑该怎么撰写成合约,并以此作为示范告诉大家整体在 CKB 上开发的流程,和过程中可以使用的开发工具。
https://www.bilibili.com/video/BV1AK411w7CH/
如果 NervosDao 中的 CKB 可花用,那么会长什么样子 ?
- 如上是 Demo 的 Liquidable Dao dApp 的界面,从中我们可以看到,在这个示范的 dApp 当中,主要可以分成四个功能,除了我们熟知的 Deposit、Withdraw 外(NervosDao 的存入和取出功能),我们还可以看到有两个特殊的选项,那就是 Deposit With Special Lock 和 Transfer Dao Cell。简单的说,我们可以将 CKB 透过 「Deposit With Special Lock 」的功能,生成一个所有权能够转移的 Nervos Dao Cell ,并且透过 Transfer Dao Cell ,并且让我们的 NervosDao Cell 能在 180 epoch 期满以后,可以被其他拥有所有权的人开启使用。
- 如此一来,NervosDao 中的 CKB 就变成一个可继续流动的资产,可以去用于借贷、抵押、收益聚合器等各种 DeFi 相关的合约或者协议中,在未来依旧可以让新的 Nervos Dao Cell 的所有者(如接收方、清算方)可以在到期日后兑换出其中的 CKB 使用。如此一来,不但能够让锁仓的状态持续,也不会让 NervosDao 中 CKB 抵押者缺乏资产的流动性。
Liquidable Dao 原理和设计
接下来我们来探讨具体的设计。首先,如果我们要让 NervosDao 中的 CKbyte 具有流动性,那么就代表这个 NervosDao Cell 不但是可以交易的 ,同时在新的拥有者这边它能够顺利的去使用 NervosDao Cell ,在 180 个epoch 期满后,可以用之兑换存在 NervosDao 中的 Cell,并且一并将二级发行奖励中的 CKB 也一起拿走。
今天如果要让我们的 NervosDao 中的 CKB 可以让别人去领取、使用,那么在原先的 NervosDao cell 中势必要有些不同,但是要如何在保持 NervosDao 的特性下,还可以对所有权进行移转呢?
我们知道 NervosDao 的脚本是设置在 Cell 的 type script 中,因此我们确定了 type script 是不能修改的。这时候我们还有另个没有用上的,就是 lock script ,我们只要能够将系统默认的 secp256k1 ,变成一个特殊的 Special lock 即可。
从下图可以发现,当我们透过上述的「Deposit With Special Lock」这个合约将 CKB 存入 NervosDao 时,在 output 除了会产生 Nervos Dao Cell 之外,还会有另一个普通的 Cell 叫做 Puppet Cell ,他相当于是 NervosDao Cell 的傀儡,因此今天,我们只要能够让 NervosDao Cell 和 Puppet Cell 产生一个联系,那么就能够透过这个关系,让 NervosDao Cell 的所有权随着 Puppet Cell 而移转。
因此我们可以在此看到拥有 Special Lock 的 NervosDao Cell ,他的 Lock Script 中的 Code_Hash 已经 是
special_lock_code_hash
的解锁逻辑,并将代表所有权的 args ,传入 puppet cell 的 type script hash 作为一个独一无二的字符串,至于怎么产生一个独一无二的字符串呢?这时候 Nervos 中的 Type ID 就派上用场了,Type ID 能够保证某 Cell 之中的 type script hash 是独一无二的。因此这时候谁拥有了这个 Puppet Cell ,就等于有了这个 NervosDao Cell 的所有权,进而可以在时间到期后对 NervosDao 合约进行 CKB 的提取。Liquidable Dao 合约编写的流程和使用的工具
在这样的设计下,我们就能够确保 Nervos Dao Cell 的所有权会随着 Puppet Cell 的转移而成功移转,而 Puppet Cell 也可以成为一个基于 NervosDao的衍生资产,因为任何收到 Puppet Cell 的人会知道,其背后支撑的是正在 NervosDao 中存放的 CKB 。
整体而言,我们也可以看到整个 Liuidable Dao 的 合约的运作如下 ,主要的目的是要确认 Nervos Dao Cell 的 lock script 能不能被 Puppet Cell 的拥有者所开启:
- 把当前 Lock 的 args 都加载进来
- 把所有的 input cell 全部循环一遍,看看里面是不是存在一个 input cell ,他的 type script hash 跟这个 args 是相等的 ,因此我们会把 args 露出,去找有没有存在对应关系的input
- 如果没有找到满足的 input cell 或者找到的大于 1 个,那么返回错误,因为我们需要确定 Puppet Cell 和 NervosDao Cell 是一对一的绑定关系
- 如果只有一个 Cell 满足这个条件,则把这个 puppet cell 的 data 字段加载出来,作为目标用户的 public key hash
- 从这笔交易的签名中恢复出一个用来签名的公钥哈希,这样就有两把公钥
- 一是目标用户存在 puppet cell 里面的 data 字段的公钥
- 这笔交易签名用的公钥
- 检查两把公钥是否相等,相等的话证明签名的和 cell 的拥有者是同一人,是的话返回正确。
在返回正确以后,我们就确认了这个 NervosDao Cell 中的 CKB 可以被持有 Puppet Hash 的所有者使用。
dApp 开发时可以使用的基础工具 — Capsule & Lumos
最后,在 Demo 中,我们也可以看到许多 Nervos 上既有的基础设施,可以在合约编写中拿来利用,方便各种开发工作能够顺利进行。
在合约编写部分,像是 Capsule 可以做为链上合约编写的开发工具,测试和部署等工作也可以直接在Capsule 中完成 ,节省了许多的工作量,好让开发者可以专注在自己的业务逻辑的编写和设计。在这个过程比较有趣的地方是在恢复公钥的部分,还会涉及到一个「动态加载(Dynamic Loading)」的技术,把签名所需要用到的算法加载进来。
有了链上合约之后,我们还会需要一个前端界面,同时会有 Server 端和区块链进行交互,例如往链上提数据,以及和前端的网页进行交互,如下图我们可以看到整体开发的架构。在前端我们可以看到各种语言或者开发框架在 Nervos 上开发都没有问题,在 Server 端一样任何语言都可以使用,但是推荐可以使用 Node.js,因为 Server 端和链进行交互时会有很多重复性的任务,这时候 Nervos 提供的工具Lumos 可以帮助开发者减轻许多的开发量,例如在 NervosDao 中的各种动作,如 Withdraw/Deposit 都是被 Lumos 封装,可以直接被使用。
如果你还想要了解更多,以下的链接都可以进行参考:
- demo video 链接:https://www.bilibili.com/video/BV1AK411w7CH/
- Liquidable Dao dApp repo https://github.com/RetricSu/liquidable-dao-dapp
- Liquidable Dao 合约 repo:https://github.com/RetricSu/liquidable-nervos-dao-contract
- dApp server 开发工具 Lumos :https://github.com/nervosnetwork/lumos
- dApp 合约编写开发工具 Capsule: https://github.com/nervosnetwork/capsule
- Type ID :https://xuejie.space/2020_02_03_introduction_to_ckb_script_programming_type_id/
- Dynamic Load :https://docs.nervos.org/docs/labs/capsule-dynamic-loading-tutorial
最后谢谢 Retric 录制的视频让我对于 Lock script 的妙用以及 Nervos dApp 开发有更深的了解,并且谢谢他对本文的审阅以及在我写作过程中的解惑。
如果对本文如果有任何的建议,欢迎不吝给予指教