在之前,CKB上的资产协议的判定仅依赖交易内省,如对于NFT协议,输入无对应NFT,输出有对应NFT,即为mint;输入和输出都有对应NFT,则为transfer;输入有输出无,则为burn,对于FT协议,也可以采用类似的内省逻辑进行判定。
在 Cobuild 出现后,资产协议将可以使用 Action 在 Witness 中显式表达交易意图,如以下是 Spore 使用的几种Action。
table MintSpore {
spore_id: Byte32,
to: Address,
data_hash: Byte32,
}
table TransferSpore {
spore_id: Byte32,
from: Address,
to: Address,
}
table BurnSpore {
spore_id: Byte32,
from: Address,
}
使用这种格式表达用户意图,使得应用方SDK与钱包方更好地组合,钱包也不用额外逐一对接所有的应用,这是在组合能力上的提升。
但我接下来将论述,如果资产(NFT/FT)协议本身强制具有这些Action约束,在涉及到协议间组合和套娃时,将使得资产之为资产的一些其他属性难以表达。
为了节省篇幅,关于资产的质押,借贷的种种例子就先省略,只讨论“交易”这一项资产最核心的属性。
订单簿DEX
首先考虑,订单簿式交易,即A挂单,B吃单,在拥有OTX以后,订单簿式交易将变得非常简单,相比使用 sighash_single|anyone_canpay 形式的BTC挂单要强大得多,其模式如下所示:
OTX-Based
Order otx:
input:
Asset X, lock-A
output:
Asset Y, lock-A
Onchain tx:
input:
Asset X, lock-A
Asset Y, lock-B
output:
Asset Y, lock-A
Asset X, lock-B
但很不幸,由于Transfer Action的强制存在,订单簿Dex将无法实施,所以我们也无法使用 OTX 为 SPORE 构建一个订单簿市场,如果 UDT 也强制加上 Transfer Action,我们也无法为UDT构建基于OTX的订单簿市场。
为什么无法实施?因为Transfer语义中要求了明确的资产接收者,但是一个挂单的用户无法知道最终是谁来完成这个订单,导致基于OTX的订单簿DEX将变得困难。
table TransferSpore {
spore_id: Byte32,
from: Address,
to: Address, // 未知
}
table Transferudt {
udt_id: Byte32,
amount: Uint128,
from: Address,
to: Address, // 未知
}
为了解决这个问题,一个订单可以使用两笔链上交易来表达:首先 A 将资产转移给一个托管协议,在成交时,B构建一个Transfer Action,将资产从托管协议转移给B,同时构建另一个Transfer Action,将A需要的资产从B转移给A,相比 OTX-Based Orderbook Dex,不仅流程更繁琐,同时需要两笔交易才能完成成交,最快可能也得一分钟才能成交。而就连BTC,对于挂单式交易也只需要一笔交易。
流程如下:
no-OTX-Based
Onchain TX1:
input:
Asset X, lock-A
output:
Asset X, Custody
witness:
Action: Transfer Asset X from lock-A to Custody
Onchain TX2:
input:
Asset X, Custody
Asset Y, lock-B
output:
Asset Y, lock-A
Asset X, lock-B
witness:
Action: Transfer Asset X from custody to lock-B
Action: Transfer Asset Y from lock-B to lock-A
AMM DEX
如果说,由于强制的Action,订单簿 Dex 只是变得更复杂,延迟更高,体验更差,那么AMM Dex将干脆变得不再可行。
基于OTX订单簿 DEX是可以清晰知道转账的发起者,数额,但不知道转账的收款方,从而不可行。而AMM Dex,是可以知道转账的发起者,收款方,但不知道具体的数额,从而不可行。
因为AMM最终能转出多少X币换得多少Y币,在用户下单的那一刻是不清楚的,而只有在排序后才能获知,但此时定序器无法再去联系每一个用户,要求他们填充好每一笔swap对应的transfer action,然后才能拼接整个交易上链。
那么NFT和FT在作为交易市场的其他组件时,情况又如何呢?
我们知道,Uniswap v2形式的AMM使用FT作为LP Token,Unswap v3使用NFT作为 LP Token(比如说使用spore,可以用cluster_id表示协议,在content里存储一些数据信息),而NFT本身也可以用来表达各种票据和债券。
当这些Token都是由Defi协议负责mint和burn的时候,用户针对资产的Action为:AddLiquidity和RemoveLiquidity,里面可能有一些协议特有的参数,而原有的强制mint和burn也会对Defi协议的组合产生干扰。
所以,我们可以得出结论,付诸于资产协议上的强加的 Cobuild Action 将严重影响资产交易协议的开发与组合。而如果资产协议本身的附加属性,影响了其本身的交易的难易,我觉得这里是需要权衡的。
Why
在ERC20里,无论怎么组合,代币协议抛 transfer 事件并不会影响协议间组合性,为什么在 Cobuild 上就会出现问题呢?
我们首先分析ERC20的模式:
在 EVM 世界,资产本身是二等公民,而合约即不同的账户间的交互,都是协议外部的交互,即A协议调用B协议,给一些参数,B协议内部怎么处理的A协议不需要考虑。
在这种模式下,ERC20代币内部进行什么操作都不影响协议间接口的兼容性,只要它的最终结果符合ERC20的要求,但是有一个特例,那就是ETH,ETH在以太坊上是 first-class asset,在协议内部控制的资产,它是对接口本身有侵入性的,Uniswap v2和v3为了使得处理模式通用化,都会在外围合约先将 ETH 转化成 wETH,从而保持接口的统一。
通过 Cobuild,在CKB上一样可以设计出这样的模式:
Protocol X可以发起一个针对Protocol Y的Action,Protocol X只需要检查这个 Action 在Witness中存在,至于Protocol Y 如何执行检查,Protocol X可以不用考虑,与上面的交互模式看起来很类似。
但是这仅在不涉及资产的时候成立,由于 CKB 上的资产都是一类资产,每个针对资产的协议不得不在协议内部也处理资产的操作,而不能简单地通过一个Action发送给资产的托管方去处理。
那么,假设我们使用AMM提出一种协议托管的资产方案,所有的操作都通过Action发送给资产协议检查,上面的问题就可以避免了吗?同样不行,因为Cell模型还有另一个特性-链下确定性,即交易要上链时,其交易的各个部分都必须是确定的,那么即便设计出协议托管的资产方案,基于OTX的订单簿DEX和AMM DEX由于交易发起时的信息并未确定,而使得整个流程走不通。
既然这样,能不能把订单簿和AMM需要的Action内置在资产协议里呢?对于特定的资产应用,这样当然没问题,但是作为资产协议的设计者,它必定无法穷尽一个资产所具有的Action的可能性,因为针对资产的应用本身的可能性是无限的。
结论
本文以对于资产来说最核心的交易协议为例,指出附加于资产协议上的强制标签会影响针对资产的交易应用的开发与组合,并认为这是由Cell模型的资产作为一等公民,以及链下确定性等特性导致。除去资产的交易应用外,在设计资产套娃的质押协议,或者借贷协议,上面提出的问题一样存在。
如果认为资产就只有一些简单的模式,如发行方发行资产,用户使用钱包互转资产,那么使用Cobuild确实简单明了。但是这种想法对Defi乐高则并不那么友好,在上面我只论述了最简单的订单簿和AMM应用遇到的困难,如果设计更为复杂,乃至于多个子协议组合的Defi应用,想必会遇到更多问题。